diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3b135e7..11ddd0b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ Changelog ========= +0.16.1 (2022-11-21) +------------------- + +- Don't flag ``TypedDict`` subclasses as missing slots (#120). + 0.16.0 (2022-11-01) ------------------- diff --git a/docs/advanced.rst b/docs/advanced.rst index a57cbf4..1e71279 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -18,7 +18,7 @@ Use the following configuration: repos: - repo: https://github.com/ariebovenberg/slotscheck - rev: v0.16.0 + rev: v0.16.1 hooks: - id: slotscheck # If your Python files are not importable from the project root, diff --git a/poetry.lock b/poetry.lock index 585beac..2874ca9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -79,7 +79,7 @@ python-versions = "*" [[package]] name = "exceptiongroup" -version = "1.0.0" +version = "1.0.4" description = "Backport of PEP 654 (exception groups)" category = "dev" optional = false @@ -201,7 +201,7 @@ pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" [[package]] name = "pathspec" -version = "0.10.1" +version = "0.10.2" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false @@ -209,15 +209,15 @@ python-versions = ">=3.7" [[package]] name = "platformdirs" -version = "2.5.2" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "2.5.4" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] +docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"] +test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" @@ -430,7 +430,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = ">=3.7,<4" -content-hash = "d81c6b1e16c500b3cdd36162d97f148a69ac8550cab27b31d93d1fafeac1a98c" +content-hash = "4b09dcca187898ae0f4f9400ec55db3474c1b4f08261239b527cf13169aaee33" [metadata.files] attrs = [ @@ -525,8 +525,8 @@ distlib = [ {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, ] exceptiongroup = [ - {file = "exceptiongroup-1.0.0-py3-none-any.whl", hash = "sha256:2ac84b496be68464a2da60da518af3785fff8b7ec0d090a581604bc870bdee41"}, - {file = "exceptiongroup-1.0.0.tar.gz", hash = "sha256:affbabf13fb6e98988c38d9c5650e701569fe3c1de3233cfb61c5f33774690ad"}, + {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, + {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, ] filelock = [ {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, @@ -593,12 +593,12 @@ packaging = [ {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] pathspec = [ - {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, - {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, + {file = "pathspec-0.10.2-py3-none-any.whl", hash = "sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5"}, + {file = "pathspec-0.10.2.tar.gz", hash = "sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0"}, ] platformdirs = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, + {file = "platformdirs-2.5.4-py3-none-any.whl", hash = "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10"}, + {file = "platformdirs-2.5.4.tar.gz", hash = "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, diff --git a/pyproject.toml b/pyproject.toml index 6d484f6..c9ab348 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "slotscheck" -version = "0.16.0" +version = "0.16.1" description = "Ensure your __slots__ are working properly." authors = ["Arie Bovenberg "] license = "MIT" @@ -25,6 +25,7 @@ homepage = "https://github.com/ariebovenberg/slotscheck" [tool.poetry.dependencies] python = ">=3.7,<4" importlib-metadata = {version = ">=1,<5", python = "<3.8"} +typing-extensions = {version = ">=4.1,<5", python = "<3.10"} dataclasses = {version = ">=0.6", python = "<3.7"} click = "^8.0" tomli = ">=0.2.6,<3.0.0" diff --git a/src/slotscheck/checks.py b/src/slotscheck/checks.py index 84e1ab7..ace6a04 100644 --- a/src/slotscheck/checks.py +++ b/src/slotscheck/checks.py @@ -3,6 +3,11 @@ import sys from typing import Collection, Iterator, Optional +try: + from typing import is_typeddict +except ImportError: # pragma: no cover + from typing_extensions import is_typeddict + def slots(c: type) -> Optional[Collection[str]]: try: @@ -18,8 +23,10 @@ def slots(c: type) -> Optional[Collection[str]]: def has_slots(c: type) -> bool: - return "__slots__" in c.__dict__ or not ( - issubclass(c, BaseException) or is_pure_python(c) + return ( + "__slots__" in c.__dict__ + or not (issubclass(c, BaseException) or is_pure_python(c)) + or is_typeddict(c) ) diff --git a/tests/src/test_checks.py b/tests/src/test_checks.py index cad3773..98fa52c 100644 --- a/tests/src/test_checks.py +++ b/tests/src/test_checks.py @@ -10,6 +10,11 @@ from slotscheck.checks import has_slotless_base, has_slots, slots_overlap +try: + from typing import TypedDict +except ImportError: + from typing_extensions import TypedDict + class HasSlots: __slots__ = ("a", "b") @@ -64,6 +69,10 @@ class Foo(metaclass=FooMeta): __slots__ = () +class MyDict(TypedDict): + foo: str + + class TestHasSlots: @pytest.mark.parametrize( "klass", @@ -72,6 +81,9 @@ class TestHasSlots: def test_not_purepython(self, klass): assert has_slots(klass) + def test_typeddict(self): + assert has_slots(MyDict) + @pytest.mark.parametrize( "klass", [