Skip to content

Commit

Permalink
Merge pull request #175 from ariebovenberg/release-0.17
Browse files Browse the repository at this point in the history
drop Python 3.7 support, remove its dependencies
  • Loading branch information
ariebovenberg authored Jul 31, 2023
2 parents 62d0477 + 6fd30e2 commit 705cbf4
Show file tree
Hide file tree
Showing 12 changed files with 188 additions and 264 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.8", "3.9", "3.10", "3.11"]

steps:
- uses: actions/checkout@v1
Expand All @@ -25,7 +25,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install 'tox<5' tox-gh-actions 'poetry>=1.4,<1.5'
pip install 'tox<5' tox-gh-actions 'poetry>=1.5,<1.6'
- name: Test with tox
run: tox

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

0.17.0 (2023-07-31)
-------------------

- Drop Python 3.7 support.
- Remove ``typing-extensions`` and ``importlib_metadata`` dependencies.
- More robust checks to prevent ``Protocol`` false positives.

0.16.5 (2023-03-01)
-------------------

Expand Down
2 changes: 1 addition & 1 deletion docs/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Use the following configuration:
repos:
- repo: https://github.com/ariebovenberg/slotscheck
rev: v0.16.5
rev: v0.17.0
hooks:
- id: slotscheck
# If your Python files are not importable from the project root,
Expand Down
320 changes: 124 additions & 196 deletions poetry.lock

Large diffs are not rendered by default.

9 changes: 3 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
[tool.poetry]
name = "slotscheck"
version = "0.16.5"
version = "0.17.0"
description = "Ensure your __slots__ are working properly."
authors = ["Arie Bovenberg <[email protected]>"]
license = "MIT"
classifiers = [
"Typing :: Typed",
"Topic :: Software Development :: Quality Assurance",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
Expand All @@ -23,14 +22,12 @@ repository = "https://github.com/ariebovenberg/slotscheck"
homepage = "https://github.com/ariebovenberg/slotscheck"

[tool.poetry.dependencies]
python = ">=3.7,<4"
importlib-metadata = {version = ">=1,<6", python = "<3.8"}
typing-extensions = {version = ">=4.1,<5", python = "<3.10"}
python = ">=3.8.1,<4"
click = "^8.0"
tomli = {version = ">=0.2.6,<3.0.0", python = "<3.11"}

[tool.poetry.dev-dependencies]
flake8 = "^5.0.4"
flake8 = "^6.1"
isort = "^5.11.5"
mypy = "^1.4"
pytest = "^7.4.0"
Expand Down
5 changes: 1 addition & 4 deletions src/slotscheck/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# Single-sourcing the version number with poetry:
# https://github.com/python-poetry/poetry/pull/2366#issuecomment-652418094
try:
__version__ = __import__("importlib.metadata").metadata.version(__name__)
except ModuleNotFoundError: # pragma: no cover
__version__ = __import__("importlib_metadata").version(__name__)
__version__ = __import__("importlib.metadata").metadata.version(__name__)
18 changes: 2 additions & 16 deletions src/slotscheck/checks.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
"Slots-related checks and inspection tools"
import platform
import sys
from typing import Callable, Collection, Iterator, Optional
from typing import Collection, Iterator, Optional

from .common import either

is_typeddict: Callable[[type], bool]

try:
from typing import is_typeddict

try:
from typing_extensions import is_typeddict as _is_typing_ext_typeddict
except ImportError: # pragma: no cover
pass
else:
is_typeddict = either(is_typeddict, _is_typing_ext_typeddict)
except ImportError: # pragma: no cover
from typing_extensions import is_typeddict
from .common import is_typeddict


def slots(c: type) -> Optional[Collection[str]]:
Expand Down
8 changes: 2 additions & 6 deletions src/slotscheck/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
compose,
flatten,
groupby,
is_protocol,
map_optional,
)
from .discovery import (
Expand All @@ -51,11 +52,6 @@
walk_classes,
)

try:
from typing import Protocol
except ImportError: # pragma: no cover
from typing_extensions import Protocol # type: ignore[assignment]


@click.command("slotscheck")
@click.argument(
Expand Down Expand Up @@ -569,7 +565,7 @@ def slot_messages(
require_subclass
and not has_slots(c)
and not has_slotless_base(c)
and Protocol not in c.__bases__
and not is_protocol(c)
):
yield ShouldHaveSlots(c)

Expand Down
46 changes: 29 additions & 17 deletions src/slotscheck/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
Iterator,
Mapping,
Optional,
Protocol,
Set,
Tuple,
TypeVar,
overload,
)

flatten = chain.from_iterable
Expand Down Expand Up @@ -70,22 +70,6 @@ def both(__a: Predicate[_T1], __b: Predicate[_T1]) -> Predicate[_T1]:
return lambda x: __a(x) and __b(x)


@overload
def either(
__a: Callable[[_T1], bool], __b: Callable[[_T1], bool]
) -> Callable[[_T1], bool]:
...


@overload
def either(__a: Predicate[_T1], __b: Predicate[_T1]) -> Predicate[_T1]:
...


def either(__a: Any, __b: Any) -> Any:
return lambda x: __a(x) or __b(x)


def map_optional(
f: Callable[[_T1], Optional[_T2]], it: Iterable[_T1]
) -> Iterator[_T2]:
Expand All @@ -96,6 +80,34 @@ def _is_none(x: object) -> bool:
return x is None


