Skip to content

Commit f8c283e

Browse files
author
Alan
committed
Make Set, Tuple, Dict, and UseEnum generic.
1 parent e3d5c5a commit f8c283e

File tree

2 files changed

+24
-30
lines changed

2 files changed

+24
-30
lines changed

tests/test_typing.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -249,16 +249,13 @@ class T(HasTraits):
249249
@pytest.mark.mypy_testing
250250
def mypy_set_typing() -> None:
251251
class T(HasTraits):
252-
remove_cell_tags = Set(
252+
remove_cell_tags: Set[str] = Set(
253253
Unicode(),
254254
default_value=[],
255-
help=(
256-
"Tags indicating which cells are to be removed,"
257-
"matches tags in ``cell.metadata.tags``."
258-
),
255+
help=("Tags indicating which cells are to be removed,matches tags in ``cell.metadata.tags``."),
259256
).tag(config=True)
260257

261-
safe_output_keys = Set(
258+
safe_output_keys: Set[t.Any] = Set(
262259
config=True,
263260
default_value={
264261
"metadata", # Not a mimetype per-se, but expected and safe.
@@ -272,13 +269,13 @@ class T(HasTraits):
272269
)
273270

274271
t = T()
275-
reveal_type(Set("foo")) # R: traitlets.traitlets.Set
276-
reveal_type(Set("").tag(sync=True)) # R: traitlets.traitlets.Set
277-
reveal_type(Set(None, allow_none=True)) # R: traitlets.traitlets.Set
278-
reveal_type(Set(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.Set
279-
reveal_type(T.remove_cell_tags) # R: traitlets.traitlets.Set
280-
reveal_type(t.remove_cell_tags) # R: builtins.set[Any]
281-
reveal_type(T.safe_output_keys) # R: traitlets.traitlets.Set
272+
reveal_type(Set({"foo"})) # R: traitlets.traitlets.Set[builtins.str]
273+
reveal_type(Set({""}).tag(sync=True)) # R: traitlets.traitlets.Set[builtins.str]
274+
reveal_type(Set(None, allow_none=True)) # R: traitlets.traitlets.Set[Never]
275+
reveal_type(Set(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.Set[Never]
276+
reveal_type(T.remove_cell_tags) # R: traitlets.traitlets.Set[builtins.str]
277+
reveal_type(t.remove_cell_tags) # R: builtins.set[builtins.str]
278+
reveal_type(T.safe_output_keys) # R: traitlets.traitlets.Set[Any]
282279
reveal_type(t.safe_output_keys) # R: builtins.set[Any]
283280

284281

traitlets/traitlets.py

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -495,16 +495,15 @@ def instance_init(self, obj: t.Any) -> None:
495495
G = TypeVar("G")
496496
S = TypeVar("S")
497497
T = TypeVar("T")
498-
498+
K = TypeVar("K", default=str)
499+
V = TypeVar("V", default=t.Any)
499500

500501
# Self from typing extension doesn't work well with mypy https://github.com/python/mypy/pull/14041
501502
# see https://peps.python.org/pep-0673/#use-in-generic-classes
502503
# Self = t.TypeVar("Self", bound="TraitType[Any, Any]")
503504
if t.TYPE_CHECKING:
504505
from typing_extensions import Literal, Self
505506

506-
K = TypeVar("K", default=str)
507-
V = TypeVar("V", default=t.Any)
508507

509508

510509
# We use a type for the getter (G) and setter (G) because we allow
@@ -519,7 +518,7 @@ class TraitType(BaseDescriptor, t.Generic[G, S]):
519518
default_value: t.Any = Undefined
520519

521520
def __init__(
522-
self: TraitType[G, S],
521+
self,
523522
default_value: t.Any = Undefined,
524523
allow_none: bool = False,
525524
read_only: bool | None = None,
@@ -3580,7 +3579,7 @@ def item_from_string(self, s: str, index: int | None = None) -> T | str:
35803579
return s
35813580

35823581

3583-
class List(Container[t.List[T]]):
3582+
class List(Container[t.List[T]], t.Generic[T]):
35843583
"""An instance of a Python list."""
35853584

35863585
klass = list # type:ignore[assignment]
@@ -3645,7 +3644,7 @@ def set(self, obj: t.Any, value: t.Any) -> None:
36453644
return super().set(obj, value)
36463645

36473646

3648-
class Set(Container[t.Set[t.Any]]):
3647+
class Set(Container[t.Set[T]], t.Generic[T]):
36493648
"""An instance of a Python set."""
36503649

36513650
klass = set
@@ -3656,8 +3655,8 @@ class Set(Container[t.Set[t.Any]]):
36563655
# Redefine __init__ just to make the docstring more accurate.
36573656
def __init__(
36583657
self,
3659-
trait: t.Any = None,
3660-
default_value: t.Any = Undefined,
3658+
trait: TraitType[T, t.Any] | t.Iterable[T] | None = None,
3659+
default_value: set[T] | t.Any = Undefined,
36613660
minlen: int = 0,
36623661
maxlen: int = sys.maxsize,
36633662
**kwargs: t.Any,
@@ -3720,13 +3719,13 @@ def default_value_repr(self) -> str:
37203719
return "{" + list_repr[1:-1] + "}"
37213720

37223721

3723-
class Tuple(Container[t.Tuple[t.Any, ...]]):
3722+
class Tuple(Container[t.Tuple[T]], t.Generic[T]):
37243723
"""An instance of a Python tuple."""
37253724

37263725
klass = tuple
37273726
_cast_types = (list,)
37283727

3729-
def __init__(self, *traits: t.Any, **kwargs: t.Any) -> None:
3728+
def __init__(self, *traits: T, **kwargs: t.Any) -> None:
37303729
"""Create a tuple from a list, set, or tuple.
37313730
37323731
Create a fixed-type tuple with Traits:
@@ -3849,7 +3848,7 @@ def subclass_init(self, cls: type[t.Any]) -> None:
38493848
# to opt out of instance_init
38503849

38513850

3852-
class Dict(Instance["dict[K, V]"]):
3851+
class Dict(Instance["dict[K, V]"], t.Generic[K, V]):
38533852
"""An instance of a Python dict.
38543853
38553854
One or more traits can be passed to the constructor
@@ -3869,9 +3868,9 @@ class Dict(Instance["dict[K, V]"]):
38693868

38703869
def __init__(
38713870
self,
3872-
value_trait: TraitType[t.Any, t.Any] | dict[K, V] | Sentinel | None = None,
3871+
value_trait: TraitType[V, t.Any] | dict[K, V] | Sentinel | None = None,
38733872
per_key_traits: t.Any = None,
3874-
key_trait: TraitType[t.Any, t.Any] | None = None,
3873+
key_trait: TraitType[K, t.Any] | None = None,
38753874
default_value: dict[K, V] | Sentinel | None = Undefined,
38763875
**kwargs: t.Any,
38773876
) -> None:
@@ -4203,7 +4202,7 @@ def validate(self, obj: t.Any, value: t.Any) -> re.Pattern[t.Any] | None:
42034202
self.error(obj, value)
42044203

42054204

4206-
class UseEnum(TraitType[t.Any, t.Any]):
4205+
class UseEnum(TraitType[G, S], t.Generic[G, S]):
42074206
"""Use a Enum class as model for the data type description.
42084207
Note that if no default-value is provided, the first enum-value is used
42094208
as default-value.
@@ -4236,9 +4235,7 @@ class MyEntity(HasTraits):
42364235
default_value: enum.Enum | None = None
42374236
info_text = "Trait type adapter to a Enum class"
42384237

4239-
def __init__(
4240-
self, enum_class: type[t.Any], default_value: t.Any = None, **kwargs: t.Any
4241-
) -> None:
4238+
def __init__(self, enum_class: type[G], default_value: t.Any = None, **kwargs: t.Any) -> None:
42424239
assert issubclass(enum_class, enum.Enum), "REQUIRE: enum.Enum, but was: %r" % enum_class
42434240
allow_none = kwargs.get("allow_none", False)
42444241
if default_value is None and not allow_none:

0 commit comments

Comments
 (0)