commit 9d9b5072223e4276793a7c6005786cb87f96cb08 from: Jelmer Vernooij via: GitHub date: Thu Jun 30 16:14:06 2022 UTC Merge pull request #984 from dimbleby/more-typechecking more typechecking commit - bbf100b63fb00ebb4a1d0ca698b1abe337611bae commit + 9d9b5072223e4276793a7c6005786cb87f96cb08 blob - 19f540e69893a800afb90122765f0e02d003faf1 blob + 81feb78ee5f3402d748c351071d28da3886741c9 --- dulwich/client.py +++ dulwich/client.py @@ -2285,6 +2285,7 @@ def iter_instead_of(config: Config, push: bool = False except KeyError: pass for needle in needles: + assert isinstance(needle, bytes) yield needle.decode('utf-8'), replacement.decode('utf-8') blob - 30b67fb3173e2adf104c10ee6f23c8b697b8b6d4 blob + f979d3cdf036e519ffd79209a431c3736611ee0c --- dulwich/config.py +++ dulwich/config.py @@ -34,11 +34,13 @@ from typing import ( BinaryIO, Iterable, Iterator, + List, KeysView, MutableMapping, Optional, Tuple, Union, + overload, ) from dulwich.file import GitFile @@ -137,14 +139,21 @@ class CaseInsensitiveOrderedMultiDict(MutableMapping): return self[key] +BytesLike = Union[bytes, str] +Key = Tuple[bytes, ...] +KeyLike = Union[bytes, str, Tuple[BytesLike, ...]] +Value = Union[bytes, bool] +ValueLike = Union[bytes, str, bool] + + class Config(object): """A Git configuration.""" - def get(self, section, name): + def get(self, section: KeyLike, name: BytesLike) -> Value: """Retrieve the contents of a configuration setting. Args: - section: Tuple with section name and optional subsection namee + section: Tuple with section name and optional subsection name name: Variable name Returns: Contents of the setting @@ -153,7 +162,7 @@ class Config(object): """ raise NotImplementedError(self.get) - def get_multivar(self, section, name): + def get_multivar(self, section: KeyLike, name: BytesLike) -> Iterator[Value]: """Retrieve the contents of a multivar configuration setting. Args: @@ -166,7 +175,12 @@ class Config(object): """ raise NotImplementedError(self.get_multivar) - def get_boolean(self, section, name, default=None): + @overload + def get_boolean(self, section: KeyLike, name: BytesLike, default: bool) -> bool: ... + @overload + def get_boolean(self, section: KeyLike, name: BytesLike) -> Optional[bool]: ... + + def get_boolean(self, section: KeyLike, name: BytesLike, default: Optional[bool] = None) -> Optional[bool]: """Retrieve a configuration setting as boolean. Args: @@ -175,20 +189,20 @@ class Config(object): subsection. Returns: Contents of the setting - Raises: - KeyError: if the value is not set """ try: value = self.get(section, name) except KeyError: return default + if isinstance(value, bool): + return value if value.lower() == b"true": return True elif value.lower() == b"false": return False raise ValueError("not a valid boolean string: %r" % value) - def set(self, section, name, value): + def set(self, section: KeyLike, name: BytesLike, value: ValueLike) -> None: """Set a configuration value. Args: @@ -199,7 +213,7 @@ class Config(object): """ raise NotImplementedError(self.set) - def items(self, section): + def items(self, section: KeyLike) -> Iterator[Tuple[bytes, Value]]: """Iterate over the configuration pairs for a specific section. Args: @@ -209,7 +223,7 @@ class Config(object): """ raise NotImplementedError(self.items) - def iteritems(self, section): + def iteritems(self, section: KeyLike) -> Iterator[Tuple[bytes, Value]]: """Iterate over the configuration pairs for a specific section. Args: @@ -224,7 +238,7 @@ class Config(object): ) return self.items(section) - def itersections(self): + def itersections(self) -> Iterator[Key]: warnings.warn( "Use %s.items instead." % type(self).__name__, DeprecationWarning, @@ -232,14 +246,14 @@ class Config(object): ) return self.sections() - def sections(self): + def sections(self) -> Iterator[Key]: """Iterate over the sections. Returns: Iterator over section tuples """ raise NotImplementedError(self.sections) - def has_section(self, name: Tuple[bytes, ...]) -> bool: + def has_section(self, name: Key) -> bool: """Check if a specified section exists. Args: @@ -250,13 +264,6 @@ class Config(object): return name in self.sections() -BytesLike = Union[bytes, str] -Key = Tuple[bytes, ...] -KeyLike = Union[bytes, str, Tuple[BytesLike, ...]] -Value = Union[bytes, bool] -ValueLike = Union[bytes, str, bool] - - class ConfigDict(Config, MutableMapping[Key, MutableMapping[bytes, Value]]): """Git configuration stored in a dictionary.""" @@ -299,7 +306,7 @@ class ConfigDict(Config, MutableMapping[Key, MutableMa return self._values.__len__() @classmethod - def _parse_setting(cls, name): + def _parse_setting(cls, name: str): parts = name.split(".") if len(parts) == 3: return (parts[0], parts[1], parts[2]) @@ -347,7 +354,7 @@ class ConfigDict(Config, MutableMapping[Key, MutableMa self, section: KeyLike, name: BytesLike, - ) -> Optional[Value]: + ) -> Value: section, name = self._check_section_and_name(section, name) if len(section) > 1: @@ -374,7 +381,7 @@ class ConfigDict(Config, MutableMapping[Key, MutableMa def items( # type: ignore[override] self, section: Key - ) -> Iterator[Value]: + ) -> Iterator[Tuple[bytes, Value]]: return self._values.get(section).items() def sections(self) -> Iterator[Key]: @@ -405,7 +412,7 @@ _COMMENT_CHARS = [ord(b"#"), ord(b";")] _WHITESPACE_CHARS = [ord(b"\t"), ord(b" ")] -def _parse_string(value): +def _parse_string(value: bytes) -> bytes: value = bytearray(value.strip()) ret = bytearray() whitespace = bytearray() @@ -450,7 +457,7 @@ def _parse_string(value): return bytes(ret) -def _escape_value(value): +def _escape_value(value: bytes) -> bytes: """Escape a value.""" value = value.replace(b"\\", b"\\\\") value = value.replace(b"\n", b"\\n") @@ -459,7 +466,7 @@ def _escape_value(value): return value -def _check_variable_name(name): +def _check_variable_name(name: bytes) -> bool: for i in range(len(name)): c = name[i : i + 1] if not c.isalnum() and c != b"-": @@ -467,7 +474,7 @@ def _check_variable_name(name): return True -def _check_section_name(name): +def _check_section_name(name: bytes) -> bool: for i in range(len(name)): c = name[i : i + 1] if not c.isalnum() and c not in (b"-", b"."): @@ -475,7 +482,7 @@ def _check_section_name(name): return True -def _strip_comments(line): +def _strip_comments(line: bytes) -> bytes: comment_bytes = {ord(b"#"), ord(b";")} quote = ord(b'"') string_open = False @@ -492,9 +499,15 @@ def _strip_comments(line): class ConfigFile(ConfigDict): """A Git configuration file, like .git/config or ~/.gitconfig.""" - def __init__(self, values=None, encoding=None): + def __init__( + self, + values: Union[ + MutableMapping[Key, MutableMapping[bytes, Value]], None + ] = None, + encoding: Union[str, None] = None + ) -> None: super(ConfigFile, self).__init__(values=values, encoding=encoding) - self.path = None + self.path: Optional[str] = None @classmethod # noqa: C901 def from_file(cls, f: BinaryIO) -> "ConfigFile": # noqa: C901 @@ -565,14 +578,14 @@ class ConfigFile(ConfigDict): return ret @classmethod - def from_path(cls, path) -> "ConfigFile": + def from_path(cls, path: str) -> "ConfigFile": """Read configuration from a file on disk.""" with GitFile(path, "rb") as f: ret = cls.from_file(f) ret.path = path return ret - def write_to_path(self, path=None) -> None: + def write_to_path(self, path: Optional[str] = None) -> None: """Write configuration to a file on disk.""" if path is None: path = self.path @@ -663,19 +676,21 @@ def get_win_system_paths(): class StackedConfig(Config): """Configuration which reads from multiple config files..""" - def __init__(self, backends, writable=None): + def __init__( + self, backends: List[ConfigFile], writable: Optional[ConfigFile] = None + ): self.backends = backends self.writable = writable - def __repr__(self): + def __repr__(self) -> str: return "<%s for %r>" % (self.__class__.__name__, self.backends) @classmethod - def default(cls): + def default(cls) -> "StackedConfig": return cls(cls.default_backends()) @classmethod - def default_backends(cls): + def default_backends(cls) -> List[ConfigFile]: """Retrieve the default configuration. See git-config(1) for details on the files searched. @@ -698,7 +713,7 @@ class StackedConfig(Config): backends.append(cf) return backends - def get(self, section, name): + def get(self, section: KeyLike, name: BytesLike) -> Value: if not isinstance(section, tuple): section = (section,) for backend in self.backends: @@ -708,7 +723,7 @@ class StackedConfig(Config): pass raise KeyError(name) - def get_multivar(self, section, name): + def get_multivar(self, section: KeyLike, name: BytesLike) -> Iterator[Value]: if not isinstance(section, tuple): section = (section,) for backend in self.backends: @@ -717,12 +732,12 @@ class StackedConfig(Config): except KeyError: pass - def set(self, section, name, value): + def set(self, section: KeyLike, name: BytesLike, value: ValueLike) -> None: if self.writable is None: raise NotImplementedError(self.set) return self.writable.set(section, name, value) - def sections(self): + def sections(self) -> Iterator[Key]: seen = set() for backend in self.backends: for section in backend.sections(): blob - 55a821f46c14bf7e8392d1d7d4ca85dd77cf3551 blob + 1831fe7d0266f3e7414db8ddae86af302fa42e1b --- dulwich/ignore.py +++ dulwich/ignore.py @@ -286,7 +286,9 @@ def default_user_ignore_filter_path(config: Config) -> Path to a global ignore file """ try: - return config.get((b"core",), b"excludesFile") + value = config.get((b"core",), b"excludesFile") + assert isinstance(value, bytes) + return value.decode(encoding="utf-8") except KeyError: pass blob - 80c2ad575723a1452ebcc6bd4b34a2fe267fc7e1 blob + 396069b9de456f1bde341145a311b23b46130da8 --- dulwich/repo.py +++ dulwich/repo.py @@ -195,12 +195,16 @@ def get_user_identity(config: "StackedConfig", kind: O email = email_uc.encode("utf-8") if user is None: try: - user = config.get(("user",), "name") + config_value = config.get(("user",), "name") + assert isinstance(config_value, bytes) + user = config_value except KeyError: user = None if email is None: try: - email = config.get(("user",), "email") + config_value = config.get(("user",), "email") + assert isinstance(config_value, bytes) + email = config_value except KeyError: email = None default_user, default_email = _get_default_identity()