botan icon indicating copy to clipboard operation
botan copied to clipboard

Add Python typechecking to CI

Open randombit opened this issue 3 months ago • 1 comments

With addition of type checking to Python code in #5086 we should be also checking the types in CI

Options include

  • https://pyrefly.org/
  • https://mypy-lang.org/ [seems very slow]
  • https://github.com/google/pytype [abandoned]
  • https://github.com/microsoft/pyright
  • https://github.com/astral-sh/ty

Haven't looked at any of these. Leaning to Pyrefly or ty just because they are written in Rust rather than Python like the others, and so likely much faster.

randombit avatar Sep 03 '25 19:09 randombit

If we're using ruff anyway, might as well use ty. They are still in beta, but quickly trying it out it works well and the error messages are nicely formatted and descriptive

❯ , ty check botan3.py
error[invalid-argument-type]: Argument to function `_ctype_str` is incorrect
    --> botan3.py:1438:87
     |
1437 |         priv = PrivateKey()
1438 |         _DLL.botan_privkey_create(byref(priv.handle_()), _ctype_str(algo), _ctype_str(params), rng_obj.handle_())
     |                                                                                       ^^^^^^ Expected `str | None`, found `str | int | tuple[int, int]`
1439 |         return priv
     |
info: Function defined here
   --> botan3.py:648:5
    |
646 |     return result
647 |
648 | def _ctype_str(s: str | None) -> bytes | None:
    |     ^^^^^^^^^^ ------------- Parameter declared here
649 |     if s is None:
650 |         return None
    |
info: rule `invalid-argument-type` is enabled by default

(Not an actual error, just an artifact of how PrivateKey handles parameters for CMCE. Also colors are missing here, but they look nice in person!)

