I'd like to run a type checker against the code in tasks.py however the fact that Context.run returns Result | None instead of just a Result makes it rather annoying to deal with. I constantly have to assert result is not None in order to access attributes like Result.stdout.
Given the underlying implementation, the solution is to describe run() with overloads. Further, since there a number of public interfaces that look like Context.run, in this PR I've devised an annotate_run_function decorator that makes it easy to declare a function as having a run-like interface. Beyond just properly describing the return type of run-like methods, this PR also annotates all the valid options that can be passed to run as well. I also find this quite useful since I always have to dive into the docs for Runner.run to figure out what parameters are available instead of just being able to refer to Context.run.
In order to make all this happen I've had to do a couple things...
First, I had to upgrade MyPy for it to understand the most recent type tooling (e.g. Unpack). This allowed me to remove a couple type: ignore comments. However, the latest version of MyPy only supports Python>=3.8 so it looks like there will need to be a change to orb/invocations so that it runs under Python 3.8 by default. Dropping support for Python<3.8 seems fairly reasonable - for example, Numpy's policy is that it's newest releases will only support the last three Python versions (3.10+ as of writing).
Second, I needed to use typing_extensions in order to get access to back-ported implementations of newer typing features. Since invoke doesn't seem to have dependencies I've defined most of the type annotations in if TYPE_CHECKING to avoid any issues at runtime.
I'd like to run a type checker against the code in
tasks.py
however the fact thatContext.run
returnsResult | None
instead of just aResult
makes it rather annoying to deal with. I constantly have toassert result is not None
in order to access attributes likeResult.stdout
.Given the underlying implementation, the solution is to describe
run()
with overloads. Further, since there a number of public interfaces that look likeContext.run
, in this PR I've devised anannotate_run_function
decorator that makes it easy to declare a function as having arun
-like interface. Beyond just properly describing the return type ofrun
-like methods, this PR also annotates all the valid options that can be passed torun
as well. I also find this quite useful since I always have to dive into the docs forRunner.run
to figure out what parameters are available instead of just being able to refer toContext.run
.In order to make all this happen I've had to do a couple things...
First, I had to upgrade MyPy for it to understand the most recent type tooling (e.g.
Unpack
). This allowed me to remove a coupletype: ignore
comments. However, the latest version of MyPy only supports Python>=3.8 so it looks like there will need to be a change toorb/invocations
so that it runs under Python 3.8 by default. Dropping support for Python<3.8 seems fairly reasonable - for example, Numpy's policy is that it's newest releases will only support the last three Python versions (3.10+ as of writing).Second, I needed to use
typing_extensions
in order to get access to back-ported implementations of newer typing features. Sinceinvoke
doesn't seem to have dependencies I've defined most of the type annotations inif TYPE_CHECKING
to avoid any issues at runtime.