Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: drop python 3.8 #341

Merged
merged 8 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
os: [ubuntu-latest, macos-latest, windows-latest]
compile: [true, false]
include:
Expand Down Expand Up @@ -65,14 +65,14 @@ jobs:
matrix:
python-version: ["3.10"]
os: [macos-latest, windows-latest]
qt: [PyQt5, PyQt6, PySide2, PySide6]
qt: [PyQt5, PyQt6, PySide6]
compile: [true, false]
exclude:
- os: macos-latest
qt: PySide2
include:
- os: macos-13
qt: PySide2
qt: "PySide2 'numpy<2'"
- os: windows-latest
python-version: "3.9"
qt: "PySide2 'numpy<2'"

upload_coverage:
if: always()
Expand Down
2 changes: 1 addition & 1 deletion asv.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"show_commit_url": "https://github.com/pyapp-kit/psygnal/commit/",
"pythons": ["3.11"],
"build_command": [
"python -m pip install build 'hatchling==1.21.1' hatch-vcs hatch-mypyc mypy pydantic types-attrs msgspec",
"python -m pip install build 'hatchling==1.21.1' hatch-vcs hatch-mypyc mypy pydantic!=2.10.0 types-attrs msgspec",
"python -c \"import os; from pathlib import Path; import hatchling.builders.wheel as h; p = Path(h.__file__); targ = os.environ.get('MACOSX_DEPLOYMENT_TARGET', '10_16').replace('.', '_'); txt = p.read_text().replace('10_16', targ); p.write_text(txt)\"",
"python -m build --wheel -o {build_cache_dir} {build_dir} --no-isolation"
],
Expand Down
15 changes: 7 additions & 8 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.8"
requires-python = ">=3.9"
license = { text = "BSD 3-Clause License" }
authors = [{ name = "Talley Lambert", email = "[email protected]" }]
classifiers = [
"Development Status :: 4 - Beta",
"License :: OSI Approved :: BSD License",
"Natural Language :: English",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand Down Expand Up @@ -95,7 +94,7 @@ dependencies = [
"hatch-mypyc>=0.13.0",
"mypy>=0.991",
"mypy_extensions >=0.4.2",
"pydantic",
"pydantic!=2.10.0",
"types-attrs",
"msgspec",
]
Expand All @@ -112,7 +111,7 @@ exclude = [
[tool.cibuildwheel]
# Skip 32-bit builds & PyPy wheels on all platforms
skip = ["*-manylinux_i686", "*-musllinux_i686", "*-win32", "pp*"]
build = ["cp38-*", "cp39-*", "cp310-*", "cp311-*", "cp312-*"]
build = ["cp39-*", "cp310-*", "cp311-*", "cp312-*", "cp313-*"]
test-extras = ["test"]
test-command = "pytest {project}/tests -v"
test-skip = ["*-musllinux*", "cp312-win*", "*-macosx_arm64"]
Expand All @@ -128,7 +127,7 @@ HATCH_BUILD_HOOKS_ENABLE = "1"
# https://docs.astral.sh/ruff/
[tool.ruff]
line-length = 88
target-version = "py37"
target-version = "py39"
src = ["src", "tests"]

[tool.ruff.lint]
Expand All @@ -150,7 +149,7 @@ select = [
"RUF", # ruff-specific rules
]
ignore = [
"D401", # First line should be in imperative mood
"D401", # First line should be in imperative mood
]

[tool.ruff.lint.per-file-ignores]
Expand All @@ -168,9 +167,9 @@ testpaths = ["tests"]
filterwarnings = [
"error",
"ignore:The distutils package is deprecated:DeprecationWarning:",
"ignore:.*BackendFinder.find_spec()", # pyinstaller import
"ignore:.*BackendFinder.find_spec()", # pyinstaller import
"ignore:.*not using a cooperative constructor:pytest.PytestDeprecationWarning:",
"ignore:Failed to disconnect::pytestqt"
"ignore:Failed to disconnect::pytestqt",
]

# https://mypy.readthedocs.io/en/stable/config_file.html
Expand Down
6 changes: 4 additions & 2 deletions src/psygnal/_dataclass_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import dataclasses
import sys
import types
from typing import TYPE_CHECKING, Any, Iterator, List, Protocol, cast, overload
from typing import TYPE_CHECKING, Any, Protocol, cast, overload

if TYPE_CHECKING:
from collections.abc import Iterator

import attrs
import msgspec
from pydantic import BaseModel
Expand All @@ -22,7 +24,7 @@ class _DataclassParams(Protocol):
frozen: bool


GenericAlias = getattr(types, "GenericAlias", type(List[int])) # safe for < py 3.9
GenericAlias = getattr(types, "GenericAlias", type(list[int])) # safe for < py 3.9


class AttrsType:
Expand Down
4 changes: 3 additions & 1 deletion src/psygnal/_evented_decorator.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Callable, Literal, Mapping, TypeVar, overload
from typing import TYPE_CHECKING, Callable, Literal, TypeVar, overload

from psygnal._group_descriptor import SignalGroupDescriptor

if TYPE_CHECKING:
from collections.abc import Mapping

from psygnal._group_descriptor import EqOperator, FieldAliasFunc

__all__ = ["evented"]
Expand Down
38 changes: 17 additions & 21 deletions src/psygnal/_evented_model.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import sys
import warnings
from collections.abc import Iterator, Mapping
from contextlib import contextmanager, suppress
from typing import (
TYPE_CHECKING,
Any,
Callable,
ClassVar,
Dict,
Iterator,
Mapping,
NamedTuple,
Set,
Type,
Union,
cast,
no_type_check,
Expand Down Expand Up @@ -111,8 +107,8 @@ def _return2(x: str, y: "Signature") -> "Signature":
if not PYDANTIC_V1:

def _get_defaults(
obj: Union[pydantic.BaseModel, Type[pydantic.BaseModel]],
) -> Dict[str, Any]:
obj: Union[pydantic.BaseModel, type[pydantic.BaseModel]],
) -> dict[str, Any]:
"""Get possibly nested default values for a Model object."""
dflt = {}
for k, v in obj.model_fields.items():
Expand All @@ -129,7 +125,7 @@ def _get_defaults(
def _get_config(cls: pydantic.BaseModel) -> "ConfigDict":
return cls.model_config

def _get_fields(cls: pydantic.BaseModel) -> Dict[str, pydantic.fields.FieldInfo]:
def _get_fields(cls: pydantic.BaseModel) -> dict[str, pydantic.fields.FieldInfo]:
return cls.model_fields

def _model_dump(obj: pydantic.BaseModel) -> dict:
Expand All @@ -147,7 +143,7 @@ def _is_pydantic_descriptor_proxy(obj: Any) -> "TypeGuard[PydanticDescriptorProx
else:

@no_type_check
def _get_defaults(obj: pydantic.BaseModel) -> Dict[str, Any]:
def _get_defaults(obj: pydantic.BaseModel) -> dict[str, Any]:
"""Get possibly nested default values for a Model object."""
dflt = {}
for k, v in obj.__fields__.items():
Expand All @@ -169,11 +165,11 @@ def _get_config(cls: type) -> "ConfigDict":
return GetAttrAsItem(cls.__config__)

class FieldInfo(NamedTuple):
annotation: Union[Type[Any], None]
annotation: Union[type[Any], None]
frozen: Union[bool, None]

@no_type_check
def _get_fields(cls: type) -> Dict[str, FieldInfo]:
def _get_fields(cls: type) -> dict[str, FieldInfo]:
return {
k: FieldInfo(annotation=f.type_, frozen=not f.field_info.allow_mutation)
for k, f in cls.__fields__.items()
Expand Down Expand Up @@ -214,7 +210,7 @@ class EventedMetaclass(pydantic_main.ModelMetaclass):
when each instance of an ``EventedModel`` is instantiated).
"""

__property_setters__: Dict[str, property]
__property_setters__: dict[str, property]

@no_type_check
def __new__(
Expand Down Expand Up @@ -308,7 +304,7 @@ def __new__(

def _get_field_dependents(
cls: "EventedMetaclass", model_config: dict, model_fields: dict
) -> Dict[str, Set[str]]:
) -> dict[str, set[str]]:
"""Return mapping of field name -> dependent set of property names.

Dependencies may be declared in the Model Config to emit an event
Expand All @@ -332,7 +328,7 @@ def c(self, val: Sequence[int]):
class Config:
field_dependencies={'c': ['a', 'b']}
"""
deps: Dict[str, Set[str]] = {}
deps: dict[str, set[str]] = {}

cfg_deps = model_config.get(FIELD_DEPENDENCIES, {}) # sourcery skip
if not cfg_deps:
Expand Down Expand Up @@ -464,15 +460,15 @@ class Config:
_events: ClassVar[SignalGroup] = PrivateAttr()

# mapping of name -> property obj for methods that are property setters
__property_setters__: ClassVar[Dict[str, property]]
__property_setters__: ClassVar[dict[str, property]]
# mapping of field name -> dependent set of property names
# when field is changed, an event for dependent properties will be emitted.
__field_dependents__: ClassVar[Dict[str, Set[str]]]
__eq_operators__: ClassVar[Dict[str, "EqOperator"]]
__field_dependents__: ClassVar[dict[str, set[str]]]
__eq_operators__: ClassVar[dict[str, "EqOperator"]]
__slots__ = {"__weakref__"}
__signal_group__: ClassVar[Type[SignalGroup]]
_changes_queue: Dict[str, Any] = PrivateAttr(default_factory=dict)
_primary_changes: Set[str] = PrivateAttr(default_factory=set)
__signal_group__: ClassVar[type[SignalGroup]]
_changes_queue: dict[str, Any] = PrivateAttr(default_factory=dict)
_primary_changes: set[str] = PrivateAttr(default_factory=set)
_delay_check_semaphore: int = PrivateAttr(0)

if PYDANTIC_V1:
Expand All @@ -496,7 +492,7 @@ def events(self) -> SignalGroup:
return self._events

@property
def _defaults(self) -> Dict[str, Any]:
def _defaults(self) -> dict[str, Any]:
return _get_defaults(self)

def __eq__(self, other: Any) -> bool:
Expand Down
4 changes: 3 additions & 1 deletion src/psygnal/_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import inspect
from contextlib import suppress
from pathlib import Path
from typing import TYPE_CHECKING, Any, Sequence
from typing import TYPE_CHECKING, Any

import psygnal

if TYPE_CHECKING:
from collections.abc import Sequence

from ._signal import SignalInstance


Expand Down
12 changes: 5 additions & 7 deletions src/psygnal/_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@
Any,
Callable,
ClassVar,
ContextManager,
Iterable,
Iterator,
Literal,
Mapping,
NamedTuple,
overload,
)
Expand All @@ -32,6 +28,8 @@

if TYPE_CHECKING:
import threading
from collections.abc import Iterable, Iterator, Mapping
from contextlib import AbstractContextManager

from psygnal._signal import F, ReducerFunc
from psygnal._weak_callback import RefErrorChoice, WeakCallback
Expand Down Expand Up @@ -175,7 +173,7 @@ def unblock(self) -> None:

def blocked(
self, exclude: Iterable[str | SignalInstance] = ()
) -> ContextManager[None]:
) -> AbstractContextManager[None]:
"""Context manager to temporarily block all emitters in this group.

Parameters
Expand Down Expand Up @@ -526,7 +524,7 @@ def unblock(self) -> None:

def blocked(
self, exclude: Iterable[str | SignalInstance] = ()
) -> ContextManager[None]:
) -> AbstractContextManager[None]:
return self._psygnal_relay.blocked(exclude=exclude)

def pause(self) -> None:
Expand All @@ -537,7 +535,7 @@ def resume(self, reducer: ReducerFunc | None = None, initial: Any = _NULL) -> No

def paused(
self, reducer: ReducerFunc | None = None, initial: Any = _NULL
) -> ContextManager[None]:
) -> AbstractContextManager[None]:
return self._psygnal_relay.paused(reducer=reducer, initial=initial)


Expand Down
6 changes: 2 additions & 4 deletions src/psygnal/_group_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,8 @@
Any,
Callable,
ClassVar,
Iterable,
Literal,
Mapping,
Optional,
Type,
TypeVar,
cast,
overload,
Expand All @@ -27,6 +24,7 @@

if TYPE_CHECKING:
from _weakref import ref as ref
from collections.abc import Iterable, Mapping

from typing_extensions import TypeAlias

Expand All @@ -38,7 +36,7 @@
__all__ = ["is_evented", "get_evented_namespace", "SignalGroupDescriptor"]


T = TypeVar("T", bound=Type)
T = TypeVar("T", bound=type)
S = TypeVar("S")


Expand Down
3 changes: 1 addition & 2 deletions src/psygnal/_pyinstaller_util/_pyinstaller_hook.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from pathlib import Path
from typing import List

CURRENT_DIR = Path(__file__).parent


def get_hook_dirs() -> List[str]:
def get_hook_dirs() -> list[str]:
return [str(CURRENT_DIR)]
7 changes: 4 additions & 3 deletions src/psygnal/_pyinstaller_util/hook-psygnal.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from collections.abc import Iterable
from importlib.metadata import PackageNotFoundError, PackagePath
from importlib.metadata import files as package_files
from pathlib import Path
from typing import Iterable, List, Union
from typing import Union

try:
import psygnal
Expand All @@ -11,11 +12,11 @@
PSYGNAL_DIR = Path(__file__).parent.parent


def binary_files(file_list: Iterable[Union[PackagePath, Path]]) -> List[Path]:
def binary_files(file_list: Iterable[Union[PackagePath, Path]]) -> list[Path]:
return [Path(file) for file in file_list if file.suffix in {".so", ".pyd"}]


def create_hiddenimports() -> List[str]:
def create_hiddenimports() -> list[str]:
res = ["queue", "mypy_extensions", "__future__"]

try:
Expand Down
Loading
Loading