Skip to content

Commit

Permalink
Support mypy typing (#127)
Browse files Browse the repository at this point in the history
* Support mypy typing

The big change here is to convert the library from a "single file module"
to a "packages" module. Here there is only only one package and we use an
__init__.py with `from .multiset import *` to continue to allow `from
multiset import Multiset` and similar. As crazy as it sounds I don't think
there's any way to signal to mypy with the py.typed file that the library
has type stubs without restructuring like this.

I also made some small changes to multiset.pyi to bring it in line with what
mypy wants.

A wheel package built with `python -m build` or `python -m build --wheel`
will now create a directory under site-packages containing multiset.py,
multiset.pyi, and py.typed, rather than just putting multiset.py into
site-packages as it did previously. If we publish this on pypi, clients of
the library will be able to use it typed without `type: ignore` comments
and will be able to type their Multisets like this and catch more bugs as
well as not getting told off by mypy:

```
from multiset import Multiset

m = Multiset[int | float] = Multiset([1, 2.2, 9, 33.8])
m1.add("hello")  # mypy says 'Argument 1 to "add" of "Multiset" has incompatible type "str"; expected "int | float"'
```

Cool!

* Update Makefiles with new location of code

---------

Co-authored-by: Manuel Krebber <admin@wheerd.de>
  • Loading branch information
bakert and wheerd committed Apr 5, 2024
1 parent ddd682b commit 73e3b4e
Show file tree
Hide file tree
Showing 8 changed files with 15 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -2,7 +2,7 @@ init:
pip install -r dev-requirements.txt

test:
py.test tests/ --doctest-modules multiset.py README.rst
py.test tests/ --doctest-modules multiset/multiset.py README.rst

check:
pylint multiset
Expand Down
2 changes: 2 additions & 0 deletions dev-requirements.txt
Expand Up @@ -4,3 +4,5 @@ coverage>=6.3,<8
setuptools_scm>=7.0,<9.0
tox>=2.5,<4.0
pytest-cov>=4.0,<5.0
mypy>=1.9,<2
build>=1.2,<2
2 changes: 1 addition & 1 deletion make.bat
Expand Up @@ -11,7 +11,7 @@ goto :eof
goto :eof

:test
py.test tests\ --doctest-modules multiset.py README.rst
py.test tests\ --doctest-modules multiset/multiset.py README.rst
goto :eof

:check
Expand Down
1 change: 1 addition & 0 deletions multiset/__init__.py
@@ -0,0 +1 @@
from .multiset import *
File renamed without changes.
9 changes: 9 additions & 0 deletions multiset.pyi → multiset/multiset.pyi
Expand Up @@ -2,6 +2,8 @@
from typing import (Generic, ItemsView, Iterable, Iterator, KeysView, Mapping, Hashable,
MutableMapping, Optional, Set, Type, TypeVar, Union, ValuesView, overload)

from _typeshed import SupportsKeysAndGetItem

T = TypeVar('T')
TElement = TypeVar('TElement', bound=Hashable)
OtherType = Union[Iterable[TElement], Mapping[TElement, int]]
Expand Down Expand Up @@ -84,7 +86,14 @@ class BaseMultiset(Mapping[TElement, int], Generic[TElement]):
class Multiset(BaseMultiset[TElement], MutableMapping[TElement, int], Generic[TElement]):
def __setitem__(self, element: TElement, multiplicity: int) -> None: ...
def __delitem__(self, element: TElement) -> None: ...
@overload
def update(self, *others: OtherType) -> None: ...
@overload
def update(self, __m: SupportsKeysAndGetItem[T, int], /, **kwargs: int) -> None: ...
@overload
def update(self, __m: Iterable[tuple[T, int]], /, **kwargs: int) -> None: ...
@overload
def update(self, **kwargs: int) -> None: ...
def union_update(self, *others: OtherType) -> None: ...
@overload
def __ior__(self: Self, other: Set[TElement]) -> Self: ...
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion setup.cfg
Expand Up @@ -29,7 +29,7 @@ setup_requires =
setuptools >= 46
setuptools_scm
python_requires = >= 3.8
py_modules = multiset
packages = multiset
test_suite = tests

[options.package_data]
Expand Down

0 comments on commit 73e3b4e

Please sign in to comment.