Improve Generic list and set typing

This commit is contained in:
Timmy Welch 2024-05-19 13:41:50 -07:00
parent 58daa4b274
commit 73552e0dd2
3 changed files with 241 additions and 129 deletions

View File

@ -32,6 +32,7 @@ repos:
hooks:
- id: pyupgrade
args: [--py38-plus]
exclude: tests
- repo: https://github.com/hhatto/autopep8
rev: v2.1.0
hooks:

View File

@ -13,11 +13,14 @@ import warnings
from argparse import Namespace
from collections import defaultdict
from collections.abc import Sequence
from collections.abc import Set
from typing import Any
from typing import Callable
from typing import cast
from typing import Collection
from typing import Dict
from typing import Generic
from typing import get_args
from typing import NoReturn
from typing import TYPE_CHECKING
from typing import TypeVar
@ -25,6 +28,8 @@ from typing import Union
logger = logging.getLogger(__name__)
NoneType = type(None)
if sys.version_info < (3, 11): # pragma: no cover
from typing_extensions import NamedTuple
else: # pragma: no cover
@ -43,6 +48,8 @@ if sys.version_info < (3, 9): # pragma: no cover
def get_typing_type(t: type) -> type:
if t.__module__ == 'builtins':
if t is NoneType:
return None
return getattr(typing, t.__name__.title(), t)
return t
@ -91,8 +98,8 @@ else: # pragma: no cover
from argparse import BooleanOptionalAction
removeprefix = str.removeprefix
def get_typing_type(t: type) -> type:
return t
def get_typing_type(t: type) -> type | None:
return None if t is NoneType else t
def _isnamedtupleinstance(x: Any) -> bool: # pragma: no cover
@ -216,47 +223,115 @@ class Setting:
return NotImplemented
return self.__dict__ == other.__dict__
def _guess_type(self) -> tuple[type | str | None, bool]:
if isinstance(self.type, type):
return self.type, self.default is None
__no_type = object()
__action_to_type = {
'store_true': (bool, False),
'store_false': (bool, False),
BooleanOptionalAction: (bool, self.default is None),
'store_const': (type(self.const), self.default is None),
'count': (int, self.default is None),
'append': (List[str], self.default is None),
'extend': (List[str], self.default is None),
'append_const': (List[type(self.const)], self.default is None), # type: ignore[misc]
'help': (None, self.default is None),
'version': (None, self.default is None),
}
def _guess_collection(self) -> tuple[type | str | None, bool]:
def get_item_type(x: Any) -> type | None:
if x is None or not isinstance(x, (Set, Sequence)) or len(x) == 0:
t = self._process_type() # Specifically this is needed when using the extend action
if typing.get_args(t): # We need the item type not the type of the collection
return typing.get_args(t)[0] # type: ignore[no-any-return]
if self.action in __action_to_type:
return __action_to_type[self.action]
# Return None so that we get the default
return None if t is None else self.__no_type # type: ignore[return-value]
if isinstance(x, Set):
return type(next(iter(x)))
return type(x[0])
if self.type is not None:
type_hints = typing.get_type_hints(self.type)
if 'return' in type_hints:
t: type | str = type_hints['return']
return t, self.default is None
try:
list_type = self._process_type()
# if the type is a generic alias than return it immediately
if isinstance(list_type, types_GenericAlias) and issubclass(list_type.__origin__, Collection):
return list_type, self.default is None
if self.default is not None:
if not isinstance(self.default, str) and not _isnamedtupleinstance(self.default) and isinstance(self.default, Sequence) and self.default and self.default[0]:
# Ensure that generic aliases work for python 3.8
if list_type is not None:
list_type = get_typing_type(list_type)
else:
list_type = get_typing_type(type(self.default))
# Default to a list if we don't know what type of collection this is
if list_type is None or not issubclass(list_type, Collection):
list_type = List
# Get the item type (int) in list[int]
it = get_item_type(self.default)
if isinstance(self.type, type):
it = self.type
if it is self.__no_type:
return self._process_type() or List[str], self.default is None
# Try to get the generic alias for this type
if it is not None:
try:
t = get_typing_type(type(self.default))
ret = cast(type, t[type(self.default[0])]), self.default is None # type: ignore[index]
ret = cast(type, list_type[it]), self.default is None # type: ignore[index]
return ret
except Exception:
...
return type(self.default), self.default is None
if self.cmdline and self.action is None and self.type is None:
if self.nargs in ('+', '*') or isinstance(self.nargs, int) and self.nargs > 1:
return List[str], self.default is None
return str, self.default is None
return 'Any', self.default is None
# Fall back to list[str] if anything fails
return list_type[str], self.default is None # type: ignore[index]
except Exception:
return None, self.default is None
def _process_type(self) -> type | None:
if self.type is None:
return None
if isinstance(self.type, type):
return self.type
return typing.get_type_hints(self.type).get('return', None) # type: ignore[no-any-return]
def _guess_type_internal(self) -> tuple[type | str | None, bool]:
default_is_none = self.default is None
__action_to_type = {
'store_true': (bool, False),
'store_false': (bool, False),
BooleanOptionalAction: (bool, default_is_none),
'store_const': (type(self.const), default_is_none),
'count': (int, default_is_none),
'extend': self._guess_collection(),
'append_const': (List[type(self.const)], default_is_none), # type: ignore[misc]
'help': (None, default_is_none),
'version': (None, default_is_none),
}
# Process standard actions
if self.action in __action_to_type:
return __action_to_type[self.action]
# nargs > 1 is always a list
if self.nargs in ('+', '*') or isinstance(self.nargs, int) and self.nargs > 1:
return self._guess_collection()
# Process the type argument
type_type = self._process_type()
if type_type is not None:
return type_type, default_is_none
# Check if a default value was given.
if self.default is not None:
if not isinstance(self.default, str) and not _isnamedtupleinstance(self.default) and isinstance(self.default, (Set, Sequence)):
return self._guess_collection()
# The type argument will convert this if it is a string. We only get here if type is a function without type hints
if not (isinstance(self.default, str) and self.type is not None):
return type(self.default), default_is_none
# There is no way to detemine the type from an action
if callable(self.action):
return 'Any', default_is_none
# Finally if this is a commandline argument it will default to a string
if self.cmdline and self.type is None:
return str, default_is_none
# For file only settings it will default to Any
return 'Any', default_is_none
def _guess_type(self) -> tuple[type | str | None, bool]:
if self.action == 'append':
return List[self._guess_type_internal()[0]], self.default is None # type: ignore[misc]
return self._guess_type_internal()
def get_dest(self, prefix: str, names: Sequence[str], dest: str | None) -> tuple[str, str, str, bool]:
setting_name = None
@ -316,6 +391,32 @@ if TYPE_CHECKING:
ns = Namespace | TypedNS | Config[T] | None
def _type_to_string(t: type | str) -> tuple[str, str]:
type_name = 'Any'
import_needed = ''
# Take a string as is
if isinstance(t, str):
type_name = t
# Handle generic aliases eg dict[str, str] instead of dict
elif isinstance(t, types_GenericAlias):
if not get_args(t):
t = t.__origin__.__name__
type_name = str(t)
# Handle standard type objects
elif isinstance(t, type):
type_name = t.__name__
# Builtin types don't need an import
if t.__module__ != 'builtins':
import_needed = f'import {t.__module__}'
# Use the full imported name
type_name = t.__module__ + '.' + type_name
# Expand Any to typing.Any
if type_name == 'Any':
type_name = 'typing.Any'
return type_name, import_needed
def generate_ns(definitions: Definitions) -> tuple[str, str]:
initial_imports = ['from __future__ import annotations', '', 'import settngs']
imports: Sequence[str] | set[str]
@ -328,27 +429,8 @@ def generate_ns(definitions: Definitions) -> tuple[str, str]:
t, noneable = setting._guess_type()
if t is None:
continue
# Default to any
type_name = 'Any'
# Take a string as is
if isinstance(t, str):
type_name = t
# Handle generic aliases eg dict[str, str] instead of dict
elif isinstance(t, types_GenericAlias):
type_name = str(t)
# Handle standard type objects
elif isinstance(t, type):
type_name = t.__name__
# Builtin types don't need an import
if t.__module__ != 'builtins':
imports.add(f'import {t.__module__}')
# Use the full imported name
type_name = t.__module__ + '.' + type_name
# Expand Any to typing.Any
if type_name == 'Any':
type_name = 'typing.Any'
type_name, import_needed = _type_to_string(t)
imports.add(import_needed)
if noneable and type_name not in ('typing.Any', 'None'):
attribute = f' {setting.internal_name}: {type_name} | None'
@ -372,7 +454,7 @@ def generate_ns(definitions: Definitions) -> tuple[str, str]:
initial_imports.append('import typing')
# Remove the possible duplicate typing import
imports = sorted(list(imports - {'import typing'}))
imports = sorted(imports - {'import typing', ''})
# Merge the imports the ns class definition and the attributes
return '\n'.join(initial_imports + imports), ns + '\n'.join(attributes)
@ -392,27 +474,8 @@ def generate_dict(definitions: Definitions) -> tuple[str, str]:
t, no_default = setting._guess_type()
if t is None:
continue
# Default to any
type_name = 'Any'
# Take a string as is
if isinstance(t, str):
type_name = t
# Handle generic aliases eg dict[str, str] instead of dict
elif isinstance(t, types_GenericAlias):
type_name = str(t)
# Handle standard type objects
elif isinstance(t, type):
type_name = t.__name__
# Builtin types don't need an import
if t.__module__ != 'builtins':
imports.add(f'import {t.__module__}')
# Use the full imported name
type_name = t.__module__ + '.' + type_name
# Expand Any to typing.Any
if type_name == 'Any':
type_name = 'typing.Any'
type_name, import_needed = _type_to_string(t)
imports.add(import_needed)
if no_default and type_name not in ('typing.Any', 'None'):
attribute = f' {setting.dest}: {type_name} | None'
@ -429,7 +492,7 @@ def generate_dict(definitions: Definitions) -> tuple[str, str]:
)
# Remove the possible duplicate typing import
imports = sorted(list(imports - {'import typing'}))
imports = sorted(list(imports - {'import typing', ''}))
if groups_are_identifiers:
ns = '\nclass SettngsDict(typing.TypedDict):\n'

