Type checking - Pyright
Ok, so we have a linter, which creates so called AST (Abstract Syntax Tree) of the code and checks it against the rules. But it doesn’t take into account the types and which packages are installed and which are not. That’s where type checking comes in.
The problem
I’d say type checking works two fold. On one hand it checks if the types are correct, on the other it is environment aware tool that knows which packages are installed and which are not and what are their contents.
Types
The simplest example is:
def divide(a: int, b: int) -> int:
return a / b
pyright
will catch this issue:
example.py:2: error: Incompatible return value type (got "float", expected "int") [return-value]
But a more interesting example is:
def foo(a: int) -> int | None:
if a > 0:
return a
else:
return None
def bar(a: int) -> int:
return a + 1
= foo(1)
x = bar(x) y
We get an error:
example.py:10:9 - error: Argument of type "int | None" cannot be assigned to parameter "a" of type "int" in function "bar"
Type "int | None" is incompatible with type "int"
"None" is incompatible with "int" (reportArgumentType)
pyright
knows that x
can be None
and it will catch it. What’s cool is that
= foo(1)
x if x is not None:
= bar(x)
y else:
= 0 y
Will work just fine and pyright
will know that x
is an integer inside the if
block.
Environment awareness
ruff
will catch issue like:
import numpy as np
= npp.array([1, 2, 3]) x
and it will tell you that npp
is not defined.
But it won’t catch issues like:
import numpy as np
= np.arrray([1, 2, 3]) x
From ruff
perspective, everything is fine, we’re importing numpy
and using it. Maybe it has a function called arrray
? Who knows?
The solution
As you may have guessed, the solution is pyright
or type checking in general (with tools like mypy
/pyre
).
Since Python 3.5, we have type annotations. You can add type hints to your functions and variables, but it’s not enforced by the language. Since then, many projects added detailed type hints to their codebase and you exactly know what type of data you should expect. For example we know that pd.read_csv
returns a pd.DataFrame
, and that df['column']
returns a pd.Series
. This way IDE can provide better autocompletion.
In tapyr
we use pyright
for type checking, as this is the tool used by shiny
internally.
How to use it
You should turn on type checking in your IDE. In VSCode
you can do it by installing Python
extension and then clicking here:
Then, pyright
will check your code and show you errors in the Problems
tab. You can also run it from the terminal:
pyright
Type checking is also automatically run in the CI pipeline (more on that later).