Pyright didn't catch my error
I was looking at some Python code I wrote a while ago, when I realized it had an obvious error:
import subprocess
from typing import Any, Tuple
def sh2(cmd: str, check: bool = True, **kwargs: Any) -> Tuple[str, str]:
proc = _sh(check=check, capture_output=True, **kwargs)
return proc.stdout, proc.stderr
def _sh(cmd: str, **kwargs: Any) -> subprocess.CompletedProcess[str]:
return subprocess.run(["/usr/bin/env", "bash", "-c", cmd], text=True, **kwargs)
When sh2
calls _sh
, it does not pass the cmd
parameter.
It's a simple bug, so I wondered why Pyright, which I run in pre-commit, didn't catch it. My conjecture is that the presence of **kwargs
fools Pyright; it thinks that kwargs
could supply the cmd
parameter, so the call to _sh
is OK. In fact, because cmd
is also a parameter to sh2
, it's impossible for kwargs
to contain cmd
, so I don't think there's any way to invoke sh2
that doesn't trigger a TypeError
. I filed an issue with Pyright: https://github.com/microsoft/pyright/issues/10933.
I also didn't get a warning that the cmd
argument was unused in sh2
, which would have alerted me to the type error. Pyright explicitly does not check for unused parameters, so it's up to the linter to catch them. Flake8 does not have built-in support, but there's a plugin, flake8-unused-arguments
, that you can install, and then add to your flake8
config:
enable-extensions = U100
unused-arguments-ignore-abstract-functions = True
unused-arguments-ignore-overload-functions = True
unused-arguments-ignore-override-functions = True
unused-arguments-ignore-variadic-names = True
When I turned this check on, it revealed many other places where parameters were unused. Most were innocuous, but one or two also appeared to be genuine bugs.
Gradual typing is better than nothing at all, but it's not perfect. Even aside from type-checker edge cases, it's hard for me to feel confident that I haven't accidentally opted out of a crucial type-check because I used the Any
type, or forgot to annotate a function's parameters, or turned off a configuration option.