pydantic / pydantic-settings

Settings management using pydantic
https://docs.pydantic.dev/latest/usage/pydantic_settings/
MIT License
589 stars 61 forks source link

2.3.4: `pyupgrade --py39-plus` generated changes causes pytest fails #323

Open kloczek opened 3 months ago

kloczek commented 3 months ago

I've been testing pyupgrade --py39-plus (Oct this year 3.8 will be EOSed) causes pytest fails

```console + PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-pydantic-settings-2.3.4-2.fc37.x86_64/usr/lib64/python3.10/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-pydantic-settings-2.3.4-2.fc37.x86_64/usr/lib/python3.10/site-packages + /usr/bin/pytest -ra -m 'not network' ============================= test session starts ============================== platform linux -- Python 3.10.14, pytest-8.2.2, pluggy-1.5.0 rootdir: /home/tkloczko/rpmbuild/BUILD/pydantic-settings-2.3.4 configfile: pyproject.toml testpaths: tests plugins: examples-0.0.10, mock-3.14.0 collected 290 items tests/test_docs.py .................................. [ 11%] tests/test_settings.py ................................................. [ 28%] ........................................................................ [ 53%] .................................F..FsFFFF..ss..............F..F.FFFF... [ 78%] ..............F....F..F....Fs..ss.............s.......... [ 97%] tests/test_sources.py ...... [100%] =================================== FAILURES =================================== ____ test_cli_metavar_format[True-list-List[ForwardRef('SomeForwardRef')]] _____ hide_none_type = True, value = typing.Optional[list['SomeForwardRef']] expected = "List[ForwardRef('SomeForwardRef')]" @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E assert 'list[str]' == "List[Forward...ForwardRef')]" E E - List[ForwardRef('SomeForwardRef')] E + list[str] tests/test_settings.py:3310: AssertionError ___________________ test_cli_metavar_format[True-list-List] ____________________ hide_none_type = True, value = typing.Optional[list], expected = 'List' @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'list' == 'List' E E - List E ? ^ E + list E ? ^ tests/test_settings.py:3310: AssertionError ____________ test_cli_metavar_format[True-list-List[Dict[str,int]]] ____________ hide_none_type = True, value = typing.Optional[list[dict[str, int]]] expected = 'List[Dict[str,int]]' @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'list[dict[str,int]]' == 'List[Dict[str,int]]' E E - List[Dict[str,int]] E ? ^ ^ E + list[dict[str,int]] E ? ^ ^ tests/test_settings.py:3310: AssertionError ___________ test_cli_metavar_format[True-tuple-Tuple[str,int,float]] ___________ hide_none_type = True, value = typing.Optional[tuple[str, int, float]] expected = 'Tuple[str,int,float]' @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'tuple[str,int,float]' == 'Tuple[str,int,float]' E E - Tuple[str,int,float] E ? ^ E + tuple[str,int,float] E ? ^ tests/test_settings.py:3310: AssertionError ______________ test_cli_metavar_format[True-tuple-Tuple[str,...]] ______________ hide_none_type = True, value = typing.Optional[tuple[str, ...]] expected = 'Tuple[str,...]' @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'tuple[str,...]' == 'Tuple[str,...]' E E - Tuple[str,...] E ? ^ E + tuple[str,...] E ? ^ tests/test_settings.py:3310: AssertionError ______ test_cli_metavar_format[True-Union-{int,List[str],Tuple[str,int]}] ______ hide_none_type = True value = typing.Union[int, list[str], tuple[str, int], NoneType] expected = '{int,List[str],Tuple[str,int]}' @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert '{int,list[st...ple[str,int]}' == '{int,List[st...ple[str,int]}' E E - {int,List[str],Tuple[str,int]} E ? ^ ^ E + {int,list[str],tuple[str,int]} E ? ^ ^ tests/test_settings.py:3310: AssertionError ____ test_cli_metavar_format[False-list-List[ForwardRef('SomeForwardRef')]] ____ hide_none_type = False, value = list['SomeForwardRef'] expected = "List[ForwardRef('SomeForwardRef')]" @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E assert 'list[str]' == "List[Forward...ForwardRef')]" E E - List[ForwardRef('SomeForwardRef')] E + list[str] tests/test_settings.py:3310: AssertionError ___________________ test_cli_metavar_format[False-list-List] ___________________ hide_none_type = False, value = , expected = 'List' @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'list' == 'List' E E - List E ? ^ E + list E ? ^ tests/test_settings.py:3310: AssertionError ___________ test_cli_metavar_format[False-list-List[Dict[str,int]]] ____________ hide_none_type = False, value = list[dict[str, int]] expected = 'List[Dict[str,int]]' @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'list[dict[str,int]]' == 'List[Dict[str,int]]' E E - List[Dict[str,int]] E ? ^ ^ E + list[dict[str,int]] E ? ^ ^ tests/test_settings.py:3310: AssertionError __________ test_cli_metavar_format[False-tuple-Tuple[str,int,float]] ___________ hide_none_type = False, value = tuple[str, int, float] expected = 'Tuple[str,int,float]' @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'tuple[str,int,float]' == 'Tuple[str,int,float]' E E - Tuple[str,int,float] E ? ^ E + tuple[str,int,float] E ? ^ tests/test_settings.py:3310: AssertionError _____________ test_cli_metavar_format[False-tuple-Tuple[str,...]] ______________ hide_none_type = False, value = tuple[str, ...], expected = 'Tuple[str,...]' @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'tuple[str,...]' == 'Tuple[str,...]' E E - Tuple[str,...] E ? ^ E + tuple[str,...] E ? ^ tests/test_settings.py:3310: AssertionError _____ test_cli_metavar_format[False-Union-{int,List[str],Tuple[str,int]}] ______ hide_none_type = False, value = typing.Union[int, list[str], tuple[str, int]] expected = '{int,List[str],Tuple[str,int]}' @pytest.mark.parametrize( 'value,expected', [ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), (list, 'List'), ([1, 2, 3], 'list'), (list[dict[str, int]], 'List[Dict[str,int]]'), (tuple[str, int, float], 'Tuple[str,int,float]'), (tuple[str, ...], 'Tuple[str,...]'), (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), (Representation(), 'Representation()'), (typing.Literal[1, 2, 3], '{1,2,3}'), (typing_extensions.Literal[1, 2, 3], '{1,2,3}'), (typing.Literal['a', 'b', 'c'], '{a,b,c}'), (typing_extensions.Literal['a', 'b', 'c'], '{a,b,c}'), (SimpleSettings, 'JSON'), (Union[SimpleSettings, SettingWithIgnoreEmpty], 'JSON'), (Union[SimpleSettings, str, SettingWithIgnoreEmpty], '{JSON,str}'), (Union[str, SimpleSettings, SettingWithIgnoreEmpty], '{str,JSON}'), (Annotated[SimpleSettings, 'annotation'], 'JSON'), (DirectoryPath, 'Path'), (FruitsEnum, '{pear,kiwi,lime}'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format(hide_none_type, value, expected): cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if value == [1, 2, 3] or isinstance(value, LoggedVar) or isinstance(value, Representation): pytest.skip() if value in ('foobar', 'SomeForwardRefString'): expected = f"ForwardRef('{value}')" # forward ref implicit cast if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert '{int,list[st...ple[str,int]}' == '{int,List[st...ple[str,int]}' E E - {int,List[str],Tuple[str,int]} E ? ^ ^ E + {int,list[str],tuple[str,int]} E ? ^ ^ tests/test_settings.py:3310: AssertionError _____________ test_cli_metavar_format_310[True--List[int]] _____________ hide_none_type = True, value_gen = at 0x7fc8b190a320> expected = 'List[int]' @pytest.mark.skipif(sys.version_info < (3, 10), reason='requires python 3.10 or higher') @pytest.mark.parametrize( 'value_gen,expected', [ (lambda: str | int, '{str,int}'), (lambda: list[int], 'list[int]'), (lambda: list[int], 'List[int]'), (lambda: list[dict[str, int]], 'list[dict[str,int]]'), (lambda: list[Union[str, int]], 'list[{str,int}]'), (lambda: list[str | int], 'list[{str,int}]'), (lambda: LoggedVar[int], 'LoggedVar[int]'), (lambda: LoggedVar[dict[int, str]], 'LoggedVar[Dict[int,str]]'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format_310(hide_none_type, value_gen, expected): value = value_gen() cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'list[int]' == 'List[int]' E E - List[int] E ? ^ E + list[int] E ? ^ tests/test_settings.py:3337: AssertionError _____ test_cli_metavar_format_310[True--LoggedVar[Dict[int,str]]] ______ hide_none_type = True, value_gen = at 0x7fc8b190a5f0> expected = 'LoggedVar[Dict[int,str]]' @pytest.mark.skipif(sys.version_info < (3, 10), reason='requires python 3.10 or higher') @pytest.mark.parametrize( 'value_gen,expected', [ (lambda: str | int, '{str,int}'), (lambda: list[int], 'list[int]'), (lambda: list[int], 'List[int]'), (lambda: list[dict[str, int]], 'list[dict[str,int]]'), (lambda: list[Union[str, int]], 'list[{str,int}]'), (lambda: list[str | int], 'list[{str,int}]'), (lambda: LoggedVar[int], 'LoggedVar[int]'), (lambda: LoggedVar[dict[int, str]], 'LoggedVar[Dict[int,str]]'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format_310(hide_none_type, value_gen, expected): value = value_gen() cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'LoggedVar[dict[int,str]]' == 'LoggedVar[Dict[int,str]]' E E - LoggedVar[Dict[int,str]] E ? ^ E + LoggedVar[dict[int,str]] E ? ^ tests/test_settings.py:3337: AssertionError ____________ test_cli_metavar_format_310[False--List[int]] _____________ hide_none_type = False, value_gen = at 0x7fc8b190a320> expected = 'List[int]' @pytest.mark.skipif(sys.version_info < (3, 10), reason='requires python 3.10 or higher') @pytest.mark.parametrize( 'value_gen,expected', [ (lambda: str | int, '{str,int}'), (lambda: list[int], 'list[int]'), (lambda: list[int], 'List[int]'), (lambda: list[dict[str, int]], 'list[dict[str,int]]'), (lambda: list[Union[str, int]], 'list[{str,int}]'), (lambda: list[str | int], 'list[{str,int}]'), (lambda: LoggedVar[int], 'LoggedVar[int]'), (lambda: LoggedVar[dict[int, str]], 'LoggedVar[Dict[int,str]]'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format_310(hide_none_type, value_gen, expected): value = value_gen() cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'list[int]' == 'List[int]' E E - List[int] E ? ^ E + list[int] E ? ^ tests/test_settings.py:3337: AssertionError _____ test_cli_metavar_format_310[False--LoggedVar[Dict[int,str]]] _____ hide_none_type = False, value_gen = at 0x7fc8b190a5f0> expected = 'LoggedVar[Dict[int,str]]' @pytest.mark.skipif(sys.version_info < (3, 10), reason='requires python 3.10 or higher') @pytest.mark.parametrize( 'value_gen,expected', [ (lambda: str | int, '{str,int}'), (lambda: list[int], 'list[int]'), (lambda: list[int], 'List[int]'), (lambda: list[dict[str, int]], 'list[dict[str,int]]'), (lambda: list[Union[str, int]], 'list[{str,int}]'), (lambda: list[str | int], 'list[{str,int}]'), (lambda: LoggedVar[int], 'LoggedVar[int]'), (lambda: LoggedVar[dict[int, str]], 'LoggedVar[Dict[int,str]]'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) def test_cli_metavar_format_310(hide_none_type, value_gen, expected): value = value_gen() cli_settings = CliSettingsSource(SimpleSettings, cli_hide_none_type=hide_none_type) if hide_none_type: if typing_extensions.get_origin(value) is Union: args = typing_extensions.get_args(value) value = Union[args + (None,) if args else (value, None)] else: value = Union[(value, None)] > assert cli_settings._metavar_format(value) == expected E AssertionError: assert 'LoggedVar[dict[int,str]]' == 'LoggedVar[Dict[int,str]]' E E - LoggedVar[Dict[int,str]] E ? ^ E + LoggedVar[dict[int,str]] E ? ^ tests/test_settings.py:3337: AssertionError =========================== short test summary info ============================ SKIPPED [3] tests/test_settings.py:3302: Skipped SKIPPED [1] tests/test_settings.py:3340: requires python 3.12 or higher SKIPPED [1] tests/test_settings.py:3402: pyYaml is not installed SKIPPED [1] tests/test_settings.py:3439: pyYaml is not installed SKIPPED [1] tests/test_settings.py:3768: pyYAML is not installed FAILED tests/test_settings.py::test_cli_metavar_format[True-list-List[ForwardRef('SomeForwardRef')]] FAILED tests/test_settings.py::test_cli_metavar_format[True-list-List] - Asse... FAILED tests/test_settings.py::test_cli_metavar_format[True-list-List[Dict[str,int]]] FAILED tests/test_settings.py::test_cli_metavar_format[True-tuple-Tuple[str,int,float]] FAILED tests/test_settings.py::test_cli_metavar_format[True-tuple-Tuple[str,...]] FAILED tests/test_settings.py::test_cli_metavar_format[True-Union-{int,List[str],Tuple[str,int]}] FAILED tests/test_settings.py::test_cli_metavar_format[False-list-List[ForwardRef('SomeForwardRef')]] FAILED tests/test_settings.py::test_cli_metavar_format[False-list-List] - Ass... FAILED tests/test_settings.py::test_cli_metavar_format[False-list-List[Dict[str,int]]] FAILED tests/test_settings.py::test_cli_metavar_format[False-tuple-Tuple[str,int,float]] FAILED tests/test_settings.py::test_cli_metavar_format[False-tuple-Tuple[str,...]] FAILED tests/test_settings.py::test_cli_metavar_format[False-Union-{int,List[str],Tuple[str,int]}] FAILED tests/test_settings.py::test_cli_metavar_format_310[True--List[int]] FAILED tests/test_settings.py::test_cli_metavar_format_310[True--LoggedVar[Dict[int,str]]] FAILED tests/test_settings.py::test_cli_metavar_format_310[False--List[int]] FAILED tests/test_settings.py::test_cli_metavar_format_310[False--LoggedVar[Dict[int,str]]] ================== 16 failed, 267 passed, 7 skipped in 3.37s =================== ```

