Skip to content

Commit

Permalink
Merge branch 'main' into computed-fields
Browse files Browse the repository at this point in the history
  • Loading branch information
tlambert03 committed Nov 22, 2024
2 parents d5ef19f + 6db5219 commit ed0a7e0
Show file tree
Hide file tree
Showing 24 changed files with 97 additions and 110 deletions.
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
7 changes: 3 additions & 4 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 @@ -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 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
40 changes: 18 additions & 22 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 @@ -131,7 +127,7 @@ def _get_config(cls: pydantic.BaseModel) -> "ConfigDict":

def _get_fields(
cls: pydantic.BaseModel,
) -> Dict[str, pydantic.fields.FieldInfo]:
) -> dict[str, pydantic.fields.FieldInfo]:
comp_fields = {
name: pydantic.fields.FieldInfo(annotation=f.return_type, frozen=False)
for name, f in cls.model_computed_fields.items()
Expand All @@ -153,7 +149,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 @@ -175,11 +171,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 @@ -220,7 +216,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 @@ -314,7 +310,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 @@ -338,7 +334,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 @@ -472,17 +468,17 @@ 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)
_names_that_need_emission: Set[str] = PrivateAttr(default_factory=set)
_names_that_need_emission: set[str] = PrivateAttr(default_factory=set)

if PYDANTIC_V1:

Expand All @@ -508,7 +504,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
15 changes: 6 additions & 9 deletions src/psygnal/_queue.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
from __future__ import annotations

from collections import defaultdict
from queue import Queue
from threading import Thread, current_thread, main_thread
from typing import TYPE_CHECKING, Any, Callable, ClassVar, DefaultDict, Literal, Tuple

if TYPE_CHECKING:
import collections

from typing import Any, Callable, ClassVar, Literal

from ._exceptions import EmitLoopError
from ._weak_callback import WeakCallback

Callback = Callable[[Tuple[Any, ...]], Any]
CbArgsTuple = Tuple[Callback, tuple]
Callback = Callable[[tuple[Any, ...]], Any]
CbArgsTuple = tuple[Callback, tuple]


class QueuedCallback(WeakCallback):
Expand All @@ -29,8 +26,8 @@ class QueuedCallback(WeakCallback):
thread will be used.
"""

_GLOBAL_QUEUE: ClassVar[collections.defaultdict[Thread, Queue[CbArgsTuple]]] = (
DefaultDict(Queue)
_GLOBAL_QUEUE: ClassVar[defaultdict[Thread, Queue[CbArgsTuple]]] = defaultdict(
Queue
)

def __init__(
Expand Down
Loading

0 comments on commit ed0a7e0

Please sign in to comment.