mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
fix: support properly path type (#2801)
* feat: add `StrPath` type * fix: support properly path type * add test * fix: merge * remove useless if statement * fix: cython Co-authored-by: PrettyWood <em.jolibois@gmail.com>
This commit is contained in:
+12
-10
@@ -6,7 +6,7 @@ from typing import AbstractSet, Any, Callable, Dict, List, Mapping, Optional, Tu
|
||||
from .config import BaseConfig, Extra
|
||||
from .fields import ModelField
|
||||
from .main import BaseModel
|
||||
from .typing import display_as_type
|
||||
from .typing import StrPath, display_as_type
|
||||
from .utils import deep_update, path_type, sequence_like
|
||||
|
||||
env_file_sentinel = str(object())
|
||||
@@ -28,9 +28,9 @@ class BaseSettings(BaseModel):
|
||||
|
||||
def __init__(
|
||||
__pydantic_self__,
|
||||
_env_file: Union[Path, str, None] = env_file_sentinel,
|
||||
_env_file: Optional[StrPath] = env_file_sentinel,
|
||||
_env_file_encoding: Optional[str] = None,
|
||||
_secrets_dir: Union[Path, str, None] = None,
|
||||
_secrets_dir: Optional[StrPath] = None,
|
||||
**values: Any,
|
||||
) -> None:
|
||||
# Uses something other than `self` the first arg to allow "self" as a settable attribute
|
||||
@@ -43,9 +43,9 @@ class BaseSettings(BaseModel):
|
||||
def _build_values(
|
||||
self,
|
||||
init_kwargs: Dict[str, Any],
|
||||
_env_file: Union[Path, str, None] = None,
|
||||
_env_file: Optional[StrPath] = None,
|
||||
_env_file_encoding: Optional[str] = None,
|
||||
_secrets_dir: Union[Path, str, None] = None,
|
||||
_secrets_dir: Optional[StrPath] = None,
|
||||
) -> Dict[str, Any]:
|
||||
# Configure built-in sources
|
||||
init_settings = InitSettingsSource(init_kwargs=init_kwargs)
|
||||
@@ -133,8 +133,8 @@ class InitSettingsSource:
|
||||
class EnvSettingsSource:
|
||||
__slots__ = ('env_file', 'env_file_encoding')
|
||||
|
||||
def __init__(self, env_file: Union[Path, str, None], env_file_encoding: Optional[str]):
|
||||
self.env_file: Union[Path, str, None] = env_file
|
||||
def __init__(self, env_file: Optional[StrPath], env_file_encoding: Optional[str]):
|
||||
self.env_file: Optional[StrPath] = env_file
|
||||
self.env_file_encoding: Optional[str] = env_file_encoding
|
||||
|
||||
def __call__(self, settings: BaseSettings) -> Dict[str, Any]:
|
||||
@@ -183,8 +183,8 @@ class EnvSettingsSource:
|
||||
class SecretsSettingsSource:
|
||||
__slots__ = ('secrets_dir',)
|
||||
|
||||
def __init__(self, secrets_dir: Union[Path, str, None]):
|
||||
self.secrets_dir: Union[Path, str, None] = secrets_dir
|
||||
def __init__(self, secrets_dir: Optional[StrPath]):
|
||||
self.secrets_dir: Optional[StrPath] = secrets_dir
|
||||
|
||||
def __call__(self, settings: BaseSettings) -> Dict[str, Any]:
|
||||
"""
|
||||
@@ -221,7 +221,9 @@ class SecretsSettingsSource:
|
||||
return f'SecretsSettingsSource(secrets_dir={self.secrets_dir!r})'
|
||||
|
||||
|
||||
def read_env_file(file_path: Path, *, encoding: str = None, case_sensitive: bool = False) -> Dict[str, Optional[str]]:
|
||||
def read_env_file(
|
||||
file_path: StrPath, *, encoding: str = None, case_sensitive: bool = False
|
||||
) -> Dict[str, Optional[str]]:
|
||||
try:
|
||||
from dotenv import dotenv_values
|
||||
except ImportError as e:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import sys
|
||||
from os import PathLike
|
||||
from typing import ( # type: ignore
|
||||
TYPE_CHECKING,
|
||||
AbstractSet,
|
||||
@@ -206,6 +207,17 @@ else:
|
||||
WithArgsTypes = (typing._GenericAlias, types.GenericAlias, types.UnionType)
|
||||
|
||||
|
||||
if sys.version_info < (3, 9):
|
||||
StrPath = Union[str, PathLike]
|
||||
else:
|
||||
StrPath = Union[str, PathLike]
|
||||
# TODO: Once we switch to Cython 3 to handle generics properly
|
||||
# (https://github.com/cython/cython/issues/2753), use following lines instead
|
||||
# of the one above
|
||||
# # os.PathLike only becomes subscriptable from Python 3.9 onwards
|
||||
# StrPath = Union[str, PathLike[str]]
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .fields import ModelField
|
||||
|
||||
@@ -256,6 +268,7 @@ __all__ = (
|
||||
'typing_base',
|
||||
'get_all_type_hints',
|
||||
'is_union_origin',
|
||||
'StrPath',
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ Test pydantic's compliance with mypy.
|
||||
Do a little skipping about with types to demonstrate its usage.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import date, datetime, timedelta
|
||||
from pathlib import Path
|
||||
from pathlib import Path, PurePath
|
||||
from typing import Any, Dict, Generic, List, Optional, TypeVar
|
||||
from uuid import UUID
|
||||
|
||||
@@ -14,6 +15,7 @@ from pydantic import (
|
||||
UUID1,
|
||||
BaseConfig,
|
||||
BaseModel,
|
||||
BaseSettings,
|
||||
DirectoryPath,
|
||||
Extra,
|
||||
FilePath,
|
||||
@@ -250,3 +252,20 @@ class Config(BaseConfig):
|
||||
title = 'Record'
|
||||
extra = Extra.ignore
|
||||
max_anystr_length = 1234
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
...
|
||||
|
||||
|
||||
class CustomPath(PurePath):
|
||||
def __init__(self, *args: str):
|
||||
self.path = os.path.join(*args)
|
||||
|
||||
def __fspath__(self) -> str:
|
||||
return f'a/custom/{self.path}'
|
||||
|
||||
|
||||
def dont_check_path_existence() -> None:
|
||||
Settings(_env_file='a/path', _secrets_dir='a/path')
|
||||
Settings(_env_file=CustomPath('a/path'), _secrets_dir=CustomPath('a/path'))
|
||||
|
||||
Reference in New Issue
Block a user