mypy plugin¶
We provide a custom mypy plugin to fix existing issues,
provide new awesome features,
and improve type-safety of things developers commonly use.
Installation¶
returns has [compatible-mypy] extra to install the supported version.
pip install 'returns[compatible-mypy]'
Or you can install mypy separately and check that version is supported.
Enabling our mypy plugin¶
To install our mypy plugin add it
to the plugins section of the config file (setup.cfg or mypy.ini):
[mypy]
plugins =
returns.contrib.mypy.returns_plugin
Or in pyproject.toml:
[tool.mypy]
plugins = ["returns.contrib.mypy.returns_plugin"]
We recommend to always add our plugin as the first one in chain.
Configuration¶
You can have a look at the suggested mypy
configuration
in our own repository.
You can also use nitpick
tool to enforce the same mypy configuration for all your projects.
We recommend to use our own setup. Add this to your pyproject.toml:
[tool.nitpick]
style = "https://raw.githubusercontent.com/wemake-services/wemake-python-styleguide/master/styles/mypy.toml"
And use flake8 to lint that configuration
defined in the setup matches yours.
This will allow to keep them in sync with the upstream.
Supported features¶
kindfeature adds Higher Kinded Types (HKT) supportcurryfeature allows to write typed curried functionspartialfeature allows to write typed partial applicationflowfeature allows to write better typed functional pipelines withflowfunctionpipefeature allows to write better typed functional pipelines withpipefunctiondo-notationfeature allows using Do Notation
Further reading¶
API Reference¶
Plugin definition¶
- TYPED_PARTIAL_FUNCTION: Final = 'returns.curry.partial'¶
Used for typed
partialfunction.
- TYPED_CURRY_FUNCTION: Final = 'returns.curry.curry'¶
Used for typed
currydecorator.
- TYPED_FLOW_FUNCTION: Final = 'returns._internal.pipeline.flow.flow'¶
Used for typed
flowcall.
- TYPED_PIPE_FUNCTION: Final = 'returns._internal.pipeline.pipe.pipe'¶
Used for typed
pipecall.
- TYPED_KINDN: Final = 'returns.primitives.hkt.KindN'¶
Used for HKT emulation.
- DO_NOTATION_METHODS: Final = ('returns.io.IO.do', 'returns.maybe.Maybe.do', 'returns.future.Future.do', 'returns.result.Result.do', 'returns.io.IOResult.do', 'returns.future.FutureResult.do')¶
Used for Do Notation.
classDiagram
Plugin <|-- _ReturnsPlugin
Custom mypy plugin to solve the temporary problem with python typing.
Important: we don’t do anything ugly here. We only solve problems of the current typing implementation.
mypy API docs are here:
https://mypy.readthedocs.io/en/latest/extending_mypy.html
We use pytest-mypy-plugins to test that it works correctly, see:
https://github.com/mkurnikov/pytest-mypy-plugins
Kind¶
- attribute_access(ctx)[source]¶
Ensures that attribute access to
KindNis correct.In other words:
from typing import TypeVar from returns.primitives.hkt import KindN from returns.interfaces.mappable import MappableN _MappableType = TypeVar('_MappableType', bound=MappableN) kind: KindN[_MappableType, int, int, int] reveal_type(kind.map) # will work correctly!
- Parameters:
ctx (
AttributeContext)- Return type:
Type
- dekind(ctx)[source]¶
Infers real type behind
Kindform.Basically, it turns
Kind[IO, int]intoIO[int]. The only limitation is that it works with onlyInstancetype in the first type argument position.So,
dekind(KindN[T, int])will fail.- Parameters:
ctx (
FunctionContext)- Return type:
Type
- kinded_signature(ctx)[source]¶
Returns the internal function wrapped as
Kinded[def].Works for
Kindedclass when__call__magic method is used. Seereturns.primitives.hkt.Kindedfor more information.- Parameters:
ctx (
MethodSigContext)- Return type:
CallableType
- kinded_call(ctx)[source]¶
Reveals the correct return type of
Kinded.__call__method.Turns
-> KindN[I, t1, t2, t3]into-> I[t1, t2, t3].Also strips unused type arguments for
KindN, so: -KindN[IO, int, Never, Never]will beIO[int]-KindN[Result, int, str, Never]will beResult[int, str]It also processes nested
KindNwith recursive strategy.See
returns.primitives.hkt.Kindedfor more information.- Parameters:
ctx (
MethodContext)- Return type:
Type
Curry¶
Partial¶
- analyze(ctx)[source]¶
This hook is used to make typed curring a thing in returns project.
This plugin is a temporary solution to the problem. It should be later replaced with the official way of doing things. One day functions will have better API and we plan to submit this plugin into
mypycore plugins, so it would not be required.Internally we just reduce the original function’s argument count. And drop some of them from function’s signature.
- Parameters:
ctx (
FunctionContext)- Return type:
ProperType
Flow¶
- analyze(ctx)[source]¶
Helps to analyze
flowfunction calls.By default,
mypycannot infer and check this function call:>>> from returns.pipeline import flow >>> assert flow( ... 1, ... lambda x: x + 1, ... lambda y: y / 2, ... ) == 1.0
But, this plugin can! It knows all the types for all
lambdafunctions in the pipeline. How?We use the first passed parameter as the first argument to the first passed function
We use parameter + function to check the call and reveal types of current pipeline step
We iterate through all passed function and use previous return type as a new parameter to call current function
- Parameters:
ctx (
FunctionContext)- Return type:
Type
Pipe¶
Typing pipe functions requires several phases.
It is pretty obvious from its usage:
When we pass a sequence of functions we have to reduce the final callable type, it is require to match the
callableprotocol. And at this point we also kinda try to check that all pipeline functions do match, but this is impossible to do 100% correctly at this point, because generic functions don’t have a type argument to infer the final resultWhen we call the function, we need to check for two things. First, we check that passed argument fits our instance requirement. Second, we check that pipeline functions match. Now we have all arguments to do the real inference.
We also need to fix generic in method signature. It might be broken, because we add new generic arguments and return type. So, it is safe to reattach generic back to the function.
Here’s when it works:
>>> from returns.pipeline import pipe
>>> def first(arg: int) -> bool:
... return arg > 0
>>> def second(arg: bool) -> str:
... return 'bigger' if arg else 'not bigger'
>>> pipeline = pipe(first, second) # `analyzed` is called
>>> assert pipeline(1) == 'bigger' # `signature and `infer` are called
>>> assert pipeline(0) == 'not bigger' # `signature and `infer` again
- analyze(ctx)[source]¶
This hook helps when we create the pipeline from sequence of funcs.
- Parameters:
ctx (
FunctionContext)- Return type:
Type
Do notation¶
- analyze(ctx)[source]¶
Used to handle validation and error types in Do Notation.
What it does?
For all types we ensure that only a single container type is used in a single do-notation. We don’t allow mixing them.
For types with error types (like
Result), it inferes what possible errors types can we have. The result is aUnionof all possible errors.Ensures that expression passed into
.domethod is literal.Checks that default value is provided if generator expression has
ifconditions inside.
- Parameters:
ctx (
MethodContext)- Return type:
Type