Skip to content

Commit dacbf9b

Browse files
authored
Improve generics handling (#886)
1 parent 4f0e68b commit dacbf9b

File tree

4 files changed

+174
-98
lines changed

4 files changed

+174
-98
lines changed

tests/test_typing.py

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

3+
import logging
34
import typing as t
5+
from abc import ABC
46

57
import pytest
68

@@ -9,6 +11,7 @@
911
Bool,
1012
CInt,
1113
Dict,
14+
Enum,
1215
HasTraits,
1316
Instance,
1417
Int,
@@ -97,12 +100,14 @@ class T(HasTraits):
97100
).tag(config=True)
98101

99102
t = T()
100-
reveal_type(List("foo")) # R: traitlets.traitlets.List
101-
reveal_type(List("").tag(sync=True)) # R: traitlets.traitlets.List
102-
reveal_type(List(None, allow_none=True)) # R: traitlets.traitlets.List
103-
reveal_type(List(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.List
104-
reveal_type(T.latex_command) # R: traitlets.traitlets.List
105-
reveal_type(t.latex_command) # R: builtins.list[Any]
103+
reveal_type(List(["foo"])) # R: traitlets.traitlets.List[builtins.str]
104+
reveal_type(List([""]).tag(sync=True)) # R: traitlets.traitlets.List[builtins.str]
105+
reveal_type(List(None, allow_none=True)) # R: traitlets.traitlets.List[<nothing>]
106+
reveal_type(
107+
List(None, allow_none=True).tag(sync=True) # R: traitlets.traitlets.List[<nothing>]
108+
)
109+
reveal_type(T.latex_command) # R: traitlets.traitlets.List[builtins.str]
110+
reveal_type(t.latex_command) # R: builtins.list[builtins.str]
106111

107112

108113
@pytest.mark.mypy_testing
@@ -111,19 +116,25 @@ class T(HasTraits):
111116
foo = Dict({}, help="Shell command used to compile latex.").tag(config=True)
112117

113118
t = T()
114-
reveal_type(Dict("foo")) # R: traitlets.traitlets.Dict
115-
reveal_type(Dict("").tag(sync=True)) # R: traitlets.traitlets.Dict
116-
reveal_type(Dict(None, allow_none=True)) # R: traitlets.traitlets.Dict
117-
reveal_type(Dict(None, allow_none=True).tag(sync=True)) # R: traitlets.traitlets.Dict
118-
reveal_type(T.foo) # R: traitlets.traitlets.Dict
119-
reveal_type(t.foo) # R: builtins.dict[Any, Any]
119+
reveal_type(Dict(None, allow_none=True)) # R: traitlets.traitlets.Dict[builtins.str, Any]
120+
reveal_type(
121+
Dict(None, allow_none=True).tag(sync=True) # R: traitlets.traitlets.Dict[builtins.str, Any]
122+
)
123+
reveal_type(T.foo) # R: traitlets.traitlets.Dict[builtins.str, Any]
124+
reveal_type(t.foo) # R: builtins.dict[builtins.str, Any]
120125

121126

122127
@pytest.mark.mypy_testing
123128
def mypy_type_typing() -> None:
124129
class KernelSpec:
125130
item = Unicode("foo")
126131

132+
class KernelSpecSubclass(KernelSpec):
133+
other = Unicode("bar")
134+
135+
class GatewayTokenRenewerBase(ABC):
136+
item = Unicode("foo")
137+
127138
class KernelSpecManager(HasTraits):
128139
"""A manager for kernel specs."""
129140

@@ -136,12 +147,30 @@ class KernelSpecManager(HasTraits):
136147
)
137148
other_class = Type("foo.bar.baz")
138149

150+
other_kernel_spec_class = Type(
151+
default_value=KernelSpecSubclass,
152+
klass=KernelSpec,
153+
config=True,
154+
)
155+
156+
gateway_token_renewer_class = Type(
157+
klass=GatewayTokenRenewerBase,
158+
config=True,
159+
help="""The class to use for Gateway token renewal. (JUPYTER_GATEWAY_TOKEN_RENEWER_CLASS env var)""",
160+
)
161+
139162
t = KernelSpecManager()
140-
reveal_type(t.kernel_spec_class) # R: def () -> tests.test_typing.KernelSpec@124
141-
reveal_type(t.kernel_spec_class()) # R: tests.test_typing.KernelSpec@124
163+
reveal_type(t.kernel_spec_class) # R: def () -> tests.test_typing.KernelSpec@129
164+
reveal_type(t.kernel_spec_class()) # R: tests.test_typing.KernelSpec@129
142165
reveal_type(t.kernel_spec_class().item) # R: builtins.str
143166
reveal_type(t.other_class) # R: builtins.type
144167
reveal_type(t.other_class()) # R: Any
168+
reveal_type(t.other_kernel_spec_class) # R: def () -> tests.test_typing.KernelSpec@129
169+
reveal_type(t.other_kernel_spec_class()) # R: tests.test_typing.KernelSpec@129
170+
reveal_type(
171+
t.gateway_token_renewer_class # R: def () -> tests.test_typing.GatewayTokenRenewerBase@135
172+
)
173+
reveal_type(t.gateway_token_renewer_class()) # R: tests.test_typing.GatewayTokenRenewerBase@135
145174

146175

147176
@pytest.mark.mypy_testing
@@ -181,6 +210,42 @@ class T(HasTraits):
181210
reveal_type(t.export_format) # R: builtins.str
182211

183212

213+
@pytest.mark.mypy_testing
214+
def mypy_enum_typing() -> None:
215+
class T(HasTraits):
216+
log_level = Enum(
217+
(0, 10, 20, 30, 40, 50),
218+
default_value=logging.WARN,
219+
help="Set the log level by value or name.",
220+
).tag(config=True)
221+
222+
t = T()
223+
reveal_type(
224+
Enum( # R: traitlets.traitlets.Enum[builtins.str]
225+
("foo",)
226+
)
227+
)
228+
reveal_type(
229+
Enum( # R: traitlets.traitlets.Enum[builtins.str]
230+
[""]
231+
).tag(sync=True)
232+
)
233+
reveal_type(
234+
Enum( # R: traitlets.traitlets.Enum[None]
235+
None, allow_none=True
236+
)
237+
)
238+
reveal_type(
239+
Enum( # R: traitlets.traitlets.Enum[None]
240+
None, allow_none=True
241+
).tag(sync=True)
242+
)
243+
reveal_type(
244+
T.log_level # R: traitlets.traitlets.Enum[builtins.int]
245+
)
246+
reveal_type(t.log_level) # R: builtins.int
247+
248+
184249
@pytest.mark.mypy_testing
185250
def mypy_set_typing() -> None:
186251
class T(HasTraits):

traitlets/config/application.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def _classes_inc_parents(
199199
version: str | Unicode[str, str | bytes] = Unicode("0.0")
200200

201201
# the argv used to initialize the application
202-
argv = List()
202+
argv: list[str] | List[str] = List()
203203

204204
# Whether failing to load config files should prevent startup
205205
raise_config_file_errors = Bool(TRAITLETS_APPLICATION_RAISE_CONFIG_FILE_ERROR)
@@ -241,7 +241,7 @@ def get_default_logging_config(self) -> StrDict:
241241
"console": {
242242
"class": "logging.StreamHandler",
243243
"formatter": "console",
244-
"level": logging.getLevelName(self.log_level),
244+
"level": logging.getLevelName(self.log_level), # type:ignore[arg-type]
245245
"stream": "ext://sys.stderr",
246246
},
247247
},
@@ -278,7 +278,7 @@ def _observe_logging_change(self, change: Bunch) -> None:
278278
# convert log level strings to ints
279279
log_level = self.log_level
280280
if isinstance(log_level, str):
281-
self.log_level = getattr(logging, log_level)
281+
self.log_level = t.cast(int, getattr(logging, log_level))
282282
self._configure_logging()
283283

284284
@observe("log", type="default")
@@ -400,7 +400,7 @@ def _log_default(self) -> AnyLogger:
400400
# this must be a dict of two-tuples,
401401
# the first element being the application class/import string
402402
# and the second being the help string for the subcommand
403-
subcommands: dict[str, t.Any] | Dict = Dict()
403+
subcommands: dict[str, t.Any] | Dict[str, t.Any] = Dict()
404404
# parse_command_line will initialize a subapp, if requested
405405
subapp = Instance("traitlets.config.application.Application", allow_none=True)
406406

@@ -418,7 +418,7 @@ def _log_default(self) -> AnyLogger:
418418
""",
419419
)
420420

421-
_loaded_config_files = List()
421+
_loaded_config_files: List[str] = List()
422422

423423
show_config = Bool(
424424
help="Instead of starting the Application, dump configuration to stdout"

traitlets/config/loader.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ class LazyConfigValue(HasTraits):
9898
_value = None
9999

100100
# list methods
101-
_extend: List = List()
102-
_prepend: List = List()
103-
_inserts: List = List()
101+
_extend: List[t.Any] = List()
102+
_prepend: List[t.Any] = List()
103+
_inserts: List[t.Any] = List()
104104

105105
def append(self, obj: t.Any) -> None:
106106
"""Append an item to a List"""

0 commit comments

Comments
 (0)