View File

@ -19,6 +19,7 @@ from testing.settngs import success
if sys.version_info >= (3, 10): # pragma: no cover
List = list
Set = set
help_output = '''\
usage: __main__.py [-h] [TEST ...]
@ -30,6 +31,7 @@ options:
'''
elif sys.version_info < (3, 9): # pragma: no cover
from typing import List
from typing import Set
help_output = '''\
usage: __main__.py [-h] [TEST [TEST ...]]
@ -42,6 +44,7 @@ optional arguments:
else: # pragma: no cover
List = list
Set = set
help_output = '''\
usage: __main__.py [-h] [TEST ...]
@ -618,6 +621,18 @@ def _typed_function(something: str) -> test_type: # pragma: no cover
return test_type()
def _typed_list_generic_function(something: test_type) -> List[test_type]: # pragma: no cover
return [test_type()]
def _typed_list_function() -> List: # type: ignore[type-arg] # pragma: no cover
return []
def _typed_set_function() -> Set: # type: ignore[type-arg] # pragma: no cover
return set()
def _untyped_function(something):
...
@ -652,23 +667,34 @@ types = (
(0, settngs.Setting('-t', '--test'), str, True),
(1, settngs.Setting('-t', '--test', cmdline=False), 'Any', True),
(2, settngs.Setting('-t', '--test', default=1, file=True, cmdline=False), int, False),
(3, settngs.Setting('-t', '--test', action='count'), int, True),
(4, settngs.Setting('-t', '--test', action='append'), List[str], True),
(5, settngs.Setting('-t', '--test', action='extend'), List[str], True),
(6, settngs.Setting('-t', '--test', nargs='+'), List[str], True),
(7, settngs.Setting('-t', '--test', action='store_const', const=1), int, True),
(8, settngs.Setting('-t', '--test', action='append_const', const=1), List[int], True),
(9, settngs.Setting('-t', '--test', action='store_true'), bool, False),
(10, settngs.Setting('-t', '--test', action='store_false'), bool, False),
(11, settngs.Setting('-t', '--test', action=settngs.BooleanOptionalAction), bool, True),
(12, settngs.Setting('-t', '--test', action=_customAction), 'Any', True),
(13, settngs.Setting('-t', '--test', action='help'), None, True),
(14, settngs.Setting('-t', '--test', action='version'), None, True),
(15, settngs.Setting('-t', '--test', type=int), int, True),
(16, settngs.Setting('-t', '--test', type=_typed_function), test_type, True),
(17, settngs.Setting('-t', '--test', type=_untyped_function, default=1), int, False),
(18, settngs.Setting('-t', '--test', type=_untyped_function, default=[1]), List[int], False),
(19, settngs.Setting('-t', '--test', type=_untyped_function), 'Any', True),
(3, settngs.Setting('-t', '--test', default='test'), str, False),
(4, settngs.Setting('-t', '--test', default='test', file=True, cmdline=False), str, False),
(5, settngs.Setting('-t', '--test', action='count'), int, True),
(6, settngs.Setting('-t', '--test', action='append'), List[str], True),
(7, settngs.Setting('-t', '--test', action='extend'), List[str], True),
(8, settngs.Setting('-t', '--test', nargs='+'), List[str], True),
(9, settngs.Setting('-t', '--test', action='store_const', const=1), int, True),
(10, settngs.Setting('-t', '--test', action='append_const', const=1), List[int], True),
(11, settngs.Setting('-t', '--test', action='store_true'), bool, False),
(12, settngs.Setting('-t', '--test', action='store_false'), bool, False),
(13, settngs.Setting('-t', '--test', action=settngs.BooleanOptionalAction), bool, True),
(14, settngs.Setting('-t', '--test', action=_customAction), 'Any', True),
(15, settngs.Setting('-t', '--test', action='help'), None, True),
(16, settngs.Setting('-t', '--test', action='version'), None, True),
(17, settngs.Setting('-t', '--test', type=int), int, True),
(18, settngs.Setting('-t', '--test', type=int, nargs='+'), List[int], True),
(19, settngs.Setting('-t', '--test', type=_typed_function), test_type, True),
(20, settngs.Setting('-t', '--test', type=_untyped_function, default=1), int, False),
(21, settngs.Setting('-t', '--test', type=_untyped_function, default=[1]), List[int], False),
(22, settngs.Setting('-t', '--test', type=_untyped_function), 'Any', True),
(23, settngs.Setting('-t', '--test', type=_untyped_function, default={1}), Set[int], False),
(24, settngs.Setting('-t', '--test', action='append', type=int), List[int], True),
(25, settngs.Setting('-t', '--test', action='extend', type=int, nargs=2), List[int], True),
(26, settngs.Setting('-t', '--test', action='append', type=int, nargs=2), List[List[int]], True),
(27, settngs.Setting('-t', '--test', action='extend', nargs='+'), List[str], True),
(28, settngs.Setting('-t', '--test', action='extend', type=_typed_list_generic_function), List[test_type], True),
(29, settngs.Setting('-t', '--test', action='extend', type=_typed_list_function), List, True),
(30, settngs.Setting('-t', '--test', action='extend', type=_typed_set_function), Set, True),
)
@ -700,23 +726,34 @@ settings = (
(0, lambda parser: parser.add_setting('-t', '--test'), expected_src.format(extra_imports='', typ='str | None')),
(1, lambda parser: parser.add_setting('-t', '--test', cmdline=False), expected_src.format(extra_imports='import typing\n', typ='typing.Any')),
(2, lambda parser: parser.add_setting('-t', '--test', default=1, file=True, cmdline=False), expected_src.format(extra_imports='', typ='int')),
(3, lambda parser: parser.add_setting('-t', '--test', action='count'), expected_src.format(extra_imports='', typ='int | None')),
(4, lambda parser: parser.add_setting('-t', '--test', action='append'), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ='typing.List[str] | None' if sys.version_info < (3, 9) else 'list[str] | None')),
(5, lambda parser: parser.add_setting('-t', '--test', action='extend'), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ='typing.List[str] | None' if sys.version_info < (3, 9) else 'list[str] | None')),
(6, lambda parser: parser.add_setting('-t', '--test', nargs='+'), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ='typing.List[str] | None' if sys.version_info < (3, 9) else 'list[str] | None')),
(7, lambda parser: parser.add_setting('-t', '--test', action='store_const', const=1), expected_src.format(extra_imports='', typ='int | None')),
(8, lambda parser: parser.add_setting('-t', '--test', action='append_const', const=1), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ='typing.List[int] | None' if sys.version_info < (3, 9) else 'list[int] | None')),
(9, lambda parser: parser.add_setting('-t', '--test', action='store_true'), expected_src.format(extra_imports='', typ='bool')),
(10, lambda parser: parser.add_setting('-t', '--test', action='store_false'), expected_src.format(extra_imports='', typ='bool')),
(11, lambda parser: parser.add_setting('-t', '--test', action=settngs.BooleanOptionalAction), expected_src.format(extra_imports='', typ='bool | None')),
(12, lambda parser: parser.add_setting('-t', '--test', action=_customAction), expected_src.format(extra_imports='import typing\n', typ='typing.Any')),
(13, lambda parser: parser.add_setting('-t', '--test', action='help'), no_type_expected_src),
(14, lambda parser: parser.add_setting('-t', '--test', action='version'), no_type_expected_src),
(15, lambda parser: parser.add_setting('-t', '--test', type=int), expected_src.format(extra_imports='', typ='int | None')),
(16, lambda parser: parser.add_setting('-t', '--test', type=_typed_function), expected_src.format(extra_imports='import tests.settngs_test\n', typ='tests.settngs_test.test_type | None')),
(17, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function, default=1), expected_src.format(extra_imports='', typ='int')),
(18, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function, default=[1]), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ='typing.List[int]' if sys.version_info < (3, 9) else 'list[int]')),
(19, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function), expected_src.format(extra_imports='import typing\n', typ='typing.Any')),
(3, lambda parser: parser.add_setting('-t', '--test', default='test'), expected_src.format(extra_imports='', typ='str')),
(4, lambda parser: parser.add_setting('-t', '--test', default='test', file=True, cmdline=False), expected_src.format(extra_imports='', typ='str')),
(5, lambda parser: parser.add_setting('-t', '--test', action='count'), expected_src.format(extra_imports='', typ='int | None')),
(6, lambda parser: parser.add_setting('-t', '--test', action='append'), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{List[str]} | None')),
(7, lambda parser: parser.add_setting('-t', '--test', action='extend'), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{List[str]} | None')),
(8, lambda parser: parser.add_setting('-t', '--test', nargs='+'), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{List[str]} | None')),
(9, lambda parser: parser.add_setting('-t', '--test', action='store_const', const=1), expected_src.format(extra_imports='', typ='int | None')),
(10, lambda parser: parser.add_setting('-t', '--test', action='append_const', const=1), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{List[int]} | None')),
(11, lambda parser: parser.add_setting('-t', '--test', action='store_true'), expected_src.format(extra_imports='', typ='bool')),
(12, lambda parser: parser.add_setting('-t', '--test', action='store_false'), expected_src.format(extra_imports='', typ='bool')),
(13, lambda parser: parser.add_setting('-t', '--test', action=settngs.BooleanOptionalAction), expected_src.format(extra_imports='', typ='bool | None')),
(14, lambda parser: parser.add_setting('-t', '--test', action=_customAction), expected_src.format(extra_imports='import typing\n', typ='typing.Any')),
(15, lambda parser: parser.add_setting('-t', '--test', action='help'), no_type_expected_src),
(16, lambda parser: parser.add_setting('-t', '--test', action='version'), no_type_expected_src),
(17, lambda parser: parser.add_setting('-t', '--test', type=int), expected_src.format(extra_imports='', typ='int | None')),
(18, lambda parser: parser.add_setting('-t', '--test', type=int, nargs='+'), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{List[int]} | None')),
(19, lambda parser: parser.add_setting('-t', '--test', type=_typed_function), expected_src.format(extra_imports='import tests.settngs_test\n', typ='tests.settngs_test.test_type | None')),
(20, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function, default=1), expected_src.format(extra_imports='', typ='int')),
(21, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function, default=[1]), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{List[int]}')),
(22, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function), expected_src.format(extra_imports='import typing\n', typ='typing.Any')),
(23, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function, default={1}), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{Set[int]}')),
(24, lambda parser: parser.add_setting('-t', '--test', action='append', type=int), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{List[int]} | None')),
(25, lambda parser: parser.add_setting('-t', '--test', action='extend', type=int, nargs=2), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{List[int]} | None')),
(26, lambda parser: parser.add_setting('-t', '--test', action='append', type=int, nargs=2), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{List[List[int]]} | None')),
(27, lambda parser: parser.add_setting('-t', '--test', action='extend', nargs='+'), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{List[str]} | None')),
(28, lambda parser: parser.add_setting('-t', '--test', action='extend', type=_typed_list_generic_function), expected_src.format(extra_imports='import typing\n' if sys.version_info < (3, 9) else '', typ=f'{List[test_type]} | None')),
(29, lambda parser: parser.add_setting('-t', '--test', action='extend', type=_typed_list_function), expected_src.format(extra_imports='', typ=f'{settngs._type_to_string(List)[0]} | None')),
(30, lambda parser: parser.add_setting('-t', '--test', action='extend', type=_typed_set_function), expected_src.format(extra_imports='', typ=f'{settngs._type_to_string(Set)[0]} | None')),
)
@ -760,23 +797,34 @@ settings_dict = (
(0, lambda parser: parser.add_setting('-t', '--test'), expected_src_dict.format(extra_imports='', typ='str | None')),
(1, lambda parser: parser.add_setting('-t', '--test', cmdline=False), expected_src_dict.format(extra_imports='', typ='typing.Any')),
(2, lambda parser: parser.add_setting('-t', '--test', default=1, file=True, cmdline=False), expected_src_dict.format(extra_imports='', typ='int')),
(3, lambda parser: parser.add_setting('-t', '--test', action='count'), expected_src_dict.format(extra_imports='', typ='int | None')),
(4, lambda parser: parser.add_setting('-t', '--test', action='append'), expected_src_dict.format(extra_imports='', typ='typing.List[str] | None' if sys.version_info < (3, 9) else 'list[str] | None')),
(5, lambda parser: parser.add_setting('-t', '--test', action='extend'), expected_src_dict.format(extra_imports='', typ='typing.List[str] | None' if sys.version_info < (3, 9) else 'list[str] | None')),
(6, lambda parser: parser.add_setting('-t', '--test', nargs='+'), expected_src_dict.format(extra_imports='', typ='typing.List[str] | None' if sys.version_info < (3, 9) else 'list[str] | None')),
(7, lambda parser: parser.add_setting('-t', '--test', action='store_const', const=1), expected_src_dict.format(extra_imports='', typ='int | None')),
(8, lambda parser: parser.add_setting('-t', '--test', action='append_const', const=1), expected_src_dict.format(extra_imports='', typ='typing.List[int] | None' if sys.version_info < (3, 9) else 'list[int] | None')),
(9, lambda parser: parser.add_setting('-t', '--test', action='store_true'), expected_src_dict.format(extra_imports='', typ='bool')),
(10, lambda parser: parser.add_setting('-t', '--test', action='store_false'), expected_src_dict.format(extra_imports='', typ='bool')),
(11, lambda parser: parser.add_setting('-t', '--test', action=settngs.BooleanOptionalAction), expected_src_dict.format(extra_imports='', typ='bool | None')),
(12, lambda parser: parser.add_setting('-t', '--test', action=_customAction), expected_src_dict.format(extra_imports='', typ='typing.Any')),
(13, lambda parser: parser.add_setting('-t', '--test', action='help'), no_type_expected_src_dict),
(14, lambda parser: parser.add_setting('-t', '--test', action='version'), no_type_expected_src_dict),
(15, lambda parser: parser.add_setting('-t', '--test', type=int), expected_src_dict.format(extra_imports='', typ='int | None')),
(16, lambda parser: parser.add_setting('-t', '--test', type=_typed_function), expected_src_dict.format(extra_imports='import tests.settngs_test\n', typ='tests.settngs_test.test_type | None')),
(17, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function, default=1), expected_src_dict.format(extra_imports='', typ='int')),
(18, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function, default=[1]), expected_src_dict.format(extra_imports='', typ='typing.List[int]' if sys.version_info < (3, 9) else 'list[int]')),
(19, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function), expected_src_dict.format(extra_imports='', typ='typing.Any')),
(3, lambda parser: parser.add_setting('-t', '--test', default='test'), expected_src_dict.format(extra_imports='', typ='str')),
(4, lambda parser: parser.add_setting('-t', '--test', default='test', file=True, cmdline=False), expected_src_dict.format(extra_imports='', typ='str')),
(5, lambda parser: parser.add_setting('-t', '--test', action='count'), expected_src_dict.format(extra_imports='', typ='int | None')),
(6, lambda parser: parser.add_setting('-t', '--test', action='append'), expected_src_dict.format(extra_imports='', typ=f'{List[str]} | None')),
(7, lambda parser: parser.add_setting('-t', '--test', action='extend'), expected_src_dict.format(extra_imports='', typ=f'{List[str]} | None')),
(8, lambda parser: parser.add_setting('-t', '--test', nargs='+'), expected_src_dict.format(extra_imports='', typ=f'{List[str]} | None')),
(9, lambda parser: parser.add_setting('-t', '--test', action='store_const', const=1), expected_src_dict.format(extra_imports='', typ='int | None')),
(10, lambda parser: parser.add_setting('-t', '--test', action='append_const', const=1), expected_src_dict.format(extra_imports='', typ=f'{List[int]} | None')),
(11, lambda parser: parser.add_setting('-t', '--test', action='store_true'), expected_src_dict.format(extra_imports='', typ='bool')),
(12, lambda parser: parser.add_setting('-t', '--test', action='store_false'), expected_src_dict.format(extra_imports='', typ='bool')),
(13, lambda parser: parser.add_setting('-t', '--test', action=settngs.BooleanOptionalAction), expected_src_dict.format(extra_imports='', typ='bool | None')),
(14, lambda parser: parser.add_setting('-t', '--test', action=_customAction), expected_src_dict.format(extra_imports='', typ='typing.Any')),
(15, lambda parser: parser.add_setting('-t', '--test', action='help'), no_type_expected_src_dict),
(16, lambda parser: parser.add_setting('-t', '--test', action='version'), no_type_expected_src_dict),
(17, lambda parser: parser.add_setting('-t', '--test', type=int), expected_src_dict.format(extra_imports='', typ='int | None')),
(18, lambda parser: parser.add_setting('-t', '--test', type=int, nargs='+'), expected_src_dict.format(extra_imports='', typ=f'{List[int]} | None')),
(19, lambda parser: parser.add_setting('-t', '--test', type=_typed_function), expected_src_dict.format(extra_imports='import tests.settngs_test\n', typ=f'{test_type.__module__}.{test_type.__name__} | None')),
(20, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function, default=1), expected_src_dict.format(extra_imports='', typ='int')),
(21, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function, default=[1]), expected_src_dict.format(extra_imports='', typ=f'{List[int]}')),
(22, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function), expected_src_dict.format(extra_imports='', typ='typing.Any')),
(23, lambda parser: parser.add_setting('-t', '--test', type=_untyped_function, default={1}), expected_src_dict.format(extra_imports='', typ=f'{Set[int]}')),
(24, lambda parser: parser.add_setting('-t', '--test', action='append', type=int), expected_src_dict.format(extra_imports='', typ=f'{List[int]} | None')),
(25, lambda parser: parser.add_setting('-t', '--test', action='extend', type=int, nargs=2), expected_src_dict.format(extra_imports='', typ=f'{List[int]} | None')),
(26, lambda parser: parser.add_setting('-t', '--test', action='append', type=int, nargs=2), expected_src_dict.format(extra_imports='', typ=f'{List[List[int]]} | None')),
(27, lambda parser: parser.add_setting('-t', '--test', action='extend', nargs='+'), expected_src_dict.format(extra_imports='', typ=f'{List[str]} | None')),
(28, lambda parser: parser.add_setting('-t', '--test', action='extend', type=_typed_list_generic_function), expected_src_dict.format(extra_imports='', typ=f'{List[test_type]} | None')),
(29, lambda parser: parser.add_setting('-t', '--test', action='extend', type=_typed_list_function), expected_src_dict.format(extra_imports='', typ=f'{settngs._type_to_string(List)[0]} | None')),
(30, lambda parser: parser.add_setting('-t', '--test', action='extend', type=_typed_set_function), expected_src_dict.format(extra_imports='', typ=f'{settngs._type_to_string(Set)[0]} | None')),
)