Skip to content

Commit 8bd4498

Browse files
authored
Require Python 3.10 and support pytest 9 (#931)
1 parent fea9686 commit 8bd4498

10 files changed

Lines changed: 52 additions & 52 deletions

File tree

pyproject.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ classifiers = [
2020
"Programming Language :: Python",
2121
"Typing :: Typed",
2222
]
23-
requires-python = ">=3.9"
23+
requires-python = ">=3.10"
2424
dynamic = ["version"]
2525

2626
[project.urls]
@@ -31,7 +31,8 @@ Tracker = "https://github.com/ipython/traitlets/issues"
3131

3232
[project.optional-dependencies]
3333
test = [
34-
"argcomplete>=3.0.3",
34+
"argcomplete>=3.0.3; python_version < '3.12'",
35+
"argcomplete>=3.5.2; python_version >= '3.12'",
3536
# See https://github.com/python/mypy/issues/20329 for PyPy support issue.
3637
# Also, test assertions will need to be updated for 1.20+ because
3738
# `reveal_type()` representations were simplified in
@@ -40,7 +41,7 @@ test = [
4041
"pre-commit",
4142
"pytest-mock",
4243
"pytest-mypy-testing",
43-
"pytest>=7.0,<8.2",
44+
"pytest>=7.0,<10.0",
4445
]
4546
docs = [
4647
"myst-parser",
@@ -189,8 +190,7 @@ ignore = [
189190
"S105", "S106", # Possible hardcoded password
190191
"S110", # S110 `try`-`except`-`pass` detected
191192
"RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
192-
"UP006", # non-pep585-annotation
193-
"UP007", # non-pep604-annotation
193+
"UP038", # non-pep604-isinstance (removed in ruff 0.13.0)
194194
"ARG001", "ARG002", # Unused function argument
195195
"RET503", # Missing explicit `return` at the end of function
196196
"RET505", # Unnecessary `else` after `return` statement

tests/config/test_application.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class MyApp(Application):
6666
help="Should print a warning if `MyApp.warn-typo=...` command is passed",
6767
)
6868

69-
aliases: t.Dict[t.Any, t.Any] = {}
69+
aliases: dict[t.Any, t.Any] = {}
7070
aliases.update(Application.aliases)
7171
aliases.update(
7272
{
@@ -83,7 +83,7 @@ class MyApp(Application):
8383
}
8484
)
8585

86-
flags: t.Dict[t.Any, t.Any] = {}
86+
flags: dict[t.Any, t.Any] = {}
8787
flags.update(Application.flags)
8888
flags.update(
8989
{

tests/config/test_argcomplete.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
class ArgcompleteApp(Application):
2424
"""Override loader to pass through kwargs for argcomplete testing"""
2525

26-
argcomplete_kwargs: t.Dict[str, t.Any]
26+
argcomplete_kwargs: dict[str, t.Any]
2727

2828
def __init__(self, *args, **kwargs):
2929
# For subcommands, inherit argcomplete_kwargs from parent app
@@ -99,9 +99,9 @@ def run_completer(
9999
self,
100100
app: ArgcompleteApp,
101101
command: str,
102-
point: t.Union[str, int, None] = None,
102+
point: str | int | None = None,
103103
**kwargs: t.Any,
104-
) -> t.List[str]:
104+
) -> list[str]:
105105
"""Mostly borrowed from argcomplete's unit tests
106106
107107
Modified to take an application instead of an ArgumentParser

tests/test_traitlets.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171

7272
def change_dict(*ordered_values):
7373
change_names = ("name", "old", "new", "owner", "type")
74-
return dict(zip(change_names, ordered_values))
74+
return dict(zip(change_names, ordered_values, strict=True))
7575

7676

7777
# -----------------------------------------------------------------------------
@@ -1666,7 +1666,7 @@ class ListTrait(HasTraits):
16661666
class TestList(TraitTestBase):
16671667
obj = ListTrait()
16681668

1669-
_default_value: t.List[t.Any] = []
1669+
_default_value: list[t.Any] = []
16701670
_good_values = [[], [1], list(range(10)), (1, 2)]
16711671
_bad_values = [10, [1, "a"], "a"]
16721672

@@ -1683,7 +1683,7 @@ class SetTrait(HasTraits):
16831683
class TestSet(TraitTestBase):
16841684
obj = SetTrait()
16851685

1686-
_default_value: t.Set[str] = set()
1686+
_default_value: set[str] = set()
16871687
_good_values = [{"a", "b"}, "ab"]
16881688
_bad_values = [1]
16891689

@@ -1705,7 +1705,7 @@ class NoneInstanceListTrait(HasTraits):
17051705
class TestNoneInstanceList(TraitTestBase):
17061706
obj = NoneInstanceListTrait()
17071707

1708-
_default_value: t.List[t.Any] = []
1708+
_default_value: list[t.Any] = []
17091709
_good_values = [[Foo(), Foo()], []]
17101710
_bad_values = [[None], [Foo(), None]]
17111711

@@ -1721,7 +1721,7 @@ def test_klass(self):
17211721
"""Test that the instance klass is properly assigned."""
17221722
self.assertIs(self.obj.traits()["value"]._trait.klass, Foo)
17231723

1724-
_default_value: t.List[t.Any] = []
1724+
_default_value: list[t.Any] = []
17251725
_good_values = [[Foo(), Foo()], []]
17261726
_bad_values = [
17271727
[
@@ -1741,7 +1741,7 @@ class UnionListTrait(HasTraits):
17411741
class TestUnionListTrait(TraitTestBase):
17421742
obj = UnionListTrait()
17431743

1744-
_default_value: t.List[t.Any] = []
1744+
_default_value: list[t.Any] = []
17451745
_good_values = [[True, 1], [False, True]]
17461746
_bad_values = [[1, "True"], False]
17471747

@@ -1897,7 +1897,7 @@ class DictTrait(HasTraits):
18971897

18981898

18991899
def test_dict_assignment():
1900-
d: t.Dict[str, int] = {}
1900+
d: dict[str, int] = {}
19011901
c = DictTrait()
19021902
c.value = d
19031903
d["a"] = 5
@@ -2520,7 +2520,7 @@ def test_klass(self):
25202520
"""Test that the instance klass is properly assigned."""
25212521
self.assertIs(self.obj.traits()["value"]._trait.klass, ForwardDeclaredBar)
25222522

2523-
_default_value: t.List[t.Any] = []
2523+
_default_value: list[t.Any] = []
25242524
_good_values = [
25252525
[ForwardDeclaredBar(), ForwardDeclaredBarSub()],
25262526
[],
@@ -2543,7 +2543,7 @@ def test_klass(self):
25432543
"""Test that the instance klass is properly assigned."""
25442544
self.assertIs(self.obj.traits()["value"]._trait.klass, ForwardDeclaredBar)
25452545

2546-
_default_value: t.List[t.Any] = []
2546+
_default_value: list[t.Any] = []
25472547
_good_values = [
25482548
[ForwardDeclaredBar, ForwardDeclaredBarSub],
25492549
[],

tests/test_typing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ class T(HasTraits):
340340
reveal_type(
341341
T.ob # R: traitlets.traitlets.Bool[builtins.bool | None, builtins.bool | builtins.int | None]
342342
)
343-
# we would expect this to be Optional[bool | int], but...
343+
# we would expect this to be bool | int | None, but...
344344
t.b = "foo" # E: Incompatible types in assignment (expression has type "str", variable has type "bool | int") [assignment]
345345
t.b = None # E: Incompatible types in assignment (expression has type "None", variable has type "bool | int") [assignment]
346346

traitlets/config/application.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@
9898

9999
T = t.TypeVar("T", bound=t.Callable[..., t.Any])
100100
AnyLogger = t.Union[logging.Logger, "logging.LoggerAdapter[t.Any]"]
101-
StrDict = t.Dict[str, t.Any]
102-
ArgvType = t.Optional[t.List[str]]
103-
ClassesType = t.List[t.Type[Configurable]]
101+
StrDict = dict[str, t.Any]
102+
ArgvType = list[str] | None
103+
ClassesType = list[type[Configurable]]
104104

105105

106106
def catch_config_error(method: T) -> T:
@@ -520,7 +520,7 @@ def emit_alias_help(self) -> t.Generator[str, None, None]:
520520
for cls in self.classes:
521521
# include all parents (up to, but excluding Configurable) in available names
522522
for c in cls.mro()[:-3]:
523-
classdict[c.__name__] = t.cast(t.Type[Configurable], c)
523+
classdict[c.__name__] = t.cast(type[Configurable], c)
524524

525525
fhelp: str | None
526526
for alias, longname in self.aliases.items():
@@ -931,7 +931,7 @@ def _load_config_files(
931931
if log:
932932
log.debug("Loaded config file: %s", loader.full_filename)
933933
if config:
934-
for filename, earlier_config in zip(filenames, loaded):
934+
for filename, earlier_config in zip(filenames, loaded, strict=True):
935935
collisions = earlier_config.collisions(config)
936936
if collisions and log:
937937
log.warning(

traitlets/config/argcomplete_config.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def __getattr__(self, attr: str) -> t.Any:
2424
CompletionFinder = object # type:ignore[assignment, misc]
2525

2626

27-
def get_argcomplete_cwords() -> t.Optional[t.List[str]]:
27+
def get_argcomplete_cwords() -> list[str] | None:
2828
"""Get current words prior to completion point
2929
3030
This is normally done in the `argcomplete.CompletionFinder` constructor,
@@ -37,7 +37,7 @@ def get_argcomplete_cwords() -> t.Optional[t.List[str]]:
3737
comp_line = os.environ["COMP_LINE"]
3838
comp_point = int(os.environ["COMP_POINT"])
3939
# argcomplete.debug("splitting COMP_LINE for:", comp_line, comp_point)
40-
comp_words: t.List[str]
40+
comp_words: list[str]
4141
try:
4242
(
4343
cword_prequote,
@@ -98,10 +98,10 @@ class in `Application.classes` that could complete the current option.
9898
"""
9999

100100
_parser: argparse.ArgumentParser
101-
config_classes: t.List[t.Any] = [] # Configurables
102-
subcommands: t.List[str] = []
101+
config_classes: list[t.Any] = [] # Configurables
102+
subcommands: list[str] = []
103103

104-
def match_class_completions(self, cword_prefix: str) -> t.List[t.Tuple[t.Any, str]]:
104+
def match_class_completions(self, cword_prefix: str) -> list[tuple[t.Any, str]]:
105105
"""Match the word to be completed against our Configurable classes
106106
107107
Check if cword_prefix could potentially match against --{class}. for any class
@@ -146,9 +146,7 @@ def inject_class_to_parser(self, cls: t.Any) -> None:
146146
except AttributeError:
147147
pass
148148

149-
def _get_completions(
150-
self, comp_words: t.List[str], cword_prefix: str, *args: t.Any
151-
) -> t.List[str]:
149+
def _get_completions(self, comp_words: list[str], cword_prefix: str, *args: t.Any) -> list[str]:
152150
"""Overridden to dynamically append --Class.trait arguments if appropriate
153151
154152
Warning:
@@ -187,7 +185,7 @@ def _get_completions(
187185
self.inject_class_to_parser(matched_cls)
188186
break
189187

190-
completions: t.List[str]
188+
completions: list[str]
191189
completions = super()._get_completions(comp_words, cword_prefix, *args) # type:ignore[no-untyped-call]
192190

193191
# For subcommand-handling: it is difficult to get this to work
@@ -203,9 +201,9 @@ def _get_completions(
203201

204202
def _get_option_completions(
205203
self, parser: argparse.ArgumentParser, cword_prefix: str
206-
) -> t.List[str]:
204+
) -> list[str]:
207205
"""Overridden to add --Class. completions when appropriate"""
208-
completions: t.List[str]
206+
completions: list[str]
209207
completions = super()._get_option_completions(parser, cword_prefix) # type:ignore[no-untyped-call]
210208
if cword_prefix.endswith("."):
211209
return completions

traitlets/config/configurable.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
# -----------------------------------------------------------------------------
3333

3434
if t.TYPE_CHECKING:
35-
LoggerType = t.Union[logging.Logger, logging.LoggerAdapter[t.Any]]
35+
LoggerType = logging.Logger | logging.LoggerAdapter[t.Any]
3636
else:
3737
LoggerType = t.Any
3838

traitlets/config/loader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ def __repr__(self) -> str:
420420
return f"{self.__class__.__name__}({self._super_repr()})"
421421

422422

423-
class DeferredConfigList(t.List[t.Any], DeferredConfig):
423+
class DeferredConfigList(list[t.Any], DeferredConfig):
424424
"""Config value for loading config from a list of strings
425425
426426
Interpretation is deferred until it is loaded into the trait.
@@ -791,7 +791,7 @@ def parse_known_args( # type:ignore[override]
791791

792792

793793
# type aliases
794-
SubcommandsDict = t.Dict[str, t.Any]
794+
SubcommandsDict = dict[str, t.Any]
795795

796796

797797
class ArgParseConfigLoader(CommandLineConfigLoader):

0 commit comments

Comments
 (0)