Is it anything which can be done in advance on pyupgrade or pydantic-settings side to make above legacy code upgrade/drop possible? 🤔

kloczek commented 3 months ago

Here is the patch:

```patch --- a/pydantic_settings/sources.py +++ b/pydantic_settings/sources.py @@ -20,14 +20,13 @@ Callable, Generic, List, - Mapping, - Sequence, Tuple, TypeVar, Union, cast, overload, ) +from collections.abc import Mapping, Sequence import typing_extensions from dotenv import dotenv_values @@ -38,7 +37,8 @@ from pydantic.dataclasses import is_pydantic_dataclass from pydantic.fields import FieldInfo from pydantic_core import PydanticUndefined -from typing_extensions import Annotated, _AnnotatedAlias, get_args, get_origin +from typing_extensions import _AnnotatedAlias, get_args, get_origin +from typing import Annotated from pydantic_settings.utils import path_type_label @@ -83,8 +83,8 @@ import tomllib -DotenvType = Union[Path, str, List[Union[Path, str]], Tuple[Union[Path, str], ...]] -PathType = Union[Path, str, List[Union[Path, str]], Tuple[Union[Path, str], ...]] +DotenvType = Union[Path, str, list[Union[Path, str]], tuple[Union[Path, str], ...]] +PathType = Union[Path, str, list[Union[Path, str]], tuple[Union[Path, str], ...]] DEFAULT_PATH: PathType = Path('') # This is used as default value for `_env_file` in the `BaseSettings` class and --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -9,7 +9,8 @@ from datetime import datetime, timezone from enum import IntEnum from pathlib import Path -from typing import Any, Callable, Dict, Generic, Hashable, List, Optional, Set, Tuple, Type, TypeVar, Union +from typing import Any, Callable, Dict, Generic, List, Optional, Set, Tuple, Type, TypeVar, Union +from collections.abc import Hashable import pytest import typing_extensions @@ -34,7 +35,8 @@ from pydantic._internal._repr import Representation from pydantic.fields import FieldInfo from pytest_mock import MockerFixture -from typing_extensions import Annotated, Literal +from typing_extensions import Literal +from typing import Annotated from pydantic_settings import ( BaseSettings, @@ -217,7 +219,7 @@ def test_merge_dict(env): class Settings(BaseSettings): - top: Dict[str, str] + top: dict[str, str] with pytest.raises(ValidationError): Settings() @@ -271,7 +273,7 @@ def test_nested_env_optional_json(env): class Child(BaseModel): - num_list: Optional[List[int]] = None + num_list: Optional[list[int]] = None class Cfg(BaseSettings, env_nested_delimiter='__'): child: Optional[Child] = None @@ -340,8 +342,8 @@ class ComplexSettings(BaseSettings): - apples: List[str] = [] - bananas: Set[int] = set() + apples: list[str] = [] + bananas: set[int] = set() carrots: dict = {} date: DateModel = DateModel() @@ -355,7 +357,7 @@ def test_annotated_list(env): class AnnotatedComplexSettings(BaseSettings): - apples: Annotated[List[str], MinLen(2)] = [] + apples: Annotated[list[str], MinLen(2)] = [] env.set('apples', '["russet", "granny smith"]') s = AnnotatedComplexSettings() @@ -698,12 +700,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return env_settings, init_settings env.set('BAR', 'env setting') @@ -718,7 +720,7 @@ See https://github.com/pydantic/pydantic/pull/341#issuecomment-450378771 """ - def nornir_settings_source() -> Dict[str, Any]: + def nornir_settings_source() -> dict[str, Any]: return {'param_a': 'config a', 'param_b': 'config b', 'param_c': 'config c'} class Settings(BaseSettings): @@ -729,12 +731,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return env_settings, init_settings, nornir_settings_source env.set('PARAM_C', 'env setting c') @@ -1204,12 +1206,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (YamlConfigSettingsSource(settings_cls),) with pytest.raises(ImportError, match=r'^PyYAML is not installed, run `pip install pydantic-settings\[yaml\]`$'): @@ -1324,7 +1326,7 @@ p.write_text('{"a": "b"}') class Settings(BaseSettings): - foo: Dict[str, str] + foo: dict[str, str] model_config = SettingsConfigDict(secrets_dir=tmp_path) @@ -1351,7 +1353,7 @@ p.write_text('{"a": "b"') class Settings(BaseSettings): - foo: Dict[str, str] + foo: dict[str, str] model_config = SettingsConfigDict(secrets_dir=tmp_path) @@ -1362,7 +1364,7 @@ def test_secrets_missing(tmp_path): class Settings(BaseSettings): foo: str - bar: List[str] + bar: list[str] model_config = SettingsConfigDict(secrets_dir=tmp_path) @@ -1427,10 +1429,10 @@ def test_external_settings_sources_precedence(env): - def external_source_0() -> Dict[str, str]: + def external_source_0() -> dict[str, str]: return {'apple': 'value 0', 'banana': 'value 2'} - def external_source_1() -> Dict[str, str]: + def external_source_1() -> dict[str, str]: return {'apple': 'value 1', 'raspberry': 'value 3'} class Settings(BaseSettings): @@ -1441,12 +1443,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return ( init_settings, env_settings, @@ -1464,7 +1466,7 @@ vault_storage = {'user:password': {'apple': 'value 0', 'banana': 'value 2'}} class VaultSettingsSource(PydanticBaseSettingsSource): - def __init__(self, settings_cls: Type[BaseSettings], user: str, password: str): + def __init__(self, settings_cls: type[BaseSettings], user: str, password: str): self.user = user self.password = password super().__init__(settings_cls) @@ -1472,7 +1474,7 @@ def get_field_value(self, field: FieldInfo, field_name: str) -> Any: pass - def __call__(self) -> Dict[str, str]: + def __call__(self) -> dict[str, str]: vault_vars = vault_storage[f'{self.user}:{self.password}'] return { field_name: vault_vars[field_name] @@ -1487,12 +1489,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return ( init_settings, env_settings, @@ -1536,7 +1538,7 @@ ) -def _parse_custom_dict(value: str) -> Callable[[str], Dict[int, str]]: +def _parse_custom_dict(value: str) -> Callable[[str], dict[int, str]]: """A custom parsing function passed into env parsing test.""" res = {} for part in value.split(','): @@ -1555,17 +1557,17 @@ def test_env_setting_source_custom_env_parse(env): class Settings(BaseSettings): - top: Dict[int, str] + top: dict[int, str] @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (CustomEnvSettingsSource(settings_cls),) with pytest.raises(ValidationError): @@ -1583,17 +1585,17 @@ def test_env_settings_source_custom_env_parse_is_bad(env): class Settings(BaseSettings): - top: Dict[int, str] + top: dict[int, str] @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (BadCustomEnvSettingsSource(settings_cls),) env.set('top', '1=apple,2=banana') @@ -1616,18 +1618,18 @@ p.write_text('1=apple,2=banana') class Settings(BaseSettings): - top: Dict[int, str] + top: dict[int, str] model_config = SettingsConfigDict(secrets_dir=tmp_path) def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (CustomSecretsSettingsSource(settings_cls, tmp_path),) s = Settings() @@ -1641,17 +1643,17 @@ def test_custom_source_get_field_value_error(env): class Settings(BaseSettings): - top: Dict[int, str] + top: dict[int, str] @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (BadCustomSettingsSource(settings_cls),) with pytest.raises( @@ -1662,10 +1664,10 @@ def test_nested_env_complex_values(env): class SubSubModel(BaseSettings): - dvals: Dict + dvals: dict class SubModel(BaseSettings): - vals: List[str] + vals: list[str] sub_sub_model: SubSubModel class Cfg(BaseSettings): @@ -1687,7 +1689,7 @@ def test_nested_env_nonexisting_field(env): class SubModel(BaseSettings): - vals: List[str] + vals: list[str] class Cfg(BaseSettings): sub_model: SubModel @@ -1701,7 +1703,7 @@ def test_nested_env_nonexisting_field_deep(env): class SubModel(BaseSettings): - vals: List[str] + vals: list[str] class Cfg(BaseSettings): sub_model: SubModel @@ -1715,7 +1717,7 @@ def test_nested_env_union_complex_values(env): class SubModel(BaseSettings): - vals: Union[List[str], Dict[str, str]] + vals: Union[list[str], dict[str, str]] class Cfg(BaseSettings): sub_model: SubModel @@ -2069,7 +2071,7 @@ def test_env_json_field_dict(env): class Settings(BaseSettings): - x: Json[Dict[str, int]] + x: Json[dict[str, int]] env.set('x', '{"foo": 1}') @@ -2124,9 +2126,9 @@ def test_root_model_as_field(env): class Foo(BaseModel): x: int - y: Dict[str, int] + y: dict[str, int] - FooRoot = RootModel[List[Foo]] + FooRoot = RootModel[list[Foo]] class Settings(BaseSettings): z: FooRoot @@ -2153,7 +2155,7 @@ class Settings(BaseSettings): model_config = SettingsConfigDict(env_file=p) - data: Optional[Json[Dict[str, str]]] = Field(default=None) + data: Optional[Json[dict[str, str]]] = Field(default=None) s = Settings() assert s.data == {'foo': 'bar'} @@ -2179,7 +2181,7 @@ v0_union: Union[SubValue, int] top: TopValue - args: List[str] = [] + args: list[str] = [] args += ['--top', '{"v1": "json-1", "v2": "json-2", "sub": {"v5": "xx"}}'] args += ['--top.sub.v5', '5'] args += ['--v0', '0'] @@ -2211,12 +2213,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return env_settings, CliSettingsSource(settings_cls, cli_parse_args=['--foo', 'FOO FROM CLI']) env.set('FOO', 'FOO FROM ENV') @@ -2408,16 +2410,16 @@ val: int class Child(BaseModel): - num_list: Optional[List[int]] = None - obj_list: Optional[List[Obj]] = None - str_list: Optional[List[str]] = None - union_list: Optional[List[Union[Obj, int]]] = None + num_list: Optional[list[int]] = None + obj_list: Optional[list[Obj]] = None + str_list: Optional[list[str]] = None + union_list: Optional[list[Union[Obj, int]]] = None class Cfg(BaseSettings): - num_list: Optional[List[int]] = None - obj_list: Optional[List[Obj]] = None - union_list: Optional[List[Union[Obj, int]]] = None - str_list: Optional[List[str]] = None + num_list: Optional[list[int]] = None + obj_list: Optional[list[Obj]] = None + union_list: Optional[list[Union[Obj, int]]] = None + str_list: Optional[list[str]] = None child: Optional[Child] = None def check_answer(cfg, prefix, expected): @@ -2433,7 +2435,7 @@ expected['child'] = None assert cfg.model_dump() == expected - args: List[str] = [] + args: list[str] = [] args = [f'--{prefix}num_list', '[1,2]'] args += [f'--{prefix}num_list', '3,4'] args += [f'--{prefix}num_list', '5', f'--{prefix}num_list', '6'] @@ -2485,7 +2487,7 @@ def test_cli_list_json_value_parsing(): class Cfg(BaseSettings): - json_list: List[Union[str, bool, None]] + json_list: list[Union[str, bool, None]] assert Cfg( _cli_parse_args=[ @@ -2507,13 +2509,13 @@ @pytest.mark.parametrize('prefix', ['', 'child.']) def test_cli_dict_arg(prefix): class Child(BaseModel): - check_dict: Dict[str, str] + check_dict: dict[str, str] class Cfg(BaseSettings): - check_dict: Optional[Dict[str, str]] = None + check_dict: Optional[dict[str, str]] = None child: Optional[Child] = None - args: List[str] = [] + args: list[str] = [] args = [f'--{prefix}check_dict', '{"k1":"a","k2":"b"}'] args += [f'--{prefix}check_dict', '{"k3":"c"},{"k4":"d"}'] args += [f'--{prefix}check_dict', '{"k5":"e"}', f'--{prefix}check_dict', '{"k6":"f"}'] @@ -2528,7 +2530,7 @@ args += [f'--{prefix}check_dict', 'k29="x,y",k30="x,y"'] args += [f'--{prefix}check_dict', 'k31="x,y"', f'--{prefix}check_dict', 'k32="x,y"'] cfg = Cfg(_cli_parse_args=args) - expected: Dict[str, Any] = { + expected: dict[str, Any] = { 'check_dict': { 'k1': 'a', 'k2': 'b', @@ -2581,7 +2583,7 @@ def test_cli_union_dict_arg(): class Cfg(BaseSettings): - union_str_dict: Union[str, Dict[str, Any]] + union_str_dict: Union[str, dict[str, Any]] with pytest.raises(ValidationError) as exc_info: args = ['--union_str_dict', 'hello world', '--union_str_dict', 'hello world'] @@ -2630,7 +2632,7 @@ assert cfg.model_dump() == {'union_str_dict': 'hello=world'} class Cfg(BaseSettings): - union_list_dict: Union[List[str], Dict[str, Any]] + union_list_dict: Union[list[str], dict[str, Any]] with pytest.raises(ValidationError) as exc_info: args = ['--union_list_dict', 'hello,world'] @@ -2703,7 +2705,7 @@ def test_cli_nested_dict_arg(): class Cfg(BaseSettings): - check_dict: Dict[str, Any] + check_dict: dict[str, Any] args = ['--check_dict', '{"k1":{"a": 1}},{"k2":{"b": 2}}'] cfg = Cfg(_cli_parse_args=args) @@ -3266,15 +3268,15 @@ (str, 'str'), ('foobar', 'str'), ('SomeForwardRefString', 'str'), # included to document current behavior; could be changed - (List['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 + (list['SomeForwardRef'], "List[ForwardRef('SomeForwardRef')]"), # noqa: F821 (Union[str, int], '{str,int}'), (list, 'list'), - (List, 'List'), + (list, 'List'), ([1, 2, 3], 'list'), - (List[Dict[str, int]], 'List[Dict[str,int]]'), - (Tuple[str, int, float], 'Tuple[str,int,float]'), - (Tuple[str, ...], 'Tuple[str,...]'), - (Union[int, List[str], Tuple[str, int]], '{int,List[str],Tuple[str,int]}'), + (list[dict[str, int]], 'List[Dict[str,int]]'), + (tuple[str, int, float], 'Tuple[str,int,float]'), + (tuple[str, ...], 'Tuple[str,...]'), + (Union[int, list[str], tuple[str, int]], '{int,List[str],Tuple[str,int]}'), (foobar, 'foobar'), (LoggedVar, 'LoggedVar'), (LoggedVar(), 'LoggedVar'), @@ -3314,12 +3316,12 @@ [ (lambda: str | int, '{str,int}'), (lambda: list[int], 'list[int]'), - (lambda: List[int], 'List[int]'), + (lambda: list[int], 'List[int]'), (lambda: list[dict[str, int]], 'list[dict[str,int]]'), (lambda: list[Union[str, int]], 'list[{str,int}]'), (lambda: list[str | int], 'list[{str,int}]'), (lambda: LoggedVar[int], 'LoggedVar[int]'), - (lambda: LoggedVar[Dict[int, str]], 'LoggedVar[Dict[int,str]]'), + (lambda: LoggedVar[dict[int, str]], 'LoggedVar[Dict[int,str]]'), ], ) @pytest.mark.parametrize('hide_none_type', [True, False]) @@ -3365,12 +3367,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (JsonConfigSettingsSource(settings_cls),) s = Settings() @@ -3385,12 +3387,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (JsonConfigSettingsSource(settings_cls),) s = Settings() @@ -3421,12 +3423,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (YamlConfigSettingsSource(settings_cls),) s = Settings() @@ -3442,12 +3444,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (YamlConfigSettingsSource(settings_cls),) s = Settings() @@ -3477,12 +3479,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (TomlConfigSettingsSource(settings_cls),) s = Settings() @@ -3498,12 +3500,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (TomlConfigSettingsSource(settings_cls),) s = Settings() @@ -3533,8 +3535,8 @@ @classmethod def settings_customise_sources( - cls, settings_cls: Type[BaseSettings], **_kwargs: PydanticBaseSettingsSource - ) -> Tuple[PydanticBaseSettingsSource, ...]: + cls, settings_cls: type[BaseSettings], **_kwargs: PydanticBaseSettingsSource + ) -> tuple[PydanticBaseSettingsSource, ...]: return (PyprojectTomlConfigSettingsSource(settings_cls),) s = Settings() @@ -3575,8 +3577,8 @@ @classmethod def settings_customise_sources( - cls, settings_cls: Type[BaseSettings], **_kwargs: PydanticBaseSettingsSource - ) -> Tuple[PydanticBaseSettingsSource, ...]: + cls, settings_cls: type[BaseSettings], **_kwargs: PydanticBaseSettingsSource + ) -> tuple[PydanticBaseSettingsSource, ...]: return (PyprojectTomlConfigSettingsSource(settings_cls, pyproject),) s = Settings() @@ -3618,8 +3620,8 @@ @classmethod def settings_customise_sources( - cls, settings_cls: Type[BaseSettings], **_kwargs: PydanticBaseSettingsSource - ) -> Tuple[PydanticBaseSettingsSource, ...]: + cls, settings_cls: type[BaseSettings], **_kwargs: PydanticBaseSettingsSource + ) -> tuple[PydanticBaseSettingsSource, ...]: return (PyprojectTomlConfigSettingsSource(settings_cls),) s = Settings() @@ -3650,8 +3652,8 @@ @classmethod def settings_customise_sources( - cls, settings_cls: Type[BaseSettings], **_kwargs: PydanticBaseSettingsSource - ) -> Tuple[PydanticBaseSettingsSource, ...]: + cls, settings_cls: type[BaseSettings], **_kwargs: PydanticBaseSettingsSource + ) -> tuple[PydanticBaseSettingsSource, ...]: return (PyprojectTomlConfigSettingsSource(settings_cls, pyproject),) s = Settings() @@ -3666,8 +3668,8 @@ @classmethod def settings_customise_sources( - cls, settings_cls: Type[BaseSettings], **_kwargs: PydanticBaseSettingsSource - ) -> Tuple[PydanticBaseSettingsSource, ...]: + cls, settings_cls: type[BaseSettings], **_kwargs: PydanticBaseSettingsSource + ) -> tuple[PydanticBaseSettingsSource, ...]: return (PyprojectTomlConfigSettingsSource(settings_cls),) s = Settings() @@ -3686,8 +3688,8 @@ @classmethod def settings_customise_sources( - cls, settings_cls: Type[BaseSettings], **_kwargs: PydanticBaseSettingsSource - ) -> Tuple[PydanticBaseSettingsSource, ...]: + cls, settings_cls: type[BaseSettings], **_kwargs: PydanticBaseSettingsSource + ) -> tuple[PydanticBaseSettingsSource, ...]: return (PyprojectTomlConfigSettingsSource(settings_cls, pyproject),) s = Settings() @@ -3720,8 +3722,8 @@ @classmethod def settings_customise_sources( - cls, settings_cls: Type[BaseSettings], **_kwargs: PydanticBaseSettingsSource - ) -> Tuple[PydanticBaseSettingsSource, ...]: + cls, settings_cls: type[BaseSettings], **_kwargs: PydanticBaseSettingsSource + ) -> tuple[PydanticBaseSettingsSource, ...]: return (PyprojectTomlConfigSettingsSource(settings_cls),) s = Settings() @@ -3751,12 +3753,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (TomlConfigSettingsSource(settings_cls, toml_file=[p1, p2]),) s = Settings() @@ -3785,12 +3787,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (YamlConfigSettingsSource(settings_cls, yaml_file=[p3, p4]),) s = Settings() @@ -3813,12 +3815,12 @@ @classmethod def settings_customise_sources( cls, - settings_cls: Type[BaseSettings], + settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, - ) -> Tuple[PydanticBaseSettingsSource, ...]: + ) -> tuple[PydanticBaseSettingsSource, ...]: return (JsonConfigSettingsSource(settings_cls, json_file=[p5, p6]),) s = Settings() @@ -3896,11 +3898,11 @@ def test_nested_models_as_dict_value(env): class NestedSettings(BaseModel): - foo: Dict[str, int] + foo: dict[str, int] class Settings(BaseSettings): nested: NestedSettings - sub_dict: Dict[str, NestedSettings] + sub_dict: dict[str, NestedSettings] model_config = SettingsConfigDict(env_nested_delimiter='__') @@ -3912,7 +3914,7 @@ def test_env_nested_dict_value(env): class Settings(BaseSettings): - nested: Dict[str, Dict[str, Dict[str, str]]] + nested: dict[str, dict[str, dict[str, str]]] model_config = SettingsConfigDict(env_nested_delimiter='__') @@ -3961,7 +3963,7 @@ def test_case_insensitive_nested_list(env): class NestedSettings(BaseModel): - FOO: List[str] + FOO: list[str] class Settings(BaseSettings): model_config = SettingsConfigDict(env_nested_delimiter='__', case_sensitive=False) ```
hramezani commented 3 months ago

Thanks @kloczek. I will consider these changes when I want to drop Python 3.8 support

kloczek commented 3 months ago

I understand 👍