try:
from typing_extensions import is_typeddict # noqa
except ImportError: # pragma: no cover
from typing import _TypedDictMeta # type: ignore

def is_typeddict(tp: object) -> bool:
return isinstance(tp, _TypedDictMeta)


# Note that typing.is_protocol is not available yet (CPython PR 104878)
# The implementation below is derived from it.
def is_protocol(t: type) -> bool: # pragma: no cover
return getattr(t, "_is_protocol", False) and t != Protocol


try:
from typing_extensions import Protocol as _TypingExtProtocol
except ImportError: # pragma: no cover
pass
else:

def is_protocol(t: type) -> bool: # noqa: F811
return getattr(t, "_is_protocol", False) and t not in (
Protocol,
_TypingExtProtocol,
)


# From https://github.com/ericvsmith/dataclasses/blob/master/dataclass_tools.py
# License: https://github.com/ericvsmith/dataclasses/blob/master/LICENSE.txt
# Changed only `dataclass.fields` naming
Expand Down
10 changes: 6 additions & 4 deletions tests/examples/module_not_ok/foo.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@

try:
from typing import Protocol
except ImportError:
from typing_extensions import Protocol
from typing import Protocol
from typing_extensions import Protocol as TypingExtProtocol


class A:
Expand Down Expand Up @@ -111,5 +109,9 @@ class MyProto(Protocol):
pass


class MyOtherProto(TypingExtProtocol):
pass


class Zb(MyProto):
__slots__ = ()
20 changes: 10 additions & 10 deletions tests/src/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def test_errors_with_default_settings(runner: CliRunner):
ERROR: 'module_not_ok.foo:Za' defines overlapping slots.
ERROR: 'module_not_ok.foo:Zb' has slots but superclass does not.
Oh no, found some problems!
Scanned 4 module(s), 30 class(es).
Scanned 4 module(s), 31 class(es).
"""
)

Expand All @@ -234,7 +234,7 @@ def test_errors_require_slots_subclass(runner: CliRunner):
ERROR: 'module_not_ok.foo:Za' defines overlapping slots.
ERROR: 'module_not_ok.foo:Zb' has slots but superclass does not.
Oh no, found some problems!
Scanned 4 module(s), 30 class(es).
Scanned 4 module(s), 31 class(es).
"""
)

Expand All @@ -259,7 +259,7 @@ def test_errors_disallow_nonslot_inherit(runner: CliRunner):
ERROR: 'module_not_ok.foo:Za' defines overlapping slots.
ERROR: 'module_not_ok.foo:Zb' has slots but superclass does not.
Oh no, found some problems!
Scanned 4 module(s), 30 class(es).
Scanned 4 module(s), 31 class(es).
"""
)

Expand All @@ -279,7 +279,7 @@ def test_errors_no_require_superclass(runner: CliRunner):
ERROR: 'module_not_ok.foo:Z' has duplicate slots.
ERROR: 'module_not_ok.foo:Za' defines overlapping slots.
Oh no, found some problems!
Scanned 4 module(s), 30 class(es).
Scanned 4 module(s), 31 class(es).
"""
)

Expand All @@ -302,7 +302,7 @@ def test_errors_with_exclude_classes(runner: CliRunner):
ERROR: 'module_not_ok.foo:Za' defines overlapping slots.
ERROR: 'module_not_ok.foo:Zb' has slots but superclass does not.
Oh no, found some problems!
Scanned 4 module(s), 30 class(es).
Scanned 4 module(s), 31 class(es).
"""
)

Expand All @@ -322,7 +322,7 @@ def test_errors_with_include_classes(runner: CliRunner):
ERROR: 'module_not_ok.foo:W' defines overlapping slots.
ERROR: 'module_not_ok.foo:Za' defines overlapping slots.
Oh no, found some problems!
Scanned 4 module(s), 30 class(es).
Scanned 4 module(s), 31 class(es).
"""
)

Expand Down Expand Up @@ -417,9 +417,9 @@ def test_module_not_ok_verbose(runner: CliRunner):
excluded: 0
skipped: 0
classes: 30
classes: 31
has slots: 22
no slots: 8
no slots: 9
n/a: 0
"""
)
Expand Down Expand Up @@ -519,7 +519,7 @@ def test_finds_config(runner: CliRunner, mocker, tmpdir):
ERROR: 'module_not_ok.foo:Z' has duplicate slots.
ERROR: 'module_not_ok.foo:Za' defines overlapping slots.
Oh no, found some problems!
Scanned 4 module(s), 30 class(es).
Scanned 4 module(s), 31 class(es).
"""
)

Expand Down Expand Up @@ -548,7 +548,7 @@ def test_given_config(runner: CliRunner, tmpdir):
ERROR: 'module_not_ok.foo:Z' has duplicate slots.
ERROR: 'module_not_ok.foo:Za' defines overlapping slots.
Oh no, found some problems!
Scanned 4 module(s), 30 class(es).
Scanned 4 module(s), 31 class(es).
"""
)

Expand Down
3 changes: 1 addition & 2 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tox]
isolated_build = true
envlist = py{37,38,39,310,311},lint,mypy,isort,slots
envlist = py{38,39,310,311},lint,mypy,isort,slots

[testenv]
allowlist_externals =
Expand Down Expand Up @@ -55,7 +55,6 @@ exclude_lines=

[gh-actions]
python =
3.7: py37
3.8: py38
3.9: py39
3.10: py310, mypy, lint, isort, docs, slots
Expand Down

0 comments on commit 705cbf4

Please sign in to comment.