Skip to content

Commit 5f9a0bf

Browse files
committed
Fix handle type alias declared with type statement
1 parent 6bae3ab commit 5f9a0bf

File tree

8 files changed

+71
-6
lines changed

8 files changed

+71
-6
lines changed

pydantic_settings/sources/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
)
1616
from pydantic._internal._utils import is_model_class
1717
from pydantic.fields import FieldInfo
18-
from typing_extensions import get_args
1918
from typing_inspection.introspection import is_union_origin
2019

2120
from ..exceptions import SettingsError
@@ -26,6 +25,7 @@
2625
_get_alias_names,
2726
_get_model_fields,
2827
_union_is_complex,
28+
get_args,
2929
)
3030

3131
if TYPE_CHECKING:

pydantic_settings/sources/providers/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
from pydantic.dataclasses import is_pydantic_dataclass
4242
from pydantic.fields import FieldInfo
4343
from pydantic_core import PydanticUndefined
44-
from typing_extensions import get_args, get_origin
4544
from typing_inspection import typing_objects
4645
from typing_inspection.introspection import is_union_origin
4746

@@ -55,6 +54,8 @@
5554
_get_model_fields,
5655
_is_function,
5756
_strip_annotated,
57+
get_args,
58+
get_origin,
5859
parse_env_vars,
5960
)
6061
from .env import EnvSettingsSource

pydantic_settings/sources/providers/env.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from pydantic._internal._utils import deep_update, is_model_class
1111
from pydantic.dataclasses import is_pydantic_dataclass
1212
from pydantic.fields import FieldInfo
13-
from typing_extensions import get_args, get_origin
1413
from typing_inspection.introspection import is_union_origin
1514

1615
from ...utils import _lenient_issubclass
@@ -20,6 +19,8 @@
2019
_annotation_enum_name_to_val,
2120
_get_model_fields,
2221
_union_is_complex,
22+
get_args,
23+
get_origin,
2324
parse_env_vars,
2425
)
2526

pydantic_settings/sources/utils.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,42 @@
22

33
from __future__ import annotations as _annotations
44

5+
import sys
56
from collections import deque
67
from collections.abc import Mapping, Sequence
78
from dataclasses import is_dataclass
89
from enum import Enum
910
from typing import Any, Optional, cast
1011

12+
import typing_extensions
1113
from pydantic import BaseModel, Json, RootModel, Secret
1214
from pydantic._internal._utils import is_model_class
1315
from pydantic.dataclasses import is_pydantic_dataclass
14-
from typing_extensions import get_args, get_origin
1516
from typing_inspection import typing_objects
1617

1718
from ..exceptions import SettingsError
1819
from ..utils import _lenient_issubclass
1920
from .types import EnvNoneType
2021

22+
if sys.version_info >= (3, 12):
23+
from typing import TypeAliasType
24+
25+
def get_origin(annotation: Any | None) -> Any | None:
26+
if isinstance(annotation, TypeAliasType):
27+
annotation = annotation.__value__
28+
29+
return typing_extensions.get_origin(annotation)
30+
31+
def get_args(annotation: Any | None) -> Any:
32+
if isinstance(annotation, TypeAliasType):
33+
annotation = annotation.__value__
34+
35+
return typing_extensions.get_args(annotation)
36+
37+
else:
38+
get_origin = typing_extensions.get_origin
39+
get_args = typing_extensions.get_args
40+
2141

2242
def _get_env_var_key(key: str, case_sensitive: bool = False) -> str:
2343
return key if case_sensitive else key.lower()

pydantic_settings/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
from pathlib import Path
44
from typing import Any, _GenericAlias # type: ignore [attr-defined]
55

6-
from typing_extensions import get_origin
7-
86
_PATH_TYPE_LABELS = {
97
Path.is_dir: 'directory',
108
Path.is_file: 'file',
@@ -35,6 +33,8 @@ def _lenient_issubclass(cls: Any, class_or_tuple: Any) -> bool: # pragma: no co
3533
try:
3634
return isinstance(cls, type) and issubclass(cls, class_or_tuple)
3735
except TypeError:
36+
from pydantic_settings.sources.utils import get_origin
37+
3838
if get_origin(cls) is not None:
3939
# Up until Python 3.10, isinstance(<generic_alias>, type) is True
4040
# (e.g. list[int])

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ pydocstyle = { convention = 'google' }
143143
[tool.ruff.format]
144144
quote-style = 'single'
145145

146+
[tool.ruff.per-file-target-version]
147+
"tests/test_settings_p312.py" = "py312"
148+
146149
[tool.mypy]
147150
python_version = '3.10'
148151
show_error_codes = true

tests/conftest.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import os
4+
import sys
45
from pathlib import Path
56
from typing import TYPE_CHECKING
67

@@ -99,3 +100,19 @@ def cli_test_env():
99100
yield setenv
100101

101102
setenv.clear()
103+
104+
105+
def pytest_ignore_collect(collection_path: Path, config) -> bool:
106+
"""
107+
Ignore collection of test files that contain syntax unsupported
108+
by the current Python version.
109+
"""
110+
p312_tests = {'test_settings_p312.py'}
111+
112+
if sys.version_info < (3, 12) and collection_path.name in p312_tests:
113+
print(
114+
f"\nSkipping collection of '{collection_path.name}' on Python {sys.version_info.major}.{sys.version_info.minor} due to syntax requirements."
115+
)
116+
return True
117+
118+
return None

tests/test_settings_p312.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from typing import Annotated
2+
3+
from annotated_types import MinLen
4+
5+
from pydantic_settings import (
6+
BaseSettings,
7+
)
8+
9+
try:
10+
import dotenv
11+
except ImportError:
12+
dotenv = None
13+
14+
15+
def test_annotated_with_type(env):
16+
type MinLenList = Annotated[list[str], MinLen(2)]
17+
18+
class AnnotatedComplexSettings(BaseSettings):
19+
apples: MinLenList
20+
21+
env.set('apples', '["russet", "granny smith"]')
22+
s = AnnotatedComplexSettings()
23+
assert s.apples == ['russet', 'granny smith']

0 commit comments

Comments
 (0)