Skip to content

Commit

Permalink
compat python 3.7
Browse files Browse the repository at this point in the history
  • Loading branch information
getzze committed Feb 9, 2024
1 parent 318a901 commit e072d02
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 17 deletions.
18 changes: 15 additions & 3 deletions src/psygnal/_dataclass_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import dataclasses
import sys
import types
from dataclasses import KW_ONLY, dataclass, fields
from dataclasses import dataclass, fields
from typing import (
TYPE_CHECKING,
Any,
Expand All @@ -19,6 +19,7 @@
from typing_extensions import Protocol

if TYPE_CHECKING:
from dataclasses import Field
import attrs
import msgspec
from pydantic import BaseModel
Expand Down Expand Up @@ -46,6 +47,9 @@ class AttrsType:
__attrs_attrs__: tuple[attrs.Attribute, ...]


KW_ONLY = object()
with contextlib.suppress(ImportError):
from dataclasses import KW_ONLY # type: ignore
_DATACLASS_PARAMS = "__dataclass_params__"
with contextlib.suppress(ImportError):
from dataclasses import _DATACLASS_PARAMS # type: ignore
Expand Down Expand Up @@ -235,15 +239,23 @@ def iter_fields(
class FieldOptions:
name: str
type_: type | None = None
_: KW_ONLY
_: KW_ONLY = KW_ONLY # set the value for compatibility with python < 3.10
alias: str | None = None
skip: bool | None = None
eq: EqOperator | None = None
disable_setattr: bool | None = None


def is_kw_only(f: Field):
if hasattr(f, "kw_only"):
return f.kw_only
# for python < 3.10
if f.name not in ["name", "type_"]:
return True
return False

def sanitize_field_options_dict(d: Mapping) -> dict[str, Any]:
field_options_kws = [f.name for f in fields(FieldOptions) if f.kw_only]
field_options_kws = [f.name for f in fields(FieldOptions) if is_kw_only(f)]
return {k: v for k, v in d.items() if k in field_options_kws}


Expand Down
20 changes: 8 additions & 12 deletions src/psygnal/_evented_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@
TYPE_CHECKING,
Any,
Callable,
Dict,
Mapping,
Optional,
Type,
TypeVar,
Union,
overload,
)

Expand All @@ -20,7 +16,7 @@

__all__ = ["evented"]

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

EqOperator = Callable[[Any, Any], bool]
PSYGNAL_GROUP_NAME = "_psygnal_group_"
Expand All @@ -32,7 +28,7 @@ def evented(
cls: T,
*,
events_namespace: str = "events",
equality_operators: Optional[Dict[str, EqOperator]] = None,
equality_operators: dict[str, EqOperator] | None = None,
warn_on_no_fields: bool = ...,
cache_on_instance: bool = ...,
skip_private_fields: bool = ...,
Expand All @@ -44,10 +40,10 @@ def evented(

@overload
def evented(
cls: "Optional[Literal[None]]" = None,
cls: Literal[None] | None = None,
*,
events_namespace: str = "events",
equality_operators: Optional[Dict[str, EqOperator]] = None,
equality_operators: dict[str, EqOperator] | None = None,
warn_on_no_fields: bool = ...,
cache_on_instance: bool = ...,
skip_private_fields: bool = ...,
Expand All @@ -58,16 +54,16 @@ def evented(


def evented(
cls: Optional[T] = None,
cls: T | None = None,
*,
events_namespace: str = "events",
equality_operators: Optional[Dict[str, EqOperator]] = None,
equality_operators: dict[str, EqOperator] | None = None,
warn_on_no_fields: bool = True,
cache_on_instance: bool = True,
skip_private_fields: bool = False,
signal_suffix: str = "",
signal_aliases: Mapping[str, str | None] | None = None,
) -> Union[Callable[[T], T], T]:
) -> Callable[[T], T] | T:
"""A decorator to add events to a dataclass.
See also the documentation for
Expand All @@ -88,7 +84,7 @@ def evented(
The class to decorate.
events_namespace : str
The name of the namespace to add the events to, by default `"events"`
equality_operators : Optional[Dict[str, Callable]]
equality_operators : dict[str, Callable] | None
A dictionary mapping field names to equality operators (a function that takes
two values and returns `True` if they are equal). These will be used to
determine if a field has changed when setting a new value. By default, this
Expand Down
3 changes: 2 additions & 1 deletion src/psygnal/_group_descriptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ def _build_dataclass_signal_group(
key = next((k for k, v in signal_aliases.items() if v == sig_name), None)
warnings.warn(

Check warning on line 219 in src/psygnal/_group_descriptor.py

View check run for this annotation

Codecov / codecov/patch

src/psygnal/_group_descriptor.py#L218-L219

Added lines #L218 - L219 were not covered by tests
f"Signal {sig_name} was already created in {group_name}, "
f"from field {key}"
f"from field {key}",
stacklevel=2,
)
continue

Check warning on line 224 in src/psygnal/_group_descriptor.py

View check run for this annotation

Codecov / codecov/patch

src/psygnal/_group_descriptor.py#L224

Added line #L224 was not covered by tests

Expand Down
11 changes: 10 additions & 1 deletion tests/test_custom_fields.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from typing import Annotated, ClassVar
# from __future__ import annotations

import contextlib
from typing import TYPE_CHECKING, ClassVar, Union
from unittest.mock import Mock

import pytest
Expand All @@ -11,6 +14,12 @@
SignalGroupDescriptor,
)

# from typing import Annotated # type: ignore
Annotated = Union
with contextlib.suppress(ImportError):
from typing import Annotated # type: ignore


dataclass_types = ["dataclass", "pydantic", "pydantic_v1", "attrs", "msgspec"]


Expand Down

0 comments on commit e072d02

Please sign in to comment.