From 16f0607ed29f20c09e89f2cacc0e28e982309d60 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Mon, 5 Jul 2021 12:31:00 +0100 Subject: Improve typing of config_levels, add assert_never() --- git/config.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index 5c5ceea8..4cb13bdf 100644 --- a/git/config.py +++ b/git/config.py @@ -31,9 +31,10 @@ import configparser as cp # typing------------------------------------------------------- -from typing import Any, Callable, IO, List, Dict, Sequence, TYPE_CHECKING, Tuple, Union, cast, overload +from typing import (Any, Callable, IO, List, Dict, Sequence, + TYPE_CHECKING, Tuple, Union, cast, overload) -from git.types import Literal, Lit_config_levels, PathLike, TBD +from git.types import Lit_config_levels, ConfigLevels_Tup, ConfigLevels_NT, PathLike, TBD, assert_never if TYPE_CHECKING: from git.repo.base import Repo @@ -48,8 +49,9 @@ log.addHandler(logging.NullHandler()) # invariants # represents the configuration level of a configuration file -CONFIG_LEVELS = ("system", "user", "global", "repository" - ) # type: Tuple[Literal['system'], Literal['user'], Literal['global'], Literal['repository']] + + +CONFIG_LEVELS: ConfigLevels_Tup = ConfigLevels_NT("system", "user", "global", "repository") # Section pattern to detect conditional includes. # https://git-scm.com/docs/git-config#_conditional_includes @@ -229,8 +231,9 @@ def get_config_path(config_level: Lit_config_levels) -> str: return osp.normpath(osp.expanduser("~/.gitconfig")) elif config_level == "repository": raise ValueError("No repo to get repository configuration from. Use Repo._get_config_path") - - raise ValueError("Invalid configuration level: %r" % config_level) + else: + # Should not reach here. Will raise ValueError if does. Static typing will warn about extra and missing elifs + assert_never(config_level, ValueError("Invalid configuration level: %r" % config_level)) class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, object)): # type: ignore ## mypy does not understand dynamic class creation # noqa: E501 @@ -300,12 +303,12 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje self._proxies = self._dict() if file_or_files is not None: - self._file_or_files = file_or_files # type: Union[PathLike, IO, Sequence[Union[PathLike, IO]]] + self._file_or_files: Union[PathLike, IO, Sequence[Union[PathLike, IO]]] = file_or_files else: if config_level is None: if read_only: - self._file_or_files = [get_config_path(f) # type: ignore - for f in CONFIG_LEVELS # Can type f properly when 3.5 dropped + self._file_or_files = [get_config_path(f) + for f in CONFIG_LEVELS if f != 'repository'] else: raise ValueError("No configuration level or configuration files specified") @@ -323,15 +326,13 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje def _acquire_lock(self) -> None: if not self._read_only: if not self._lock: - if isinstance(self._file_or_files, (tuple, list)): - raise ValueError( - "Write-ConfigParsers can operate on a single file only, multiple files have been passed") - # END single file check - if isinstance(self._file_or_files, (str, os.PathLike)): file_or_files = self._file_or_files + elif isinstance(self._file_or_files, (tuple, list, Sequence)): + raise ValueError( + "Write-ConfigParsers can operate on a single file only, multiple files have been passed") else: - file_or_files = cast(IO, self._file_or_files).name + file_or_files = self._file_or_files.name # END get filename from handle/stream # initialize lock base - we want to write -- cgit v1.2.1 From 41e9781b640983cd3f38223e5b349eb299a0e4f6 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Mon, 5 Jul 2021 14:32:57 +0100 Subject: Improve BlameEntry.commit typing --- git/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index 4cb13bdf..6931dd12 100644 --- a/git/config.py +++ b/git/config.py @@ -34,7 +34,7 @@ import configparser as cp from typing import (Any, Callable, IO, List, Dict, Sequence, TYPE_CHECKING, Tuple, Union, cast, overload) -from git.types import Lit_config_levels, ConfigLevels_Tup, ConfigLevels_NT, PathLike, TBD, assert_never +from git.types import Lit_config_levels, ConfigLevels_Tup, PathLike, TBD, assert_never if TYPE_CHECKING: from git.repo.base import Repo @@ -51,7 +51,7 @@ log.addHandler(logging.NullHandler()) # represents the configuration level of a configuration file -CONFIG_LEVELS: ConfigLevels_Tup = ConfigLevels_NT("system", "user", "global", "repository") +CONFIG_LEVELS: ConfigLevels_Tup = ("system", "user", "global", "repository") # Section pattern to detect conditional includes. # https://git-scm.com/docs/git-config#_conditional_includes -- cgit v1.2.1 From 23b5d6b434551e1df1c954ab5d2c0166f080fba8 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Mon, 5 Jul 2021 15:42:46 +0100 Subject: Add types to submodule.util.py --- git/config.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index 6931dd12..bfdfd916 100644 --- a/git/config.py +++ b/git/config.py @@ -38,6 +38,7 @@ from git.types import Lit_config_levels, ConfigLevels_Tup, PathLike, TBD, assert if TYPE_CHECKING: from git.repo.base import Repo + from io import BytesIO # ------------------------------------------------------------- @@ -274,7 +275,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje # list of RawConfigParser methods able to change the instance _mutating_methods_ = ("add_section", "remove_section", "remove_option", "set") - def __init__(self, file_or_files: Union[None, PathLike, IO, Sequence[Union[PathLike, IO]]] = None, + def __init__(self, file_or_files: Union[None, PathLike, BytesIO, Sequence[Union[PathLike, BytesIO]]] = None, read_only: bool = True, merge_includes: bool = True, config_level: Union[Lit_config_levels, None] = None, repo: Union['Repo', None] = None) -> None: @@ -303,7 +304,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje self._proxies = self._dict() if file_or_files is not None: - self._file_or_files: Union[PathLike, IO, Sequence[Union[PathLike, IO]]] = file_or_files + self._file_or_files: Union[PathLike, 'BytesIO', Sequence[Union[PathLike, 'BytesIO']]] = file_or_files else: if config_level is None: if read_only: @@ -650,7 +651,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje a file lock""" self._assure_writable("write") if not self._dirty: - return + return None if isinstance(self._file_or_files, (list, tuple)): raise AssertionError("Cannot write back if there is not exactly a single file to write to, have %i files" @@ -675,7 +676,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje with open(fp, "wb") as fp_open: self._write(fp_open) else: - fp = cast(IO, fp) + fp = cast(BytesIO, fp) fp.seek(0) # make sure we do not overwrite into an existing file if hasattr(fp, 'truncate'): -- cgit v1.2.1 From c2317a768f4d6b72b9c20d4fbe455af8a0d77c36 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Mon, 5 Jul 2021 18:47:44 +0100 Subject: Make bytesIO forwardref --- git/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index bfdfd916..0ce3e831 100644 --- a/git/config.py +++ b/git/config.py @@ -275,7 +275,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje # list of RawConfigParser methods able to change the instance _mutating_methods_ = ("add_section", "remove_section", "remove_option", "set") - def __init__(self, file_or_files: Union[None, PathLike, BytesIO, Sequence[Union[PathLike, BytesIO]]] = None, + def __init__(self, file_or_files: Union[None, PathLike, 'BytesIO', Sequence[Union[PathLike, 'BytesIO']]] = None, read_only: bool = True, merge_includes: bool = True, config_level: Union[Lit_config_levels, None] = None, repo: Union['Repo', None] = None) -> None: @@ -667,7 +667,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje fp = self._file_or_files # we have a physical file on disk, so get a lock - is_file_lock = isinstance(fp, (str, IOBase)) # can't use Pathlike until 3.5 dropped + is_file_lock = isinstance(fp, (str, os.PathLike, IOBase)) # can't use Pathlike until 3.5 dropped if is_file_lock and self._lock is not None: # else raise Error? self._lock._obtain_lock() @@ -676,7 +676,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje with open(fp, "wb") as fp_open: self._write(fp_open) else: - fp = cast(BytesIO, fp) + fp = cast('BytesIO', fp) fp.seek(0) # make sure we do not overwrite into an existing file if hasattr(fp, 'truncate'): -- cgit v1.2.1 From 2e2fe186d09272c3cb6c96467fff362deb90994f Mon Sep 17 00:00:00 2001 From: Yobmod Date: Thu, 8 Jul 2021 11:30:16 +0100 Subject: Increase mypy strictness (no_implicit_optional & warn_redundant_casts) and fix errors --- git/config.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index 0ce3e831..19ce1f84 100644 --- a/git/config.py +++ b/git/config.py @@ -34,7 +34,7 @@ import configparser as cp from typing import (Any, Callable, IO, List, Dict, Sequence, TYPE_CHECKING, Tuple, Union, cast, overload) -from git.types import Lit_config_levels, ConfigLevels_Tup, PathLike, TBD, assert_never +from git.types import Lit_config_levels, ConfigLevels_Tup, PathLike, TBD, assert_never, is_config_level if TYPE_CHECKING: from git.repo.base import Repo @@ -54,6 +54,7 @@ log.addHandler(logging.NullHandler()) CONFIG_LEVELS: ConfigLevels_Tup = ("system", "user", "global", "repository") + # Section pattern to detect conditional includes. # https://git-scm.com/docs/git-config#_conditional_includes CONDITIONAL_INCLUDE_REGEXP = re.compile(r"(?<=includeIf )\"(gitdir|gitdir/i|onbranch):(.+)\"") @@ -310,7 +311,7 @@ class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, obje if read_only: self._file_or_files = [get_config_path(f) for f in CONFIG_LEVELS - if f != 'repository'] + if is_config_level(f) and f != 'repository'] else: raise ValueError("No configuration level or configuration files specified") else: -- cgit v1.2.1 From 937746291cfdaa40938de03db305b1137c391907 Mon Sep 17 00:00:00 2001 From: Yobmod Date: Fri, 9 Jul 2021 11:40:32 +0100 Subject: Make has_repo protocol runtime checkable and use in Diffable --- git/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'git/config.py') diff --git a/git/config.py b/git/config.py index 19ce1f84..2c863f93 100644 --- a/git/config.py +++ b/git/config.py @@ -234,8 +234,8 @@ def get_config_path(config_level: Lit_config_levels) -> str: elif config_level == "repository": raise ValueError("No repo to get repository configuration from. Use Repo._get_config_path") else: - # Should not reach here. Will raise ValueError if does. Static typing will warn about extra and missing elifs - assert_never(config_level, ValueError("Invalid configuration level: %r" % config_level)) + # Should not reach here. Will raise ValueError if does. Static typing will warn missing elifs + assert_never(config_level, ValueError(f"Invalid configuration level: {config_level!r}")) class GitConfigParser(with_metaclass(MetaParserBuilder, cp.RawConfigParser, object)): # type: ignore ## mypy does not understand dynamic class creation # noqa: E501 -- cgit v1.2.1