From 09cf4aba6279848e99874ac552660c7108b6ae50 Mon Sep 17 00:00:00 2001 From: Eddie Aftandilian Date: Tue, 10 Jan 2023 21:37:56 -0800 Subject: [PATCH] Fix type hint for dotenv_path var, add StrPath alias (#432) * Fix type hint for load_dotenv Fixes #431 * Quote type hints to avoid runtime errors in earlier Python versions * Revise type of dotenv_path parameter Based on PR feedback and typeshed's type hint for the built-in open() function: https://github.com/python/typeshed/blob/e2d67bf7034f68c07bd35150247e58e0817725d9/stdlib/builtins.pyi#L1421 * Allow only string paths, not byte paths These paths can flow into `shutil.move`, which does not accept byte paths or (int) file descriptors. See https://github.com/python/typeshed/pull/6832 * Create a type alias for the paths this library accepts And use it consistently in main.py. --- src/dotenv/main.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/dotenv/main.py b/src/dotenv/main.py index 36ed94f5..f40c20ea 100644 --- a/src/dotenv/main.py +++ b/src/dotenv/main.py @@ -12,6 +12,12 @@ from .parser import Binding, parse_stream from .variables import parse_variables +# A type alias for a string path to be used for the paths in this file. +# These paths may flow to `open()` and `shutil.move()`; `shutil.move()` +# only accepts string paths, not byte paths or file descriptors. See +# https://github.com/python/typeshed/pull/6832. +StrPath = Union[str, 'os.PathLike[str]'] + logger = logging.getLogger(__name__) @@ -28,14 +34,14 @@ def with_warn_for_invalid_lines(mappings: Iterator[Binding]) -> Iterator[Binding class DotEnv: def __init__( self, - dotenv_path: Optional[Union[str, os.PathLike]], + dotenv_path: Optional[StrPath], stream: Optional[IO[str]] = None, verbose: bool = False, encoding: Optional[str] = None, interpolate: bool = True, override: bool = True, ) -> None: - self.dotenv_path: Optional[Union[str, os.PathLike]] = dotenv_path + self.dotenv_path: Optional[StrPath] = dotenv_path self.stream: Optional[IO[str]] = stream self._dict: Optional[Dict[str, Optional[str]]] = None self.verbose: bool = verbose @@ -108,7 +114,7 @@ def get(self, key: str) -> Optional[str]: def get_key( - dotenv_path: Union[str, os.PathLike], + dotenv_path: StrPath, key_to_get: str, encoding: Optional[str] = "utf-8", ) -> Optional[str]: @@ -122,7 +128,7 @@ def get_key( @contextmanager def rewrite( - path: Union[str, os.PathLike], + path: StrPath, encoding: Optional[str], ) -> Iterator[Tuple[IO[str], IO[str]]]: if not os.path.isfile(path): @@ -139,7 +145,7 @@ def rewrite( def set_key( - dotenv_path: Union[str, os.PathLike], + dotenv_path: StrPath, key_to_set: str, value_to_set: str, quote_mode: str = "always", @@ -188,7 +194,7 @@ def set_key( def unset_key( - dotenv_path: Union[str, os.PathLike], + dotenv_path: StrPath, key_to_unset: str, quote_mode: str = "always", encoding: Optional[str] = "utf-8", @@ -303,7 +309,7 @@ def _is_interactive(): def load_dotenv( - dotenv_path: Union[str, os.PathLike, None] = None, + dotenv_path: Optional[StrPath] = None, stream: Optional[IO[str]] = None, verbose: bool = False, override: bool = False, @@ -341,7 +347,7 @@ def load_dotenv( def dotenv_values( - dotenv_path: Union[str, os.PathLike, None] = None, + dotenv_path: Optional[StrPath] = None, stream: Optional[IO[str]] = None, verbose: bool = False, interpolate: bool = True,