Pyrefly seems cool too, but has not as nice formatting (and doesn't resolve the original function):

  Object of class `int` has no attribute `__getitem__`
ERROR Argument `int | str | tuple[int, int]` is not assignable to parameter `s` with type `str | None` in function `_ctype_str` [bad-argument-type]
    --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:1438:87
     |
1438 |         _DLL.botan_privkey_create(byref(priv.handle_()), _ctype_str(algo), _ctype_str(params), rng_obj.handle_())
     |                                                                                       ^^^^^^
     |

It does complain about more errors though:

ty
error[unresolved-attribute]: Unresolved attribute `output` on type `_CFunctionType`.
   --> botan3.py:628:5
    |
626 | @VIEW_BIN_CALLBACK
627 | def _view_bin_fn(_ctx, buf_val, buf_len):
628 |     _view_bin_fn.output = buf_val[0:buf_len]
    |     ^^^^^^^^^^^^^^^^^^^
629 |     return 0
    |
info: rule `unresolved-attribute` is enabled by default

error[unresolved-attribute]: Type `_CFunctionType` has no attribute `output`
   --> botan3.py:633:14
    |
631 | def _call_fn_viewing_vec(fn) -> bytes:
632 |     fn(None, _view_bin_fn)
633 |     result = _view_bin_fn.output
    |              ^^^^^^^^^^^^^^^^^^^
634 |     _view_bin_fn.output = None
635 |     return result
    |
info: rule `unresolved-attribute` is enabled by default

error[unresolved-attribute]: Unresolved attribute `output` on type `_CFunctionType`.
   --> botan3.py:634:5
    |
632 |     fn(None, _view_bin_fn)
633 |     result = _view_bin_fn.output
634 |     _view_bin_fn.output = None
    |     ^^^^^^^^^^^^^^^^^^^
635 |     return result
    |
info: rule `unresolved-attribute` is enabled by default

error[unresolved-attribute]: Unresolved attribute `output` on type `_CFunctionType`.
   --> botan3.py:639:5
    |
637 | @VIEW_STR_CALLBACK
638 | def _view_str_fn(_ctx, str_val, _str_len):
639 |     _view_str_fn.output = str_val
    |     ^^^^^^^^^^^^^^^^^^^
640 |     return 0
    |
info: rule `unresolved-attribute` is enabled by default

error[unresolved-attribute]: Type `_CFunctionType` has no attribute `output`
   --> botan3.py:644:14
    |
642 | def _call_fn_viewing_str(fn) -> str:
643 |     fn(None, _view_str_fn)
644 |     result = _view_str_fn.output.decode('utf8')
    |              ^^^^^^^^^^^^^^^^^^^
645 |     _view_str_fn.output = None
646 |     return result
    |
info: rule `unresolved-attribute` is enabled by default

error[unresolved-attribute]: Unresolved attribute `output` on type `_CFunctionType`.
   --> botan3.py:645:5
    |
643 |     fn(None, _view_str_fn)
644 |     result = _view_str_fn.output.decode('utf8')
645 |     _view_str_fn.output = None
    |     ^^^^^^^^^^^^^^^^^^^
646 |     return result
    |
info: rule `unresolved-attribute` is enabled by default

error[non-subscriptable]: Cannot subscript object of type `int` with no `__getitem__` method
    --> botan3.py:1437:33
     |
1435 |             # TODO(Botan4) remove this case
1436 |             algo = 'McEliece'
1437 |             params = "%d,%d" % (params[0], params[1])
     |                                 ^^^^^^
1438 |
1439 |         priv = PrivateKey()
     |
info: rule `non-subscriptable` is enabled by default

error[non-subscriptable]: Cannot subscript object of type `int` with no `__getitem__` method
    --> botan3.py:1437:44
     |
1435 |             # TODO(Botan4) remove this case
1436 |             algo = 'McEliece'
1437 |             params = "%d,%d" % (params[0], params[1])
     |                                            ^^^^^^
1438 |
1439 |         priv = PrivateKey()
     |
info: rule `non-subscriptable` is enabled by default

error[invalid-argument-type]: Argument to function `_ctype_str` is incorrect
    --> botan3.py:1440:87
     |
1439 |         priv = PrivateKey()
1440 |         _DLL.botan_privkey_create(byref(priv.handle_()), _ctype_str(algo), _ctype_str(params), rng_obj.handle_())
     |                                                                                       ^^^^^^ Expected `str | None`, found `str | int | tuple[int, int]`
1441 |         return priv
     |
info: Function defined here
   --> botan3.py:648:5
    |
646 |     return result
647 |
648 | def _ctype_str(s: str | None) -> bytes | None:
    |     ^^^^^^^^^^ ------------- Parameter declared here
649 |     if s is None:
650 |         return None
    |
info: rule `invalid-argument-type` is enabled by default

Found 9 diagnostics
pyrefly
ERROR Object of class `_CFunctionType` has no attribute `output` [missing-attribute]
   --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:628:5
    |
628 |     _view_bin_fn.output = buf_val[0:buf_len]
    |     ^^^^^^^^^^^^^^^^^^^
    |
ERROR Object of class `_CFunctionType` has no attribute `output` [missing-attribute]
   --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:633:14
    |
633 |     result = _view_bin_fn.output
    |              ^^^^^^^^^^^^^^^^^^^
    |
ERROR Object of class `_CFunctionType` has no attribute `output` [missing-attribute]
   --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:634:5
    |
634 |     _view_bin_fn.output = None
    |     ^^^^^^^^^^^^^^^^^^^
    |
ERROR Object of class `_CFunctionType` has no attribute `output` [missing-attribute]
   --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:639:5
    |
639 |     _view_str_fn.output = str_val
    |     ^^^^^^^^^^^^^^^^^^^
    |
ERROR Object of class `_CFunctionType` has no attribute `output` [missing-attribute]
   --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:644:14
    |
644 |     result = _view_str_fn.output.decode('utf8')
    |              ^^^^^^^^^^^^^^^^^^^
    |
ERROR Object of class `_CFunctionType` has no attribute `output` [missing-attribute]
   --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:645:5
    |
645 |     _view_str_fn.output = None
    |     ^^^^^^^^^^^^^^^^^^^
    |
ERROR `RandomNumberGenerator` is not assignable to attribute `rng_` with type `None` [bad-assignment]
   --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:751:21
    |
751 |         self.rng_ = rng
    |                     ^^^
    |
ERROR Object of class `NoneType` has no attribute `handle_` [missing-attribute]
   --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:752:67
    |
752 |         _DLL.botan_tpm2_ctx_enable_crypto_backend(self.handle_(), self.rng_.handle_())
    |                                                                   ^^^^^^^^^^^^^^^^^
    |
ERROR `list[Unknown]` is not assignable to variable `args` with type `tuple[Unknown, ...]` [bad-assignment]
   --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:766:24
    |
766 |                 args = list(args[0])
    |                        ^^^^^^^^^^^^^
    |
ERROR `list[@_]` is not assignable to variable `args` with type `tuple[Unknown, ...]` [bad-assignment]
   --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:768:24
    |
768 |                 args = []
    |                        ^^
    |
ERROR Cannot index into `int` [index-error]
    --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:1437:33
     |
1437 |             params = "%d,%d" % (params[0], params[1])
     |                                 ^^^^^^^^^
     |
  Object of class `int` has no attribute `__getitem__`
ERROR Cannot index into `int` [index-error]
    --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:1437:44
     |
1437 |             params = "%d,%d" % (params[0], params[1])
     |                                            ^^^^^^^^^
     |
  Object of class `int` has no attribute `__getitem__`
ERROR Argument `int | str | tuple[int, int]` is not assignable to parameter `s` with type `str | None` in function `_ctype_str` [bad-argument-type]
    --> /home/arckoor/Scripts/C++/botan-wrapper/botan/botan3.py:1440:87
     |
1440 |         _DLL.botan_privkey_create(byref(priv.handle_()), _ctype_str(algo), _ctype_str(params), rng_obj.handle_())
     |                                                                                       ^^^^^^
     |

arckoor avatar Sep 05 '25 18:09 arckoor