Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-immutabledict for openSUSE:Factory checked in at 2023-12-08 22:32:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-immutabledict (Old) and /work/SRC/openSUSE:Factory/.python-immutabledict.new.25432 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-immutabledict" Fri Dec 8 22:32:08 2023 rev:4 rq:1131733 version:4.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-immutabledict/python-immutabledict.changes 2023-08-10 15:35:03.544597330 +0200 +++ /work/SRC/openSUSE:Factory/.python-immutabledict.new.25432/python-immutabledict.changes 2023-12-08 22:32:39.381330942 +0100 @@ -1,0 +2,10 @@ +Thu Dec 7 22:34:25 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 4.0.0: + * Replace `__init__` by `__new__`. + * Add explicit items()/keys()/values() methods to speedup these + methods. + * Add set/delete/update functions. + * Add documentation at immutabledict.corenting.fr + +------------------------------------------------------------------- Old: ---- immutabledict-3.0.0.tar.gz New: ---- immutabledict-4.0.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-immutabledict.spec ++++++ --- /var/tmp/diff_new_pack.poZ2ye/_old 2023-12-08 22:32:39.861348604 +0100 +++ /var/tmp/diff_new_pack.poZ2ye/_new 2023-12-08 22:32:39.865348752 +0100 @@ -27,7 +27,7 @@ %define short_name immutabledict %{?sle15_python_module_pythons} Name: python-%{short_name}%{psuffix} -Version: 3.0.0 +Version: 4.0.0 Release: 0 Summary: Immutable wrapper around dictionaries (a fork of frozendict) License: MIT ++++++ immutabledict-3.0.0.tar.gz -> immutabledict-4.0.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/immutabledict-3.0.0/PKG-INFO new/immutabledict-4.0.0/PKG-INFO --- old/immutabledict-3.0.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/immutabledict-4.0.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: immutabledict -Version: 3.0.0 +Version: 4.0.0 Summary: Immutable wrapper around dictionaries (a fork of frozendict) Home-page: https://github.com/corenting/immutabledict License: MIT @@ -15,9 +15,12 @@ Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 Classifier: Topic :: Software Development :: Libraries :: Python Modules Project-URL: Bug Tracker, https://github.com/corenting/immutabledict/issues Project-URL: Changelog, https://github.com/corenting/immutabledict/blob/master/CHANGELOG.md +Project-URL: Documentation, https://immutabledict.corenting.fr +Project-URL: Donation, https://corenting.fr/donate Project-URL: Repository, https://github.com/corenting/immutabledict Description-Content-Type: text/markdown @@ -27,15 +30,13 @@ ![License](https://img.shields.io/pypi/l/immutabledict) ![Build](https://img.shields.io/github/actions/workflow/status/corenting/immutabledict/ci.yml?branch=master) ![Codecov](https://img.shields.io/codecov/c/github/corenting/immutabledict) ![PyPI - Downloads](https://img.shields.io/pypi/dm/immutabledict) -A fork of the original [frozendict](https://github.com/slezica/python-frozendict), an immutable wrapper around dictionaries. -This library is a pure Python, MIT-licensed alternative to the new LGPL-3.0 licensed [frozendict](https://github.com/Marco-Sulla/python-frozendict). +An immutable wrapper around dictionaries. immutabledict implements the complete mapping interface and can be used as a drop-in replacement for dictionaries where immutability is desired. -It implements the complete mapping interface and can be used as a drop-in replacement for dictionaries where immutability is desired. -The immutabledict constructor mimics dict, and all of the expected interfaces (iter, len, repr, hash, getitem) are provided. Note that an immutabledict does not guarantee the immutability of its values, so the utility of hash method is restricted by usage. +It's a fork of slezica's [frozendict](https://github.com/slezica/python-frozendict). This library is a pure Python, MIT-licensed alternative to the new LGPL-3.0 licensed [frozendict](https://github.com/Marco-Sulla/python-frozendict). ## Installation -Official release in [on pypy](https://pypi.org/project/immutabledict/) as `immutabledict`. +Official release in [on pypi](https://pypi.org/project/immutabledict/) as `immutabledict`. **Community-maintained** releases are available: - On [conda-forge](https://anaconda.org/conda-forge/immutabledict) as `immutabledict` @@ -58,3 +59,7 @@ - [PEP 584 union operators](https://www.python.org/dev/peps/pep-0584/) - Keep the same signature for `copy()` as `dict` (starting with immutabledict 3.0.0), don't accept extra keyword arguments. +## Donations + +If you wish to support the app, donations are possible [here](https://corenting.fr/donate). + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/immutabledict-3.0.0/README.md new/immutabledict-4.0.0/README.md --- old/immutabledict-3.0.0/README.md 2023-07-21 20:13:55.780401200 +0200 +++ new/immutabledict-4.0.0/README.md 2023-11-25 13:34:07.496336700 +0100 @@ -4,15 +4,13 @@ ![License](https://img.shields.io/pypi/l/immutabledict) ![Build](https://img.shields.io/github/actions/workflow/status/corenting/immutabledict/ci.yml?branch=master) ![Codecov](https://img.shields.io/codecov/c/github/corenting/immutabledict) ![PyPI - Downloads](https://img.shields.io/pypi/dm/immutabledict) -A fork of the original [frozendict](https://github.com/slezica/python-frozendict), an immutable wrapper around dictionaries. -This library is a pure Python, MIT-licensed alternative to the new LGPL-3.0 licensed [frozendict](https://github.com/Marco-Sulla/python-frozendict). +An immutable wrapper around dictionaries. immutabledict implements the complete mapping interface and can be used as a drop-in replacement for dictionaries where immutability is desired. -It implements the complete mapping interface and can be used as a drop-in replacement for dictionaries where immutability is desired. -The immutabledict constructor mimics dict, and all of the expected interfaces (iter, len, repr, hash, getitem) are provided. Note that an immutabledict does not guarantee the immutability of its values, so the utility of hash method is restricted by usage. +It's a fork of slezica's [frozendict](https://github.com/slezica/python-frozendict). This library is a pure Python, MIT-licensed alternative to the new LGPL-3.0 licensed [frozendict](https://github.com/Marco-Sulla/python-frozendict). ## Installation -Official release in [on pypy](https://pypi.org/project/immutabledict/) as `immutabledict`. +Official release in [on pypi](https://pypi.org/project/immutabledict/) as `immutabledict`. **Community-maintained** releases are available: - On [conda-forge](https://anaconda.org/conda-forge/immutabledict) as `immutabledict` @@ -34,3 +32,7 @@ - Typing - [PEP 584 union operators](https://www.python.org/dev/peps/pep-0584/) - Keep the same signature for `copy()` as `dict` (starting with immutabledict 3.0.0), don't accept extra keyword arguments. + +## Donations + +If you wish to support the app, donations are possible [here](https://corenting.fr/donate). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/immutabledict-3.0.0/immutabledict/__init__.py new/immutabledict-4.0.0/immutabledict/__init__.py --- old/immutabledict-3.0.0/immutabledict/__init__.py 2023-07-21 20:13:55.780401200 +0200 +++ new/immutabledict-4.0.0/immutabledict/__init__.py 2023-11-25 13:34:07.496336700 +0100 @@ -1,33 +1,49 @@ +"""Implementation of the :class:`immutabledict` and :class:`ImmutableOrderedDict` classes.""" from __future__ import annotations from collections import OrderedDict -from typing import Any, Dict, Iterable, Iterator, Mapping, Optional, Type, TypeVar +from typing import ( + Any, + Dict, + ItemsView, + Iterable, + Iterator, + KeysView, + Mapping, + Optional, + Type, + TypeVar, + ValuesView, +) -__version__ = "3.0.0" +__version__ = "4.0.0" _K = TypeVar("_K") _V = TypeVar("_V", covariant=True) -class immutabledict(Mapping[_K, _V]): +class immutabledict(Mapping[_K, _V]): # noqa: N801 """ - An immutable wrapper around dictionaries that implements - the complete :py:class:`collections.Mapping` interface. - It can be used as a drop-in replacement for dictionaries - where immutability is desired. + An immutable wrapper around dictionaries that implements the complete :py:class:`collections.Mapping` interface. + + It can be used as a drop-in replacement for dictionaries where immutability is desired. """ - dict_cls: Type[Dict[Any, Any]] = dict + _dict_cls: Type[Dict[Any, Any]] = dict + _dict: Dict[_K, _V] + _hash: Optional[int] @classmethod - def fromkeys( + def fromkeys( # noqa: D102 cls, seq: Iterable[_K], value: Optional[_V] = None ) -> immutabledict[_K, _V]: - return cls(cls.dict_cls.fromkeys(seq, value)) + return cls(cls._dict_cls.fromkeys(seq, value)) - def __init__(self, *args: Any, **kwargs: Any) -> None: - self._dict = self.dict_cls(*args, **kwargs) - self._hash: Optional[int] = None + def __new__(cls, *args: Any, **kwargs: Any) -> immutabledict[_K, _V]: # noqa: D102 + inst = super().__new__(cls) + setattr(inst, "_dict", cls._dict_cls(*args, **kwargs)) + setattr(inst, "_hash", None) + return inst def __getitem__(self, key: _K) -> _V: return self._dict[key] @@ -35,7 +51,7 @@ def __contains__(self, key: object) -> bool: return key in self._dict - def copy(self) -> immutabledict[_K, _V]: + def copy(self) -> immutabledict[_K, _V]: # noqa: D102 return self.__class__(self) def __iter__(self) -> Iterator[_K]: @@ -45,7 +61,7 @@ return len(self._dict) def __repr__(self) -> str: - return "{}({!r})".format(self.__class__.__name__, self._dict) + return f"{self.__class__.__name__}({self._dict!r})" def __hash__(self) -> int: if self._hash is None: @@ -73,10 +89,58 @@ def __ior__(self, other: Any) -> immutabledict[_K, _V]: raise TypeError(f"'{self.__class__.__name__}' object is not mutable") + def items(self) -> ItemsView[_K, _V]: # noqa: D102 + return self._dict.items() + + def keys(self) -> KeysView[_K]: # noqa: D102 + return self._dict.keys() + + def values(self) -> ValuesView[_V]: # noqa: D102 + return self._dict.values() + + def set(self, key: _K, value: Any) -> immutabledict[_K, _V]: + """ + Return a new :class:`immutabledict` where the item at the given key is set to to the given value. If there is already an item at the given key it will be replaced. + + :param key: the key for which we want to set a value + :param value: the value we want to use + + :return: the new :class:`immutabledict` with the key set to the given value + """ + new = dict(self._dict) + new[key] = value + return self.__class__(new) + + def delete(self, key: _K) -> immutabledict[_K, _V]: + """ + Return a new :class:`immutabledict` without the item at the given key. + + :param key: the key of the item you want to remove in the returned :class:`immutabledict` + + :raises [KeyError]: a KeyError is raised if there is no item at the given key + + :return: the new :class:`immutabledict` without the item at the given key + """ + new = dict(self._dict) + del new[key] + return self.__class__(new) + + def update(self, _dict: Dict[_K, _V]) -> immutabledict[_K, _V]: + """ + Similar to :meth:`dict.update` but returning an immutabledict. + + :return: the updated :class:`immutabledict` + """ + new = dict(self._dict) + new.update(_dict) + return self.__class__(new) + class ImmutableOrderedDict(immutabledict[_K, _V]): """ An immutabledict subclass that maintains key order. + + Same as :class:`immutabledict` but based on :class:`collections.OrderedDict`. """ dict_cls = OrderedDict diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/immutabledict-3.0.0/pyproject.toml new/immutabledict-4.0.0/pyproject.toml --- old/immutabledict-3.0.0/pyproject.toml 2023-07-21 20:13:55.780401200 +0200 +++ new/immutabledict-4.0.0/pyproject.toml 2023-11-25 13:34:07.496336700 +0100 @@ -1,11 +1,12 @@ [tool.poetry] name = "immutabledict" -version = "3.0.0" +version = "4.0.0" description = "Immutable wrapper around dictionaries (a fork of frozendict)" authors = ["Corentin Garcia <corent...@gmail.com>"] license = "MIT" readme = "README.md" repository = "https://github.com/corenting/immutabledict" +documentation = "https://immutabledict.corenting.fr" keywords = ["immutable", "dictionary"] classifiers = [ "Development Status :: 5 - Production/Stable", @@ -18,6 +19,7 @@ [tool.poetry.urls] "Changelog" = "https://github.com/corenting/immutabledict/blob/master/CHANGELOG.md" "Bug Tracker" = "https://github.com/corenting/immutabledict/issues" +"Donation" = "https://corenting.fr/donate" [tool.poetry.dependencies] python = "^3.8" @@ -26,14 +28,30 @@ black = "*" coverage = "*" mypy = "*" -pdbpp = "*" pytest = "*" pytest-cov = "*" ruff = "*" +sphinx = { version = "^7.2", python = "^3.9" } tox = "*" [tool.ruff] line-length = 88 +target-version = "py38" +per-file-ignores = {"tests/**" = ["S101", "D"]} +# Enable pycodestyle (E), Pyflakes (F), flake8-print (T20), ruff rules (RUF), +# isort (I), pep8-naming (N), pyupgrade (UP), flake8-async (ASYNC), +# flake8-bandit (S), perflint (PERF) and pydocstyle (D) +select = ["E", "F", "T20", "RUF", "I", "N", "UP", "ASYNC", "S", "PERF", "D"] +ignore = [ + "E501", # line too long as black is used + "D203", # one blank line before class + "D212", # multiline summary first line + "D105" # no docstrings for magic methods +] + +[tool.ruff.pyupgrade] +# Preserve types, even if a file imports `from __future__ import annotations`. +keep-runtime-typing = true [tool.mypy] ignore_missing_imports = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/immutabledict-3.0.0/tests/test_immutabledict.py new/immutabledict-4.0.0/tests/test_immutabledict.py --- old/immutabledict-3.0.0/tests/test_immutabledict.py 2023-07-21 20:13:55.780401200 +0200 +++ new/immutabledict-4.0.0/tests/test_immutabledict.py 2023-11-25 13:34:07.496336700 +0100 @@ -1,4 +1,5 @@ from typing import Any, Dict, Union + import pytest from immutabledict import ImmutableOrderedDict, immutabledict @@ -6,8 +7,7 @@ class TestImmutableDict: def test_covariance(self) -> None: - """ - Not a real unit test, but test covariance + """Not a real unit test, but test covariance as mypy runs on the tests. """ @@ -26,6 +26,10 @@ my_dict = second_dict assert my_dict == second_dict + def test_new_init_methods(self) -> None: + assert "__new__" in immutabledict.__dict__ + assert "__init__" not in immutabledict.__dict__ + def test_cannot_assign_value(self) -> None: with pytest.raises(AttributeError): immutabledict().setitem("key", "value") # type: ignore @@ -94,7 +98,7 @@ immutable_dict: immutabledict[str, str] = immutabledict( {"a": "value", "b": "other_value"} ) - eval_ret = eval(repr(immutable_dict)) + eval_ret = eval(repr(immutable_dict)) # noqa: S307 assert immutable_dict == eval_ret def test_hash(self) -> None: @@ -185,6 +189,54 @@ assert first_dict == {"a": "a", "b": "b"} assert second_dict == {"a": "A", "c": "c"} + @pytest.mark.parametrize( + "statement", + [ + "for k, v in d.items(): s += 1", + "for v in d.values(): s += 1", + "for k in d.keys(): s += 1", + ], + ) + def test_performance(self, statement: str) -> None: + from timeit import timeit + + time_standard = timeit( + statement, + number=3, + setup="s=0; d = {i:i for i in range(1000000)}", + ) + + time_immutable = timeit( + statement, + globals=globals(), + number=3, + setup="s=0; d = immutabledict({i:i for i in range(1000000)})", + ) + + assert time_immutable < 1.2 * time_standard + + def test_set_delete_update(self) -> None: + d: immutabledict[str, int] = immutabledict(a=1, b=2) + + assert d.set("a", 10) == immutabledict(a=10, b=2) == dict(a=10, b=2) + assert d.delete("a") == immutabledict(b=2) == dict(b=2) + + with pytest.raises(KeyError): + d.delete("c") + + assert d.update({"a": 3}) == immutabledict(a=3, b=2) == dict(a=3, b=2) + + assert ( + d.update({"c": 17}) == immutabledict(a=1, b=2, c=17) == dict(a=1, b=2, c=17) + ) + + # Make sure d doesn't change + assert d == immutabledict(a=1, b=2) == dict(a=1, b=2) + + def test_new_kwargs(self) -> None: + immutable_dict: immutabledict[str, int] = immutabledict(a=1, b=2) + assert immutable_dict == {"a": 1, "b": 2} == dict(a=1, b=2) + class TestImmutableOrderedDict: def test_ordered(self) -> None: