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:
Kyungmin Lee
2021-09-04 19:40:53 +09:00
committed by GitHub
parent d1a9ba53fa
commit f404aa25e8
3 changed files with 45 additions and 11 deletions
+12 -10
View File
@@ -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:
+13
View File
@@ -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',
)
+20 -1
View File
@@ -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'))