Skip to content

Commit

Permalink
fix merge property in rare cases
Browse files Browse the repository at this point in the history
Signed-off-by: Zhiyuan Chen <[email protected]>
  • Loading branch information
ZhiyuanChen committed Jan 4, 2024
1 parent 81420ad commit d2e3aad
Show file tree
Hide file tree
Showing 10 changed files with 49 additions and 59 deletions.
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ default_language_version:
python: python3
repos:
- repo: https://github.com/PSF/black
rev: 23.11.0
rev: 23.12.1
hooks:
- id: black
args: [--safe, --quiet]
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
rev: 5.13.2
hooks:
- id: isort
name: isort
Expand All @@ -25,11 +25,11 @@ repos:
- id: pyupgrade
args: [--keep-runtime-typing]
- repo: https://github.com/tox-dev/pyproject-fmt
rev: 1.5.1
rev: 1.5.3
hooks:
- id: pyproject-fmt
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.7.0
rev: v1.8.0
hooks:
- id: mypy
files: chanfig
Expand All @@ -41,7 +41,7 @@ repos:
- id: codespell
args: [--ignore-words=.codespell-whitelist.txt]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
rev: v4.0.0-alpha.8
hooks:
- id: prettier
files: chanfig
Expand Down
5 changes: 1 addition & 4 deletions chanfig/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@
from functools import wraps
from typing import Any

try:
from typing import Self
except ImportError:
from typing_extensions import Self
from typing_extensions import Self

from .nested_dict import NestedDict
from .parser import ConfigParser
Expand Down
36 changes: 16 additions & 20 deletions chanfig/flat_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@
from typing import IO, Any
from warnings import warn

try:
from typing import Self
except ImportError:
from typing_extensions import Self

from typing_extensions import Self
from yaml import dump as yaml_dump
from yaml import load as yaml_load

Expand Down Expand Up @@ -379,7 +375,7 @@ def getattr(self, name: str, default: Any = Null) -> Any:
return self.__dict__[name]
if name in self.__class__.__dict__:
return self.__class__.__dict__[name]
return super().getattr(name, default) # type: ignore
return super().getattr(name, default) # type: ignore[misc]
except AttributeError:
if default is not Null:
return default
Expand Down Expand Up @@ -466,7 +462,7 @@ def hasattr(self, name: str) -> bool:
try:
if name in self.__dict__ or name in self.__class__.__dict__:
return True
return super().hasattr(name) # type: ignore
return super().hasattr(name) # type: ignore[misc]
except AttributeError:
return False

