oconnor663 / blake3-py

Python bindings for the BLAKE3 cryptographic hash function
Other
139 stars 12 forks source link

Add type stubs for blake3 library. #26

Open gwk opened 2 years ago

gwk commented 2 years ago

Below is a draft type stub for the blake3 module. I found this issue in the Maturin repo suggesting that adding type stubs to the project should be easy: https://github.com/PyO3/maturin/issues/618

One detail to double-check is that the py.typed file gets generated; this signals that the installed library also includes type stubs. See https://github.com/PyO3/maturin/blob/main/test-crates/pyo3-pure/check_installed/check_installed.py.

The type definitions below are derived from the hashlib definition in typeshed and the interactive help in python. I inlined the definition of ReadableBuffer.

Forgive me for throwing this over the wall instead of creating a proper PR. I am not a rust programmer and do not have anything set up. I am however happy to test and follow up on a library release that includes type stubs.

Thanks for creating the library - I'm curious to test blake3!

from array import array
from ctypes import _CData
from mmap import mmap
from typing import Any, Optional, TypeVar, Union

ReadableBuffer = Union[bytes, bytearray, memoryview, array[Any], mmap, _CData]
#^ Derived from typeshed/stdlib/typeshed/__init__.py.

_Self = TypeVar("_Self")

class blake3:

  def __init__(self, data:ReadableBuffer=..., key:Optional[ReadableBuffer]=None,
    derive_key_context:Optional[str]=None, multithreading:bool=False) -> None: ...

  @property
  def block_size(self) -> int: ...

  @property
  def digest_size(self) -> int: ...

  @property
  def key_size(self) -> int: ...

  @property
  def name(self) -> str: ...

  def copy(self: _Self) -> _Self: ...

  def digest(self) -> bytes: ...

  def hexdigest(self) -> str: ...

  def update(self, __data: ReadableBuffer, *, multithreading=False) -> None: ...
oconnor663 commented 2 years ago

I think this is a good idea, but I'm not very experienced with Python typechecking, so I'm probably not the best person to figure out how to test this and ship it. Is there any chance you can point to another project that uses Maturin and ships .pyi files?

LorenzoAncora commented 1 year ago

I've coded a ready-made version which is compatible with IDEs: https://gist.github.com/LorenzoAncora/24e19fd31591b2028d52eb496ba60587 🎁

chapinb commented 1 year ago

I think this is a good idea, but I'm not very experienced with Python typechecking, so I'm probably not the best person to figure out how to test this and ship it. Is there any chance you can point to another project that uses Maturin and ships .pyi files?

Here's another project that uses Maturin and has a .pyi for typing: https://github.com/strozfriedberg/notatin/blob/master/pynotatin/notatin.pyi

oconnor663 commented 1 year ago

@gwk and @LorenzoAncora: I added a pretty minimal blake3.pyi in 1393d4db6cfac71ac8fbcc4486aeaca13dec7682. I'd be curious to get your reviews, both for style and to see whether it's compatible with the IDEs you're using. It looks like Maturin does take care of putting __init__.pyi and py.typed in the right place, which is very nice.

gwk commented 1 year ago

@oconnor663 this is great to hear. I just looked through the changes but have not tested yet. You might consider using typing_extensions.Buffer to annotate data, which should fix all of those type ignores and PEP 688 comments. I recently did this for a zstandard wrapper library and it placated mypy 1.4. https://typing-extensions.readthedocs.io/en/latest/#Buffer

I have wrestled with Buffer typing in the past but this advice is far from authoritative. As you can see in my initial report above, the stdlib originally used a big union of types to represent ReadableBuffer. Recently however this no longer seems to be totally compatible with mypy's ever more precise analysis. If you don't want to depend on typing_extensions then perhaps we could try using the ReadableBuffer union. My guess is it will work for direct usage but when you try to pass the blake3 object around as an IO type then you will get "liskov principle" subclass typing errors.

Thanks for working on this!