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 / bpyright 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
x = foo(1)
y = bar(x)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
x = foo(1)
if x is not None:
y = bar(x)
else:
y = 0Will 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
x = npp.array([1, 2, 3])and it will tell you that npp is not defined.
But it won’t catch issues like:
import numpy as np
x = np.arrray([1, 2, 3])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:
pyrightType checking is also automatically run in the CI pipeline (more on that later).