Skip to content

Commit

Permalink
Drop python 3.7 (#268)
Browse files Browse the repository at this point in the history
  • Loading branch information
tlambert03 authored Feb 12, 2024
1 parent d3d558d commit 46e354f
Show file tree
Hide file tree
Showing 12 changed files with 22 additions and 63 deletions.
7 changes: 2 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
os: [ubuntu-latest, macos-latest, windows-latest]
compile: [true, false]
include:
Expand All @@ -44,12 +44,9 @@ jobs:
python-version: "3.11"
pydantic: "'pydantic<2'"
exclude:
# still working on ci test errors
- os: windows-latest
python-version: "3.12"
- os: windows-latest
python-version: "3.7"
- os: macos-latest
python-version: "3.7"

test-qt:
name: Test Qt
Expand Down
19 changes: 4 additions & 15 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ build-backend = "hatchling.build"
name = "psygnal"
description = "Fast python callback/event system modeled after Qt Signals"
readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.8"
license = { text = "BSD 3-Clause License" }
authors = [{ name = "Talley Lambert", email = "talley.lambert@gmail.com" }]
classifiers = [
"Development Status :: 4 - Beta",
"License :: OSI Approved :: BSD License",
"Natural Language :: English",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
Expand All @@ -25,11 +24,7 @@ classifiers = [
"Typing :: Typed",
]
dynamic = ["version"]
dependencies = [
"typing-extensions >=3.7.4.2",
"mypy_extensions",
"importlib_metadata ; python_version < '3.8'",
]
dependencies = ["typing-extensions >=3.7.4.2", "mypy_extensions"]

# extras
# https://peps.python.org/pep-0621/#dependencies-optional-dependencies
Expand Down Expand Up @@ -122,7 +117,7 @@ exclude = [
[tool.cibuildwheel]
# Skip 32-bit builds & PyPy wheels on all platforms
skip = ["*-manylinux_i686", "*-musllinux_i686", "*-win32", "pp*"]
build = ["cp37-*", "cp38-*", "cp39-*", "cp310-*", "cp311-*"]
build = ["cp38-*", "cp39-*", "cp310-*", "cp311-*", "cp312-*"]
test-extras = ["test"]
test-command = "pytest {project}/tests -v"
test-skip = "*-musllinux*"
Expand Down Expand Up @@ -196,15 +191,9 @@ module = ["numpy.*", "wrapt", "pydantic.*"]
ignore_errors = true

[[tool.mypy.overrides]]
# msgspec is only available on Python 3.8+ ... so we need to ignore it
module = ["wrapt", "msgspec"]
module = ["wrapt"]
ignore_missing_imports = true

# remove when dropping python 3.7
[[tool.mypy.overrides]]
module = ["psygnal._signal"]
warn_unused_ignores = false

# https://coverage.readthedocs.io/en/6.4/config.html
[tool.coverage.report]
exclude_lines = [
Expand Down
12 changes: 1 addition & 11 deletions src/psygnal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,12 @@
"""

import os
from importlib.metadata import PackageNotFoundError, version
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
PackageNotFoundError = Exception
from ._evented_model_v1 import EventedModel # noqa: TCH004

def version(package: str) -> str:
"""Return version."""

else:
# hiding this import from type checkers so mypyc can work on both 3.7 and later
try:
from importlib.metadata import PackageNotFoundError, version
except ImportError:
from importlib_metadata import PackageNotFoundError, version


try:
__version__ = version("psygnal")
Expand Down
6 changes: 2 additions & 4 deletions src/psygnal/_dataclass_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,7 @@ def iter_fields(
"""
# generally opting for speed here over public API

dclass_fields = getattr(cls, "__dataclass_fields__", None)
if dclass_fields is not None:
if (dclass_fields := getattr(cls, "__dataclass_fields__", None)) is not None:
for d_field in dclass_fields.values():
if d_field._field_type is dataclasses._FIELD: # type: ignore [attr-defined]
yield d_field.name, d_field.type
Expand All @@ -186,8 +185,7 @@ def iter_fields(
yield p_field.name, p_field.outer_type_ # type: ignore
return

attrs_fields = getattr(cls, "__attrs_attrs__", None)
if attrs_fields is not None:
if (attrs_fields := getattr(cls, "__attrs_attrs__", None)) is not None:
for a_field in attrs_fields:
yield a_field.name, a_field.type
return
Expand Down
3 changes: 1 addition & 2 deletions src/psygnal/_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,7 @@ def is_uniform(cls) -> bool:
return cls._uniform

def _slot_relay(self, *args: Any) -> None:
emitter = Signal.current_emitter()
if emitter:
if emitter := Signal.current_emitter():
info = EmissionInfo(emitter, args)
self._run_emit_loop((info,))

Expand Down
7 changes: 2 additions & 5 deletions src/psygnal/_group_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,11 +440,8 @@ def __get__(
setattr(instance, self._name, self._instance_map[obj_id])

# clean up the cache when the instance is deleted
with contextlib.suppress(TypeError):
# on 3.7 this is type error, above it's not... but mypy yells about
# type ignore on 3.8+, so we do this funny business instead.
args = (instance, self._instance_map.pop, obj_id, None)
weakref.finalize(*args) # type: ignore
with contextlib.suppress(TypeError): # if it's not weakref-able
weakref.finalize(instance, self._instance_map.pop, obj_id, None)

return self._instance_map[obj_id]

Expand Down
7 changes: 2 additions & 5 deletions src/psygnal/_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,9 +493,7 @@ def _wrapper(
extra = f"- Slot types {slot_sig} do not match types in signal."
self._raise_connection_error(slot, extra)

# this type ignore is only needed to build mypyc on pythong 3.7
# can be removed when we drop support for 3.7
cb = weak_callback( # type: ignore
cb = weak_callback(
slot,
max_args=max_args,
finalize=self._try_discard,
Expand Down Expand Up @@ -774,9 +772,8 @@ def _check_nargs(
) from e
raise

n_spec_params = len(spec.parameters)
# if `slot` requires more arguments than we will provide, raise.
if minargs > n_spec_params:
if minargs > (n_spec_params := len(spec.parameters)):
extra = (
f"- Slot requires at least {minargs} positional "
f"arguments, but spec only provides {n_spec_params}"
Expand Down
6 changes: 4 additions & 2 deletions src/psygnal/_weak_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,12 @@ def object_repr(obj: Any) -> str:
return f"{module}.{obj.__qualname__}"
elif getattr(type(obj), "__qualname__", ""):
return f"{module}.{type(obj).__qualname__}"
return repr(obj)
# this line was hit in py3.7, but not afterwards.
# retained as a fallback, but not covered by tests.
return repr(obj) # pragma: no cover

def __repr__(self) -> str:
return f"<{self.__class__.__name__} on {self._object_repr}>"
return f"<{self.__class__.__name__} on {self._object_repr}>" # pragma: no cover


def _kill_and_finalize(
Expand Down
6 changes: 2 additions & 4 deletions src/psygnal/containers/_evented_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ def events(self) -> ProxyEvents: # pragma: no cover # unclear why
def __setattr__(self, name: str, value: None) -> None:
before = getattr(self, name, _UNSET)
super().__setattr__(name, value)
after = getattr(self, name, _UNSET)
if before is not after:
if before is not (after := getattr(self, name, _UNSET)):
self.events.attribute_set(name, after)

def __delattr__(self, name: str) -> None:
Expand All @@ -93,8 +92,7 @@ def __delattr__(self, name: str) -> None:
def __setitem__(self, key: Any, value: Any) -> None:
before = self[key]
super().__setitem__(key, value)
after = self[key]
if before is not after:
if before is not (after := self[key]):
self.events.item_set(key, after)

def __delitem__(self, key: Any) -> None:
Expand Down
3 changes: 1 addition & 2 deletions src/psygnal/qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,5 @@ def stop_emitting_from_queue(thread: Thread | None = None) -> None:
in the thread from which this function is called.
"""
_thread = current_thread() if thread is None else thread
timer = _TIMERS.get(_thread)
if timer is not None:
if (timer := _TIMERS.get(_thread)) is not None:
timer.stop()
1 change: 0 additions & 1 deletion tests/test_evented_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ class User(EventedModel):
name_mock.assert_not_called()


@pytest.mark.skipif(sys.version_info < (3, 8), reason="requires python3.8 or higher")
def test_evented_model_array_updates():
"""Test updating an evented pydantic model with an array."""

Expand Down
8 changes: 1 addition & 7 deletions tests/test_psygnal.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import gc
import sys
import time
from contextlib import suppress
from functools import partial, wraps
Expand Down Expand Up @@ -846,12 +845,7 @@ def test_emit_loop_exceptions():
"f_any_assigned",
"partial",
"partial_kwargs",
pytest.param(
"partial",
marks=pytest.mark.xfail(
sys.version_info < (3, 8), reason="no idea why this fails on 3.7"
),
),
"partial",
],
)
def test_weakref_disconnect(slot):
Expand Down

0 comments on commit 46e354f

Please sign in to comment.