Expand Down Expand Up @@ -519,7 +515,7 @@ def from_dict(cls, obj: Mapping | Sequence) -> Any: # pylint: disable=R0911
if obj is None:
return cls()
if issubclass(cls, FlatDict):
cls = cls.empty # type: ignore # pylint: disable=W0642
cls = cls.empty # type: ignore[assignment] # pylint: disable=W0642
if isinstance(obj, Mapping):
return cls(obj)
if isinstance(obj, Sequence):
Expand Down Expand Up @@ -710,7 +706,7 @@ def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self:
if len(args) == 1:
args = args[0]
if isinstance(args, (PathLike, str, bytes)):
args = self.load(args) # type: ignore
args = self.load(args) # type: ignore[assignment]
warn(
"merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.",
PendingDeprecationWarning,
Expand Down Expand Up @@ -850,7 +846,7 @@ def difference(self, other: Mapping | Iterable | PathStr) -> Self:
if not isinstance(other, Iterable):
raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.")
return self.empty(
**{key: value for key, value in other if key not in self or self[key] != value} # type: ignore
**{key: value for key, value in other if key not in self or self[key] != value} # type: ignore[misc]
)

def diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:
Expand Down Expand Up @@ -1029,7 +1025,7 @@ def clone(self, memo: Mapping | None = None) -> Self:
return self.deepcopy(memo=memo)

def save( # pylint: disable=W1113
self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore
self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment]
) -> None:
r"""
Save `FlatDict` to file.
Expand Down Expand Up @@ -1060,13 +1056,13 @@ def save( # pylint: disable=W1113
method = splitext(file)[-1][1:]
extension = method.lower()
if extension in YAML:
return self.yaml(file=file, *args, **kwargs) # type: ignore
return self.yaml(file=file, *args, **kwargs) # type: ignore[misc] # noqa: B026
if extension in JSON:
return self.json(file=file, *args, **kwargs) # type: ignore
return self.json(file=file, *args, **kwargs) # type: ignore[misc] # noqa: B026
raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.")

def dump( # pylint: disable=W1113
self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore
self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment]
) -> None:
r"""
Alias of [`save`][chanfig.FlatDict.save].
Expand All @@ -1075,7 +1071,7 @@ def dump( # pylint: disable=W1113

@classmethod
def load( # pylint: disable=W1113
cls, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore
cls, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment]
) -> Self:
"""
Load `FlatDict` from file.
Expand Down Expand Up @@ -1149,7 +1145,7 @@ def from_json(cls, file: File, *args: Any, **kwargs: Any) -> Self:

with cls.open(file) as fp: # pylint: disable=C0103
if isinstance(file, (IOBase, IO)):
return cls.from_jsons(fp.getvalue(), *args, **kwargs) # type: ignore
return cls.from_jsons(fp.getvalue(), *args, **kwargs) # type: ignore[union-attr]
return cls.from_jsons(fp.read(), *args, **kwargs)

def jsons(self, *args: Any, **kwargs: Any) -> str:
Expand Down Expand Up @@ -1222,7 +1218,7 @@ def from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> Self:
kwargs.setdefault("Loader", YamlLoader)
with cls.open(file) as fp: # pylint: disable=C0103
if isinstance(file, (IOBase, IO)):
return cls.from_yamls(fp.getvalue(), *args, **kwargs) # type: ignore
return cls.from_yamls(fp.getvalue(), *args, **kwargs) # type: ignore[union-attr]
return cls.from_dict(yaml_load(fp, *args, **kwargs))

def yamls(self, *args: Any, **kwargs: Any) -> str:
Expand Down Expand Up @@ -1304,11 +1300,11 @@ def open(file: File, *args: Any, encoding: str = "utf-8", **kwargs: Any) -> Gene
yield file
elif isinstance(file, (PathLike, str, bytes)):
try:
file = open(file, *args, encoding=encoding, **kwargs) # type: ignore # noqa: SIM115
yield file # type: ignore
file = open(file, *args, encoding=encoding, **kwargs) # type: ignore[call-overload] # noqa: SIM115
yield file # type: ignore[misc]
finally:
with suppress(Exception):
file.close() # type: ignore
file.close() # type: ignore[union-attr]
else:
raise TypeError(f"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}")

Expand Down
4 changes: 2 additions & 2 deletions chanfig/functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@


def save( # pylint: disable=W1113
obj, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore
obj, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment]
) -> None:
r"""
Save `FlatDict` to file.
Expand Down Expand Up @@ -62,7 +62,7 @@ def save( # pylint: disable=W1113
if method is None:
if isinstance(file, IOBase):
raise ValueError("`method` must be specified when saving to IO.")
method = splitext(file)[-1][1:] # type: ignore
method = splitext(file)[-1][1:]
extension = method.lower()
if extension in YAML:
with FlatDict.open(file, mode="w") as fp: # pylint: disable=C0103
Expand Down
21 changes: 10 additions & 11 deletions chanfig/nested_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,15 @@
from os import PathLike
from typing import Any

from typing_extensions import Self

try:
from functools import cached_property # pylint: disable=C0412
except ImportError:
try:
from backports.cached_property import cached_property # type: ignore
from backports.cached_property import cached_property # type: ignore[no-redef]
except ImportError:
cached_property = property # type: ignore # pylint: disable=C0103

try:
from typing import Self
except ImportError:
from typing_extensions import Self
cached_property = property # type: ignore[misc, assignment] # pylint: disable=C0103

from .default_dict import DefaultDict
from .flat_dict import FlatDict
Expand Down Expand Up @@ -698,8 +695,7 @@ def _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping:
return this
if isinstance(that, Mapping):
that = that.items()
context = this.converting() if isinstance(this, NestedDict) else nullcontext()
with context:
with this.converting() if isinstance(this, NestedDict) else nullcontext():
for key, value in that:
if key in this and isinstance(this[key], Mapping):
if isinstance(value, Mapping):
Expand All @@ -709,8 +705,11 @@ def _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping:
this.set(key, value)
else:
this[key] = value
elif key in dir(this) and isinstance(getattr(this.__class__, key), (property, cached_property)):
getattr(this, key).merge(value, overwrite=overwrite)
elif key in dir(this) and isinstance(getattr(this.__class__, key, None), (property, cached_property)):
if isinstance(getattr(this, key, None), FlatDict):
getattr(this, key).merge(value, overwrite=overwrite)
else:
setattr(this, key, value)
elif overwrite or key not in this:
if isinstance(this, NestedDict):
this.set(key, value)
Expand Down
4 changes: 2 additions & 2 deletions chanfig/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ def build(self, name: str | Mapping, *args: Any, **kwargs: Any) -> Any:

if isinstance(name, Mapping):
name = deepcopy(name)
name, kwargs = name.pop("name"), dict(name, **kwargs) # type: ignore
return self.init(self.lookup(name), *args, **kwargs) # type: ignore
name, kwargs = name.pop("name"), dict(name, **kwargs) # type: ignore[attr-defined, arg-type]
return self.init(self.lookup(name), *args, **kwargs) # type: ignore[arg-type]


GlobalRegistry = Registry()
23 changes: 10 additions & 13 deletions chanfig/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,17 @@
from re import compile, findall # pylint: disable=W0622
from types import GetSetDescriptorType, ModuleType
from typing import IO, Any, Union, no_type_check
from typing_extensions import get_args, get_origin

import typing_extensions
from yaml import SafeDumper, SafeLoader

try: # python 3.8+
import typing_extensions as typing
except ImportError:
import typing # type: ignore

try: # python 3.10+
from types import UnionType # pylint: disable=C0412
except ImportError:
UnionType = Union # type: ignore
UnionType = Union # type: ignore[misc, assignment]

GLOBAL_NS = {k: v for k, v in typing.__dict__.items() if not k.startswith("_")}
GLOBAL_NS = {k: v for k, v in typing_extensions.__dict__.items() if not k.startswith("_")}
PY310_PLUS = sys.version_info >= (3, 10)

PathStr = Union[PathLike, str, bytes]
Expand Down Expand Up @@ -191,7 +188,7 @@ def get_annotations( # pylint: disable=all

@no_type_check
def isvalid(data: Any, expected_type: type) -> bool:
expected_origin = typing.get_origin(expected_type)
expected_origin = get_origin(expected_type)
if expected_origin not in (
Callable,
GetSetDescriptorType,
Expand All @@ -200,20 +197,20 @@ def isvalid(data: Any, expected_type: type) -> bool:
None,
):
if issubclass(expected_origin, Sequence):
inner_type = typing.get_args(expected_type)[0]
inner_type = get_args(expected_type)[0]
return isinstance(data, expected_origin) and all(isinstance(item, inner_type) for item in data)
if issubclass(expected_origin, Mapping):
key_type, value_type = typing.get_args(expected_type)
key_type, value_type = get_args(expected_type)
return isinstance(data, expected_origin) and all(
isinstance(key, key_type) and isinstance(value, value_type) for key, value in data.items()
)
raise TypeError(f"Expected type {expected_type} is not supported.")
if expected_origin is UnionType and not PY310_PLUS:
return any(isinstance(data, inner_type) for inner_type in typing.get_args(expected_type))
return any(isinstance(data, inner_type) for inner_type in get_args(expected_type))
return isinstance(data, expected_type)


class Dict(type(dict)): # type: ignore
class Dict(type(dict)): # type: ignore[misc]
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
instance = super().__call__(*args, **kwargs)
instance.__post_init__()
Expand All @@ -230,7 +227,7 @@ class Singleton(type):

def __call__(cls, *args: Any, **kwargs: Any):
if cls not in cls.__instances__:
cls.__instances__[cls] = super().__call__(*args, **kwargs) # type: ignore
cls.__instances__[cls] = super().__call__(*args, **kwargs) # type: ignore[index]
return cls.__instances__[cls]


Expand Down
2 changes: 1 addition & 1 deletion chanfig/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def __init__( # pylint: disable=R0913
self._required = required
self._help = help

@property # type: ignore
@property # type: ignore[misc]
def __class__(self) -> type:
return self.value.__class__ if self.wrap_type else type(self)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ dynamic = [
]
dependencies = [
"pyyaml",
'typing-extensions; python_version < "3.8"',
"typing-extensions",
]
[project.optional-dependencies]
include = [
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pyyaml
typing-extensions

0 comments on commit d2e3aad

Please sign in to comment.