IO¶
Mathematicians dream in pure functions. Each of them only relies on its arguments and always produces the same result for the same input.
That’s not how a useful program works. We need to rely on the environment and we need to do side effects.
Furthermore, there are several types of IO in our programs:
Some
IOnever fails, like: getting current date and time, random number, or OS nameSome
IOmight fail, like: sending network requests, accessing filesystem, or database
There’s a solution.
IO container¶
Once you have an IO operation you can mark it appropriately.
We can use a simple class returns.io.IO
to mark impure parts of the program that do not fail.
>>> import random
>>> from returns.io import IO
>>> def get_random_number() -> IO[int]:
... return IO(random.randint(1, 10))
...
>>> assert isinstance(get_random_number(), IO)
And later we can work inside this IO context
and do not break into our pure part of the program:
>>> assert get_random_number().map(lambda number: number / number) == IO(1.0)
And it infects all other functions that call it.
>>> def modify_number(number: int) -> IO[float]:
... return get_random_number().map(lambda rnd: number / rnd)
...
>>> assert isinstance(modify_number(1), IO)
It is good enough to indicate that you are aware of side effects of the function.
IOResult¶
On the other hand, we can have IO parts of the program that do fail.
Imagine we have this beautiful pure function:
def can_book_seats(
number_of_seats: int,
reservation: 'Reservation',
) -> bool:
return reservation.capacity >= number_of_seats + reservation.booked
What’s good about it? We can test it easily. Even without setting up any testing framework, simple doctests will be enough.
This code is beautiful, because it is simple.
We can later use its result to process the result of the booking request:
def process_booking_result(is_successful: bool) -> 'ProcessID':
...
process_booking_result(is_successful) # works just fine!
At this point we don’t have IO in our program.
Impure functions¶
But, imagine that our requirements had changed. And now we have to grab the number of already booked tickets from some other microservice and fetch the maximum capacity from the database:
import requests
import db
def can_book_seats(
number_of_seats: int,
place_id: int,
) -> bool:
capacity = db.get_place_capacity(place_id) # sql query
booked = requests('https://partner.com/api').json()['booked'] # http req
return capacity >= number_of_seats + booked
Now testing this code will become a nightmare! It will require to setup:
real database and tables
fixture data
requestsmocks for different outcomesand the whole Universe!
Our complexity has sky-rocketed!
And the most annoying part is that all other functions
that call can_book_seats now also have to do the same setup.
It seams like IO is indelible mark (some people also call it “effect”).
And at some point it time we will start to mix pure and impure code together. Let’s not forget that all of these operations can fail too!
Separating two worlds¶
Well, IO mark is indeed indelible and should be respected.
And then impurity becomes explicit:
import requests
import db
from returns.io import IOResultE
def can_book_seats(
number_of_seats: int,
place_id: int,
) -> IOResultE[bool]:
...
Now this function returns IOResultE[bool] instead of a regular bool.
It means, that it cannot be used where regular bool can be:
def process_booking_result(is_successful: bool) -> 'ProcessID':
...
is_successful: IOResultE[bool] = can_book_seats(number_of_seats, place_id)
process_booking_result(is_successful) # Boom!
# => Argument 1 has incompatible type "IOResultE[bool]"; expected "bool"
See? It is now impossible for a pure function to use IOResultE[bool].
It is impossible to unwrap or get a raw value from this container.
Once it is marked as IO it will never return to the pure state
(well, there’s a hack actually:
unsafe_perform_io).
Now we have to work inside the IO context:
message_id: IOResultE['ProcessID'] = can_book_seats(
number_of_seats,
place_id,
).map(
process_booking_result,
)
Or it can be annotated to work with impure results:
def process_booking_result(
is_successful: IOResultE[bool],
) -> IOResultE['ProcessID']:
...
is_successful: IOResult[bool] = can_book_seats(number_of_seats, place_id)
process_booking_result(is_successful) # Works!
Now, all our impurity is explicit. We can track it, we can fight it, we can design it better. By saying that, it is assumed that you have a functional core and imperative shell.
Lifting¶
You can also lift regular functions into one
that works with IO or IOResult on both ends.
It really helps you with the composition!
>>> from returns.io import IO
>>> from returns.pointfree import map_
>>> def regular_function(arg: int) -> float:
... return arg / 2 # not an `IO` operation
>>> container = IO(1)
>>> # When we need to compose `regular_function` with `IO`,
>>> # we have two ways of doing it:
>>> io = container.map(regular_function)
>>> assert io == IO(0.5)
>>> # or, it is the same as:
>>> io = map_(regular_function)(container)
>>> assert io == IO(0.5)
IOResult can lift both regular functions and ones that return Result:
>>> from returns.io import IOResult, IOSuccess
>>> from returns.pointfree import map_
>>> def regular_function(arg: int) -> float:
... return arg / 2 # not an `IO` operation
>>> container: IOResult[int, str] = IOSuccess(1)
>>> # When we need to compose `regular_function` with `IOResult`,
>>> # we have two ways of doing it:
>>> io = container.map(regular_function)
>>> assert io == IOSuccess(0.5)
>>> # or, it is the same as:
>>> io = map_(regular_function)(container)
>>> assert io == IOSuccess(0.5)
And Result based functions:
>>> from returns.io import IOResult, IOSuccess
>>> from returns.result import Result, Success, Failure
>>> from returns.pointfree import bind_result
>>> def regular_function(arg: int) -> Result[float, str]:
... if arg > 0:
... return Success(arg / 2)
... return Failure('zero')
>>> assert bind_result(regular_function)(
... IOSuccess(1),
... ) == IOResult.from_result(regular_function(1))
Lifting is useful when using returns.pipeline.pipe()
and other different declarative tools.
Pattern Matching¶
IOResult values can be matched using the new feature of Python 3.10,
Structural Pattern Matching,
see the example below:
from returns.io import IOFailure, IOResult, IOSuccess
from returns.result import Success
container: IOResult[int, str] = IOSuccess(42)
match container:
# Matches if the result stored inside `IOSuccess` is `42`
# We need to use `Success` until the custom matching protocol
# is released. For more information, please visit:
# https://www.python.org/dev/peps/pep-0622/#custom-matching-protocol
case IOSuccess(Success(42)):
print('Result is "42"')
# Matches any `IOSuccess` instance
# and binds its value to the `value` variable
case IOSuccess(value):
print(f'Result is "{value}"')
# Matches any `IOFailure` instance
case IOFailure(_):
print('A failure was occurred')
Aliases¶
There are several useful aliases for IOResult type with some common values:
returns.io.IOResultEis an alias forIOResult[... Exception], just use it when you want to work withIOResultcontainers that use exceptions as error type. It is namedIOResultEbecause it isIOResultExceptionandIOResultErrorat the same time.
Decorators¶
Limitations¶
Typing will only work correctly if our mypy plugin is used. This happens due to mypy issue.
impure¶
We also have this handy decorator to help you with the existing impure things in Python:
from returns.io import impure
name: IO[str] = impure(input)('What is your name?')
You can also decorate your own functions
with @impure for better readability and clearness:
import random
from returns.io import impure
@impure
def get_user() -> 'User':
return random.randint(1, 5)
impure_safe¶
Similar to impure and safe decorators.
Once applied, it transforms the return type to be IOResultE:
from returns.io import IOResultE, impure_safe
@impure_safe
def http_get(path: str) -> 'Response':
return requests.get(path)
container: IOResultE['Response'] = http_get('/home')
Use for impure operations that might fail.
Helpers¶
Don’t forget to check out Converters.
unsafe_perform_io¶
Sometimes you really need to get the raw value from IO container.
For example:
def index_view(request, user_id):
user: IO[User] = get_user(user_id)
return render('index.html', {'user': user}) # ???
In this case your web-framework will not render your user correctly.
Since it does not expect it to be wrapped inside IO containers.
And we obviously cannot map or bind this function.
What to do? Use unsafe_perform_io:
from returns.unsafe import unsafe_perform_io
def index_view(request, user_id):
user: IO[User] = get_user(user_id)
return render('index.html', {'user': unsafe_perform_io(user)}) # Ok
We need it as an escape and compatibility mechanism for our imperative shell.
In other words:
>>> from returns.unsafe import unsafe_perform_io
>>> from returns.io import IO
>>> assert unsafe_perform_io(IO('abc')) == 'abc'
It is recommended
to use import-linter
to restrict imports from returns.unsafe expect the top-level modules.
Inspired by Haskell’s unsafePerformIO
FAQ¶
Why aren’t IO lazy?¶
Please, note that our IO implementation is not lazy by design.
This way when you mark something as @impure it will work as previously.
The only thing that changes is the return type.
Instead we offer to use unsafe_perform_io
to work with IO and simulate laziness.
But, you can always make your IO lazy:
>>> from returns.io import IO
>>> lazy = lambda: IO(1)
>>> assert lazy() == IO(1)
We have decided that it would be better and more familiar for Python devs.
What is the difference between IO[T] and T?¶
What kind of input parameter should
my function accept IO[T] or simple T?
It really depends on your domain / context.
If the value is pure, than use raw unwrapped values.
If the value is fetched, input, received, selected,
than use IO or IOResult container:
first one for operations that never fail,
second one for operations that might fail.
Most web applications are just fully covered with IO.
Why can’t we use IO[Result] instead of IOResult?¶
We actually can! But, it is harder to write.
And IOResult is actually the very same thing as IO[Result],
but has nicer API:
x: IO[Result[int, str]]
x.map(lambda io: io.map(lambda number: number + 1))
# Is the same as:
y: IOResult[int, str]
y.map(lambda number: number + 1)
The second one looks better, doesn’t it?
How to create unit objects for IOResult?¶
TLDR: you need to use IOSuccess and IOFailure functions
or IOResult.from_value and IOResult.from_failure methods:
>>> from returns.io import IOResult, IOSuccess, IOFailure
>>> first: IOResult[int, str] = IOSuccess(1)
>>> second: IOResult[float, int] = IOFailure(1)
>>> assert IOResult.from_value(1) == IOSuccess(1)
>>> assert IOResult.from_failure(2) == IOFailure(2)
You can also annotate your variables properly.
Otherwise, mypy will treat IOSuccess(1) as IOSuccess[int, Any].
You can narrow the type in advance.
See How to create unit objects? for more details.
Further reading¶
API Reference¶
classDiagram
ABC <|-- IOResult
BaseContainer <|-- IO
BaseContainer <|-- IOResult
IOLikeN <|-- IO
IOResult <|-- IOFailure
IOResult <|-- IOSuccess
IOResultBasedN <|-- IOResult
SupportsKindN <|-- IO
SupportsKindN <|-- IOResult
- class IO(inner_value)[source]¶
Bases:
BaseContainer,SupportsKindN[IO,_ValueType_co,Never,Never],IOLikeN[_ValueType_co,Never,Never]Explicit container for impure function results.
We also sometimes call it “marker” since once it is marked, it cannot be ever unmarked. There’s no way to directly get its internal value.
Note that
IOrepresents a computation that never fails.Examples of such computations are:
read / write to localStorage
get the current time
write to the console
get a random number
Use
IOResult[...]for operations that might fail. Like DB access or network operations.See also
- Parameters:
inner_value (
TypeVar(_ValueType_co, covariant=True))
- equals(other)¶
Typesafe equality comparison with other Result objects.
- map(function)[source]¶
Applies function to the inner value.
Applies ‘function’ to the contents of the IO instance and returns a new IO object containing the result. ‘function’ should accept a single “normal” (non-container) argument and return a non-container result.
>>> def mappable(string: str) -> str: ... return string + 'b' >>> assert IO('a').map(mappable) == IO('ab')
- Parameters:
function (
Callable[[TypeVar(_ValueType_co, covariant=True)],TypeVar(_NewValueType)])- Return type:
IO[TypeVar(_NewValueType)]
- apply(container)[source]¶
Calls a wrapped function in a container on this container.
>>> from returns.io import IO >>> assert IO('a').apply(IO(lambda inner: inner + 'b')) == IO('ab')
Or more complex example that shows how we can work with regular functions and multiple
IOarguments:>>> from returns.curry import curry >>> @curry ... def appliable(first: str, second: str) -> str: ... return first + second >>> assert IO('b').apply(IO('a').apply(IO(appliable))) == IO('ab')
- bind(function)[source]¶
Applies ‘function’ to the result of a previous calculation.
‘function’ should accept a single “normal” (non-container) argument and return
IOtype object.>>> def bindable(string: str) -> IO[str]: ... return IO(string + 'b') >>> assert IO('a').bind(bindable) == IO('ab')
- bind_io(function)¶
Alias for bind method. Part of the IOLikeN interface.
- classmethod do(expr)[source]¶
Allows working with unwrapped values of containers in a safe way.
>>> from returns.io import IO >>> assert IO.do( ... first + second ... for first in IO(2) ... for second in IO(3) ... ) == IO(5)
See Do Notation to learn more.
- Parameters:
expr (
Generator[TypeVar(_NewValueType),None,None])- Return type:
IO[TypeVar(_NewValueType)]
- classmethod from_value(inner_value)[source]¶
Unit function to construct new
IOvalues.Is the same as regular constructor:
>>> from returns.io import IO >>> assert IO(1) == IO.from_value(1)
Part of the
returns.interfaces.applicative.ApplicativeNinterface.- Parameters:
inner_value (
TypeVar(_NewValueType))- Return type:
IO[TypeVar(_NewValueType)]
- classmethod from_io(inner_value)[source]¶
Unit function to construct new
IOvalues from existingIO.>>> from returns.io import IO >>> assert IO(1) == IO.from_io(IO(1))
Part of the
returns.interfaces.specific.IO.IOLikeNinterface.
- classmethod from_ioresult(inner_value)[source]¶
Converts
IOResult[a, b]back toIO[Result[a, b]].Can be really helpful for composition.
>>> from returns.io import IO, IOSuccess >>> from returns.result import Success >>> assert IO.from_ioresult(IOSuccess(1)) == IO(Success(1))
Is the reverse of
returns.io.IOResult.from_typecast().
- impure(function)[source]¶
Decorator to mark function that it returns
IOcontainer.If you need to mark
asyncfunction as impure, usereturns.future.future()instead. This decorator only works with sync functions. Example:>>> from returns.io import IO, impure >>> @impure ... def function(arg: int) -> int: ... return arg + 1 # this action is pure, just an example ... >>> assert function(1) == IO(2)
- Parameters:
function (
Callable[[ParamSpec(_FuncParams, bound=None)],TypeVar(_NewValueType)])- Return type:
Callable[[ParamSpec(_FuncParams, bound=None)],IO[TypeVar(_NewValueType)]]
- class IOResult(inner_value)[source]¶
Bases:
BaseContainer,SupportsKindN[IOResult,_ValueType_co,_ErrorType_co,Never],IOResultBasedN[_ValueType_co,_ErrorType_co,Never],ABCExplicit container for impure function results that might fail.
Definition
This type is similar to
returns.result.Result. This basically a more useful version ofIO[Result[a, b]]. Use this type forIOcomputations that might fail. Examples ofIOcomputations that might fail are:access database
access network
access filesystem
Use
IOfor operations that doIObut do not fail.Note, that even methods like
unwrap`()andvalue_or()return values wrapped inIO.IOResultis a complex compound value that consists of:raw value
ResultIO
This is why it has so many helper and factory methods:
You can construct
IOResultfrom raw values withIOSuccess()andIOFailure()public type constructorsYou can construct
IOResultfromIOvalues withfrom_failed_io()andIOResult.from_io()You can construct
IOResultfromResultvalues withfrom_result()
We also have a lot of utility methods for better function composition like:
bind_result()to work with functions which returnResultfrom_typecast()to work withIO[Result[...]]values
Implementation
This class contains all the methods that can be delegated to
Result. But, some methods are not implemented which means that we have to use special_IOSuccessand_IOFailureimplementation details to correctly handle these callbacks.Do not rely on them! Use public functions and types instead.
- Parameters:
inner_value (
Result[TypeVar(_ValueType_co, covariant=True),TypeVar(_ErrorType_co, covariant=True)])
- equals(other)¶
Typesafe equality comparison with other IOResult objects.
- property trace: list[FrameInfo] | None¶
Returns a stack trace when
IOFailure()was called.
- swap()[source]¶
Swaps value and error types.
So, values become errors and errors become values. It is useful when you have to work with errors a lot. And since we have a lot of
.bind_related methods and only a single.lash- it is easier to work with values.>>> from returns.io import IOSuccess, IOFailure >>> assert IOSuccess(1).swap() == IOFailure(1) >>> assert IOFailure(1).swap() == IOSuccess(1)
- Return type:
IOResult[TypeVar(_ErrorType_co, covariant=True),TypeVar(_ValueType_co, covariant=True)]
- map(function)[source]¶
Composes successful container with a pure function.
>>> from returns.io import IOSuccess >>> assert IOSuccess(1).map(lambda num: num + 1) == IOSuccess(2)
- Parameters:
function (
Callable[[TypeVar(_ValueType_co, covariant=True)],TypeVar(_NewValueType)])- Return type:
IOResult[TypeVar(_NewValueType),TypeVar(_ErrorType_co, covariant=True)]
- apply(container)[source]¶
Calls a wrapped function in a container on this container.
>>> from returns.io import IOSuccess, IOFailure >>> def appliable(first: str) -> str: ... return first + 'b' >>> assert IOSuccess('a').apply( ... IOSuccess(appliable), ... ) == IOSuccess('ab') >>> assert IOFailure('a').apply( ... IOSuccess(appliable), ... ) == IOFailure('a') >>> assert IOSuccess('a').apply(IOFailure(1)) == IOFailure(1) >>> assert IOFailure('a').apply(IOFailure('b')) == IOFailure('a')
- bind(function)[source]¶
Composes successful container with a function that returns a container.
>>> from returns.io import IOResult, IOFailure, IOSuccess >>> def bindable(string: str) -> IOResult[str, str]: ... if len(string) > 1: ... return IOSuccess(string + 'b') ... return IOFailure(string + 'c') >>> assert IOSuccess('aa').bind(bindable) == IOSuccess('aab') >>> assert IOSuccess('a').bind(bindable) == IOFailure('ac') >>> assert IOFailure('a').bind(bindable) == IOFailure('a')
- bind_ioresult(function)¶
Alias for bind_ioresult method. Part of the IOResultBasedN interface.
- bind_result(function)[source]¶
Composes successful container with a function that returns a container.
Similar to
bind(), but works with containers that returnreturns.result.Resultinstead ofIOResult.>>> from returns.io import IOFailure, IOSuccess >>> from returns.result import Result, Success >>> def bindable(string: str) -> Result[str, str]: ... if len(string) > 1: ... return Success(string + 'b') ... return Failure(string + 'c') >>> assert IOSuccess('aa').bind_result(bindable) == IOSuccess('aab') >>> assert IOSuccess('a').bind_result(bindable) == IOFailure('ac') >>> assert IOFailure('a').bind_result(bindable) == IOFailure('a')
- bind_io(function)[source]¶
Composes successful container with a function that returns a container.
Similar to
bind(), but works with containers that returnreturns.io.IOinstead ofIOResult.>>> from returns.io import IO, IOFailure, IOSuccess >>> def bindable(string: str) -> IO[str]: ... return IO(string + 'z') >>> assert IOSuccess('a').bind_io(bindable) == IOSuccess('az') >>> assert IOFailure('a').bind_io(bindable) == IOFailure('a')
- alt(function)[source]¶
Composes failed container with a pure function to modify failure.
>>> from returns.io import IOFailure >>> assert IOFailure(1).alt(float) == IOFailure(1.0)
- Parameters:
function (
Callable[[TypeVar(_ErrorType_co, covariant=True)],TypeVar(_NewErrorType)])- Return type:
IOResult[TypeVar(_ValueType_co, covariant=True),TypeVar(_NewErrorType)]
- lash(function)[source]¶
Composes failed container with a function that returns a container.
>>> from returns.io import IOFailure, IOSuccess, IOResult >>> def lashable(state: str) -> IOResult[int, str]: ... if len(state) > 1: ... return IOSuccess(len(state)) ... return IOFailure('oops') >>> assert IOFailure('a').lash(lashable) == IOFailure('oops') >>> assert IOFailure('abc').lash(lashable) == IOSuccess(3) >>> assert IOSuccess('a').lash(lashable) == IOSuccess('a')
- value_or(default_value)[source]¶
Get value from successful container or default value from failed one.
>>> from returns.io import IO, IOFailure, IOSuccess >>> assert IOSuccess(1).value_or(None) == IO(1) >>> assert IOFailure(1).value_or(None) == IO(None)
- Parameters:
default_value (
TypeVar(_NewValueType))- Return type:
IO[Union[TypeVar(_ValueType_co, covariant=True),TypeVar(_NewValueType)]]
- unwrap()[source]¶
Get value from successful container or raise exception for failed one.
>>> from returns.io import IO, IOFailure, IOSuccess >>> assert IOSuccess(1).unwrap() == IO(1) >>> IOFailure(1).unwrap() Traceback (most recent call last): ... returns.primitives.exceptions.UnwrapFailedError
- Return type:
IO[TypeVar(_ValueType_co, covariant=True)]
- failure()[source]¶
Get failed value from failed container or raise exception from success.
>>> from returns.io import IO, IOFailure, IOSuccess >>> assert IOFailure(1).failure() == IO(1) >>> IOSuccess(1).failure() Traceback (most recent call last): ... returns.primitives.exceptions.UnwrapFailedError
- Return type:
IO[TypeVar(_ErrorType_co, covariant=True)]
- compose_result(function)[source]¶
Composes inner
ResultwithIOResultreturning function.Can be useful when you need an access to both states of the result.
>>> from returns.io import IOResult, IOSuccess, IOFailure >>> from returns.result import Result >>> def count(container: Result[int, int]) -> IOResult[int, int]: ... return IOResult.from_result( ... container.map(lambda x: x + 1).alt(abs), ... ) >>> assert IOSuccess(1).compose_result(count) == IOSuccess(2) >>> assert IOFailure(-1).compose_result(count) == IOFailure(1)
- classmethod do(expr)[source]¶
Allows working with unwrapped values of containers in a safe way.
>>> from returns.io import IOResult, IOFailure, IOSuccess >>> assert IOResult.do( ... first + second ... for first in IOSuccess(2) ... for second in IOSuccess(3) ... ) == IOSuccess(5) >>> assert IOResult.do( ... first + second ... for first in IOFailure('a') ... for second in IOSuccess(3) ... ) == IOFailure('a')
See Do Notation to learn more. This feature requires our mypy plugin.
- Parameters:
expr (
Generator[TypeVar(_NewValueType),None,None])- Return type:
IOResult[TypeVar(_NewValueType),TypeVar(_NewErrorType)]
- classmethod from_typecast(inner_value)[source]¶
Converts
IO[Result[_ValueType_co, _ErrorType_co]]toIOResult.Also prevails the type of
ResulttoIOResult, so:IO[Result[_ValueType_co, _ErrorType_co]]would becomeIOResult[_ValueType_co, _ErrorType_co].>>> from returns.result import Success >>> from returns.io import IO, IOResult, IOSuccess >>> container = IO(Success(1)) >>> assert IOResult.from_typecast(container) == IOSuccess(1)
Can be reverted via
returns.io.IO.from_ioresult()method.
- classmethod from_failed_io(inner_value)[source]¶
Creates new
IOResultfrom “failed”IOcontainer.>>> from returns.io import IO, IOResult, IOFailure >>> container = IO(1) >>> assert IOResult.from_failed_io(container) == IOFailure(1)
- classmethod from_io(inner_value)[source]¶
Creates new
IOResultfrom “successful”IOcontainer.>>> from returns.io import IO, IOResult, IOSuccess >>> container = IO(1) >>> assert IOResult.from_io(container) == IOSuccess(1)
- classmethod from_result(inner_value)[source]¶
Creates
IOResultfromResultvalue.>>> from returns.io import IOResult, IOSuccess, IOFailure >>> from returns.result import Success, Failure >>> assert IOResult.from_result(Success(1)) == IOSuccess(1) >>> assert IOResult.from_result(Failure(2)) == IOFailure(2)
- classmethod from_ioresult(inner_value)[source]¶
Creates
IOResultfrom existingIOResultvalue.>>> from returns.io import IOResult, IOSuccess, IOFailure >>> assert IOResult.from_ioresult(IOSuccess(1)) == IOSuccess(1) >>> assert IOResult.from_ioresult(IOFailure(2)) == IOFailure(2)
- classmethod from_value(inner_value)[source]¶
One more value to create success unit values.
It is useful as a united way to create a new value from any container.
>>> from returns.io import IOResult, IOSuccess >>> assert IOResult.from_value(1) == IOSuccess(1)
You can use this method or
IOSuccess(), choose the most convenient for you.- Parameters:
inner_value (
TypeVar(_NewValueType))- Return type:
IOResult[TypeVar(_NewValueType),Any]
- classmethod from_failure(inner_value)[source]¶
One more value to create failure unit values.
It is useful as a united way to create a new value from any container.
>>> from returns.io import IOResult, IOFailure >>> assert IOResult.from_failure(1) == IOFailure(1)
You can use this method or
IOFailure(), choose the most convenient for you.- Parameters:
inner_value (
TypeVar(_NewErrorType))- Return type:
IOResult[Any,TypeVar(_NewErrorType)]
- final class IOFailure(inner_value)[source]¶
Bases:
IOResult[Any,_ErrorType_co]IOFailurerepresentation.- Parameters:
inner_value (
TypeVar(_ErrorType_co, covariant=True))
- bind_ioresult(function)¶
Alias for bind_ioresult method. Part of the IOResultBasedN interface. # noqa: E501
- final class IOSuccess(inner_value)[source]¶
Bases:
IOResult[_ValueType_co,Any]IOSuccessrepresentation.- Parameters:
inner_value (
TypeVar(_ValueType_co, covariant=True))
- bind_ioresult(function)¶
Alias for bind_ioresult method. Part of the IOResultBasedN interface. # noqa: E501
- IOResultE¶
Alias for
IOResult[_ValueType_co, Exception].alias of
IOResult[_ValueType_co,Exception]
- impure_safe(exceptions)[source]¶
Decorator to mark function that it returns
IOResultcontainer.Should be used with care, since it only catches
Exceptionsubclasses. It does not catchBaseExceptionsubclasses.If you need to mark
asyncfunction as impure, usereturns.future.future_safe()instead. This decorator only works with sync functions. Example:>>> from returns.io import IOSuccess, impure_safe >>> @impure_safe ... def function(arg: int) -> float: ... return 1 / arg ... >>> assert function(1) == IOSuccess(1.0) >>> assert function(0).failure()
You can also use it with explicit exception types as the first argument:
>>> from returns.io import IOSuccess, IOFailure, impure_safe >>> @impure_safe(exceptions=(ZeroDivisionError,)) ... def might_raise(arg: int) -> float: ... return 1 / arg >>> assert might_raise(1) == IOSuccess(1.0) >>> assert isinstance(might_raise(0), IOFailure)
In this case, only exceptions that are explicitly listed are going to be caught.
Similar to
returns.future.future_safe()andreturns.result.safe()decorators.- Parameters:
exceptions (
Callable[[ParamSpec(_FuncParams, bound=None)],TypeVar(_NewValueType)] |tuple[type[TypeVar(_ExceptionType, bound=Exception)],...])- Return type:
Callable[[ParamSpec(_FuncParams, bound=None)],IOResult[TypeVar(_NewValueType),Exception]] |Callable[[Callable[[ParamSpec(_FuncParams, bound=None)],TypeVar(_NewValueType)]],Callable[[ParamSpec(_FuncParams, bound=None)],IOResult[TypeVar(_NewValueType),TypeVar(_ExceptionType, bound=Exception)]]]
- unsafe_perform_io(wrapped_in_io)[source]¶
Compatibility utility and escape mechanism from
IOworld.Just unwraps the internal value from
returns.io.IOcontainer. Should be used with caution! Since it might be overused by lazy and ignorant developers.It is recommended to have only one place (module / file) in your program where you allow unsafe operations.
We recommend to use
import-linterto enforce this rule.>>> from returns.io import IO >>> assert unsafe_perform_io(IO(1)) == 1
- Parameters:
wrapped_in_io (
IO[TypeVar(_ValueType)])- Return type:
TypeVar(_ValueType)