Hello community, here is the log from the commit of package python-pyrsistent for openSUSE:Factory checked in at 2019-06-07 12:16:49 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pyrsistent (Old) and /work/SRC/openSUSE:Factory/.python-pyrsistent.new.4811 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyrsistent" Fri Jun 7 12:16:49 2019 rev:4 rq:707795 version:0.15.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pyrsistent/python-pyrsistent.changes 2019-03-19 10:00:28.179941891 +0100 +++ /work/SRC/openSUSE:Factory/.python-pyrsistent.new.4811/python-pyrsistent.changes 2019-06-07 12:16:54.864818144 +0200 @@ -1,0 +2,14 @@ +Wed Jun 5 09:20:42 UTC 2019 - Marketa Calabkova <mcalabk...@suse.com> + +- Update t0 0.15.2 + * Fix #166, Propagate 'ignore_extra' param in hierarchy. Thanks + @ss18 for this! + * Fix #167, thaw typing. Thanks @nattofriends for this! + * Fix #154, not possible to insert empty pmap as leaf node with + transform. + * Python 3.4 is no longer officially supported since it is EOL + since 2019-03-18. + * Fix #157, major improvements to type hints. Thanks @je-l for + working on this and @nattofriend for reviewing the PR! + +------------------------------------------------------------------- Old: ---- pyrsistent-0.14.11.tar.gz New: ---- pyrsistent-0.15.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pyrsistent.spec ++++++ --- /var/tmp/diff_new_pack.7pD14Z/_old 2019-06-07 12:16:56.092817753 +0200 +++ /var/tmp/diff_new_pack.7pD14Z/_new 2019-06-07 12:16:56.136817739 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-pyrsistent -Version: 0.14.11 +Version: 0.15.2 Release: 0 Summary: Persistent, Functional, Immutable data structures License: MIT @@ -27,6 +27,7 @@ Source: https://files.pythonhosted.org/packages/source/p/pyrsistent/pyrsistent-%{version}.tar.gz BuildRequires: %{python_module devel} BuildRequires: %{python_module hypothesis} +BuildRequires: %{python_module numpy} BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module six} ++++++ pyrsistent-0.14.11.tar.gz -> pyrsistent-0.15.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/CHANGES.txt new/pyrsistent-0.15.2/CHANGES.txt --- old/pyrsistent-0.14.11/CHANGES.txt 2019-02-21 22:31:17.000000000 +0100 +++ new/pyrsistent-0.15.2/CHANGES.txt 2019-05-12 14:13:50.000000000 +0200 @@ -1,5 +1,20 @@ Revision history ---------------- +0.15.2, 2019-05-12 + * Fix #166, Propagate 'ignore_extra' param in hierarchy. Thanks @ss18 for this! + * Fix #167, thaw typing. Thanks @nattofriends for this! + * Fix #154, not possible to insert empty pmap as leaf node with transform. + +0.15.1, 2019-04-26 + * Fix #163 installation broken on Python 2 because of fix of #161, thanks @vphilippon for this! Sorry for the + inconvenience. + +0.15.0, 2019-04-25 + * Python 3.4 is no longer officially supported since it is EOL since 2019-03-18. + * Fix #157, major improvements to type hints. Thanks @je-l for working on this and @nattofriend for reviewing the PR! + * Fix #161, installation fails on some Windows platforms because fallback to Python pvector does not work. + Thanks @MaxTaggart for fixing and verifying this! + 0.14.11, 2019-02-21 * Fix #152 Don't use __builtin_popcount, this hopefully fixes #147 Error in pvectorc.cp37-win_amd64.pyd file, as well. Thanks @benrg for this! diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/PKG-INFO new/pyrsistent-0.15.2/PKG-INFO --- old/pyrsistent-0.14.11/PKG-INFO 2019-02-21 22:31:40.000000000 +0100 +++ new/pyrsistent-0.15.2/PKG-INFO 2019-05-12 14:14:06.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pyrsistent -Version: 0.14.11 +Version: 0.15.2 Summary: Persistent/Functional/Immutable data structures Home-page: http://github.com/tobgu/pyrsistent/ Author: Tobias Gustafsson @@ -570,7 +570,7 @@ Compatibility ------------- - Pyrsistent is developed and tested on Python 2.6, 2.7, 3.4, 3.5, 3.6 and PyPy (Python 2 and 3 compatible). It will most + Pyrsistent is developed and tested on Python 2.7, 3.5, 3.6, 3.7 and PyPy (Python 2 and 3 compatible). It will most likely work on all other versions >= 3.4 but no guarantees are given. :) Compatibility issues @@ -674,6 +674,14 @@ benrg https://github.com/benrg + Jere Lahelma https://github.com/je-l + + Max Taggart https://github.com/MaxTaggart + + Vincent Philippon https://github.com/vphilippon + + Semen Zhydenko https://github.com/ss18 + Contributing ------------ @@ -719,7 +727,6 @@ Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/README new/pyrsistent-0.15.2/README --- old/pyrsistent-0.14.11/README 2019-02-21 22:31:17.000000000 +0100 +++ new/pyrsistent-0.15.2/README 2019-05-12 14:12:29.000000000 +0200 @@ -562,7 +562,7 @@ Compatibility ------------- -Pyrsistent is developed and tested on Python 2.6, 2.7, 3.4, 3.5, 3.6 and PyPy (Python 2 and 3 compatible). It will most +Pyrsistent is developed and tested on Python 2.7, 3.5, 3.6, 3.7 and PyPy (Python 2 and 3 compatible). It will most likely work on all other versions >= 3.4 but no guarantees are given. :) Compatibility issues @@ -666,6 +666,14 @@ benrg https://github.com/benrg +Jere Lahelma https://github.com/je-l + +Max Taggart https://github.com/MaxTaggart + +Vincent Philippon https://github.com/vphilippon + +Semen Zhydenko https://github.com/ss18 + Contributing ------------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/README.rst new/pyrsistent-0.15.2/README.rst --- old/pyrsistent-0.14.11/README.rst 2019-02-21 22:31:17.000000000 +0100 +++ new/pyrsistent-0.15.2/README.rst 2019-05-12 14:12:29.000000000 +0200 @@ -562,7 +562,7 @@ Compatibility ------------- -Pyrsistent is developed and tested on Python 2.6, 2.7, 3.4, 3.5, 3.6 and PyPy (Python 2 and 3 compatible). It will most +Pyrsistent is developed and tested on Python 2.7, 3.5, 3.6, 3.7 and PyPy (Python 2 and 3 compatible). It will most likely work on all other versions >= 3.4 but no guarantees are given. :) Compatibility issues @@ -666,6 +666,14 @@ benrg https://github.com/benrg +Jere Lahelma https://github.com/je-l + +Max Taggart https://github.com/MaxTaggart + +Vincent Philippon https://github.com/vphilippon + +Semen Zhydenko https://github.com/ss18 + Contributing ------------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/_pyrsistent_version.py new/pyrsistent-0.15.2/_pyrsistent_version.py --- old/pyrsistent-0.14.11/_pyrsistent_version.py 2019-02-21 22:31:17.000000000 +0100 +++ new/pyrsistent-0.15.2/_pyrsistent_version.py 2019-05-12 14:13:50.000000000 +0200 @@ -1 +1 @@ -__version__ = '0.14.11' +__version__ = '0.15.2' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/pyrsistent/__init__.pyi new/pyrsistent-0.15.2/pyrsistent/__init__.pyi --- old/pyrsistent-0.14.11/pyrsistent/__init__.pyi 2018-12-19 22:07:14.000000000 +0100 +++ new/pyrsistent-0.15.2/pyrsistent/__init__.pyi 2019-05-11 06:41:48.000000000 +0200 @@ -6,6 +6,7 @@ from typing import AnyStr from typing import Callable from typing import Iterable +from typing import Iterator from typing import List from typing import Optional from typing import Mapping @@ -18,6 +19,15 @@ from typing import TypeVar from typing import overload +# see commit 08519aa for explanation of the re-export +from pyrsistent.typing import CheckedKeyTypeError as CheckedKeyTypeError +from pyrsistent.typing import CheckedPMap as CheckedPMap +from pyrsistent.typing import CheckedPSet as CheckedPSet +from pyrsistent.typing import CheckedPVector as CheckedPVector +from pyrsistent.typing import CheckedType as CheckedType +from pyrsistent.typing import CheckedValueTypeError as CheckedValueTypeError +from pyrsistent.typing import InvariantException as InvariantException +from pyrsistent.typing import PClass as PClass from pyrsistent.typing import PBag as PBag from pyrsistent.typing import PDeque as PDeque from pyrsistent.typing import PList as PList @@ -25,70 +35,114 @@ from pyrsistent.typing import PMapEvolver as PMapEvolver from pyrsistent.typing import PSet as PSet from pyrsistent.typing import PSetEvolver as PSetEvolver +from pyrsistent.typing import PTypeError as PTypeError from pyrsistent.typing import PVector as PVector from pyrsistent.typing import PVectorEvolver as PVectorEvolver +T = TypeVar('T') KT = TypeVar('KT') VT = TypeVar('VT') -def pmap(initial: Mapping[KT, VT] = {}, pre_size: int = 0) -> PMap[KT, VT]: ... -def m(**kwargs: Mapping[KT, VT]) -> PMap[KT, VT]: ... - -T = TypeVar('T') +def pmap(initial: Union[Mapping[KT, VT], Iterable[Tuple[KT, VT]]] = {}, pre_size: int = 0) -> PMap[KT, VT]: ... +def m(**kwargs: VT) -> PMap[str, VT]: ... -def pvector(iterable: Iterable[T]) -> PVector[T]: ... -def v(*iterable: Iterable[T]) -> PVector[T]: ... +def pvector(iterable: Iterable[T] = ...) -> PVector[T]: ... +def v(*iterable: T) -> PVector[T]: ... def pset(iterable: Iterable[T] = (), pre_size: int = 8) -> PSet[T]: ... -def s(*iterable: Iterable[T]) -> PSet[T]: ... +def s(*iterable: T) -> PSet[T]: ... -# The actual return value (_PField) is irrelevant after a PRecord has been instantiated, -# see https://github.com/tobgu/pyrsistent/blob/master/pyrsistent/_precord.py#L10 +# see class_test.py for use cases +Invariant = Tuple[bool, Optional[Union[str, Callable[[], str]]]] + +@overload def field( - type: Union[Type[T], Sequence[Type[T]]] = (), - invariant: Callable[[Any], Tuple[bool, Optional[str]]] = lambda _: (True, None), + type: Union[Type[T], Sequence[Type[T]]] = ..., + invariant: Callable[[Any], Union[Invariant, Iterable[Invariant]]] = lambda _: (True, None), initial: Any = object(), mandatory: bool = False, factory: Callable[[Any], T] = lambda x: x, serializer: Callable[[Any, T], Any] = lambda _, value: value, ) -> T: ... - +# The actual return value (_PField) is irrelevant after a PRecord has been instantiated, +# see https://github.com/tobgu/pyrsistent/blob/master/pyrsistent/_precord.py#L10 +@overload +def field( + type: Any = ..., + invariant: Callable[[Any], Union[Invariant, Iterable[Invariant]]] = lambda _: (True, None), + initial: Any = object(), + mandatory: bool = False, + factory: Callable[[Any], Any] = lambda x: x, + serializer: Callable[[Any, Any], Any] = lambda _, value: value, +) -> Any: ... + +# Use precise types for the simplest use cases, but fall back to Any for +# everything else. See record_test.py for the wide range of possible types for +# item_type +@overload def pset_field( item_type: Type[T], optional: bool = False, - initial: Any = (), + initial: Iterable[T] = ..., ) -> PSet[T]: ... +@overload +def pset_field( + item_type: Any, + optional: bool = False, + initial: Any = (), +) -> PSet[Any]: ... +@overload def pmap_field( key_type: Type[KT], value_type: Type[VT], optional: bool = False, invariant: Callable[[Any], Tuple[bool, Optional[str]]] = lambda _: (True, None), ) -> PMap[KT, VT]: ... +@overload +def pmap_field( + key_type: Any, + value_type: Any, + optional: bool = False, + invariant: Callable[[Any], Tuple[bool, Optional[str]]] = lambda _: (True, None), +) -> PMap[Any, Any]: ... +@overload def pvector_field( item_type: Type[T], optional: bool = False, - initial: Any = (), + initial: Iterable[T] = ..., ) -> PVector[T]: ... +@overload +def pvector_field( + item_type: Any, + optional: bool = False, + initial: Any = (), +) -> PVector[Any]: ... def pbag(elements: Iterable[T]) -> PBag[T]: ... -def b(*elements: Iterable[T]) -> PBag[T]: ... +def b(*elements: T) -> PBag[T]: ... def plist(iterable: Iterable[T] = (), reverse: bool = False) -> PList[T]: ... -def l(*elements: Iterable[T]) -> PList[T]: ... +def l(*elements: T) -> PList[T]: ... -def pdeque(iterable: Iterable[T], maxlen: Optional[int] = None) -> PDeque[T]: ... -def dq(*iterable: Iterable[T]) -> PDeque[T]: ... +def pdeque(iterable: Optional[Iterable[T]] = None, maxlen: Optional[int] = None) -> PDeque[T]: ... +def dq(*iterable: T) -> PDeque[T]: ... -def optional(*typs: Iterable[type]) -> Iterable[type]: ... +@overload +def optional(type: T) -> Tuple[T, Type[None]]: ... +@overload +def optional(*typs: Any) -> Tuple[Any, ...]: ... T_PRecord = TypeVar('T_PRecord', bound='PRecord') class PRecord(PMap[AnyStr, Any]): - _precord_fields: dict - _precord_initial_values: dict + _precord_fields: Mapping + _precord_initial_values: Mapping + def __hash__(self) -> int: ... def __init__(self, **kwargs: Any) -> None: ... + def __iter__(self) -> Iterator[Any]: ... + def __len__(self) -> int: ... @classmethod def create( cls: Type[T_PRecord], @@ -101,7 +155,8 @@ def remove(self: T_PRecord, key: KT) -> T_PRecord: ... def set(self: T_PRecord, key: KT, val: VT) -> T_PRecord: ... - def serialize(format: Any = None) -> MutableMapping: ... + def serialize(self, format: Optional[Any] = ...) -> MutableMapping: ... + # From pyrsistent documentation: # This set function differs slightly from that in the PMap # class. First of all it accepts key-value pairs. Second it accepts multiple key-value @@ -117,25 +172,32 @@ verbose: bool = False, ) -> Tuple: ... # actually a namedtuple +# ignore mypy warning "Overloaded function signatures 1 and 5 overlap with +# incompatible return types" @overload -def freeze(o: Mapping[KT, VT]) -> PMap[KT, VT]: ... +def freeze(o: Mapping[KT, VT]) -> PMap[KT, VT]: ... # type: ignore @overload -def freeze(o: List[T]) -> PVector[T]: ... +def freeze(o: List[T]) -> PVector[T]: ... # type: ignore @overload def freeze(o: Tuple[T, ...]) -> Tuple[T, ...]: ... @overload -def freeze(o: Set[T]) -> PSet[T]: ... +def freeze(o: Set[T]) -> PSet[T]: ... # type: ignore +@overload +def freeze(o: T) -> T: ... + @overload -def thaw(o: PMap[KT, VT]) -> Mapping[KT, VT]: ... +def thaw(o: PMap[KT, VT]) -> MutableMapping[KT, VT]: ... # type: ignore @overload -def thaw(o: PVector[T]) -> List[T]: ... +def thaw(o: PVector[T]) -> List[T]: ... # type: ignore @overload def thaw(o: Tuple[T, ...]) -> Tuple[T, ...]: ... # collections.abc.MutableSet is kind of garbage: # https://stackoverflow.com/questions/24977898/why-does-collections-mutableset-not-bestow-an-update-method @overload -def thaw(o: PSet[T]) -> Set[T]: ... +def thaw(o: PSet[T]) -> Set[T]: ... # type: ignore +@overload +def thaw(o: T) -> T: ... def mutant(fn: Callable) -> Callable: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/pyrsistent/_field_common.py new/pyrsistent-0.15.2/pyrsistent/_field_common.py --- old/pyrsistent-0.14.11/pyrsistent/_field_common.py 2018-10-14 12:06:35.000000000 +0200 +++ new/pyrsistent-0.15.2/pyrsistent/_field_common.py 2019-05-12 14:07:03.000000000 +0200 @@ -13,7 +13,7 @@ ) from pyrsistent._checked_types import optional as optional_type from pyrsistent._checked_types import wrap_invariant -from pyrsistent._compat import Enum +import inspect def set_fields(dct, bases, name): @@ -46,6 +46,24 @@ raise PTypeError(destination_cls, name, field.type, actual_type, message) +def is_type_cls(type_cls, field_type): + types = tuple(field_type) + if len(types) == 0: + return False + return issubclass(get_type(types[0]), type_cls) + + +def is_field_ignore_extra_complaint(type_cls, field, ignore_extra): + # ignore_extra param has default False value, for speed purpose no need to propagate False + if not ignore_extra: + return False + + if not is_type_cls(type_cls, field.type): + return False + + return 'ignore_extra' in inspect.getargspec(field.factory).args + + class _PField(object): __slots__ = ('type', 'invariant', 'initial', 'mandatory', '_factory', 'serializer') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/pyrsistent/_pclass.py new/pyrsistent-0.15.2/pyrsistent/_pclass.py --- old/pyrsistent-0.14.11/pyrsistent/_pclass.py 2018-11-17 06:52:33.000000000 +0100 +++ new/pyrsistent-0.15.2/pyrsistent/_pclass.py 2019-05-12 14:07:03.000000000 +0200 @@ -1,6 +1,8 @@ import six from pyrsistent._checked_types import (InvariantException, CheckedType, _restore_pickle, store_invariants) -from pyrsistent._field_common import (set_fields, check_type, PFIELD_NO_INITIAL, serialize, check_global_invariants) +from pyrsistent._field_common import ( + set_fields, check_type, is_field_ignore_extra_complaint, PFIELD_NO_INITIAL, serialize, check_global_invariants +) from pyrsistent._transformations import transform @@ -46,12 +48,16 @@ def __new__(cls, **kwargs): # Support *args? result = super(PClass, cls).__new__(cls) factory_fields = kwargs.pop('_factory_fields', None) + ignore_extra = kwargs.pop('ignore_extra', None) missing_fields = [] invariant_errors = [] for name, field in cls._pclass_fields.items(): if name in kwargs: if factory_fields is None or name in factory_fields: - value = field.factory(kwargs[name]) + if is_field_ignore_extra_complaint(PClass, field, ignore_extra): + value = field.factory(kwargs[name], ignore_extra=ignore_extra) + else: + value = field.factory(kwargs[name]) else: value = kwargs[name] _check_and_set_attr(cls, field, name, value, result, invariant_errors) @@ -122,7 +128,7 @@ if ignore_extra: kwargs = {k: kwargs[k] for k in cls._pclass_fields if k in kwargs} - return cls(_factory_fields=_factory_fields, **kwargs) + return cls(_factory_fields=_factory_fields, ignore_extra=ignore_extra, **kwargs) def serialize(self, format=None): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/pyrsistent/_precord.py new/pyrsistent-0.15.2/pyrsistent/_precord.py --- old/pyrsistent-0.14.11/pyrsistent/_precord.py 2018-11-17 06:52:33.000000000 +0100 +++ new/pyrsistent-0.15.2/pyrsistent/_precord.py 2019-05-12 14:07:03.000000000 +0200 @@ -1,7 +1,8 @@ import six from pyrsistent._checked_types import CheckedType, _restore_pickle, InvariantException, store_invariants from pyrsistent._field_common import ( - set_fields, check_type, PFIELD_NO_INITIAL, serialize, check_global_invariants) + set_fields, check_type, is_field_ignore_extra_complaint, PFIELD_NO_INITIAL, serialize, check_global_invariants +) from pyrsistent._pmap import PMap, pmap @@ -39,6 +40,7 @@ return super(PRecord, cls).__new__(cls, kwargs['_precord_size'], kwargs['_precord_buckets']) factory_fields = kwargs.pop('_factory_fields', None) + ignore_extra = kwargs.pop('_ignore_extra', False) initial_values = kwargs if cls._precord_initial_values: @@ -46,7 +48,7 @@ for k, v in cls._precord_initial_values.items()) initial_values.update(kwargs) - e = _PRecordEvolver(cls, pmap(), _factory_fields=factory_fields) + e = _PRecordEvolver(cls, pmap(), _factory_fields=factory_fields, _ignore_extra=ignore_extra) for k, v in initial_values.items(): e[k] = v @@ -91,7 +93,7 @@ if ignore_extra: kwargs = {k: kwargs[k] for k in cls._precord_fields if k in kwargs} - return cls(_factory_fields=_factory_fields, **kwargs) + return cls(_factory_fields=_factory_fields, _ignore_extra=ignore_extra, **kwargs) def __reduce__(self): # Pickling support @@ -106,14 +108,15 @@ class _PRecordEvolver(PMap._Evolver): - __slots__ = ('_destination_cls', '_invariant_error_codes', '_missing_fields', '_factory_fields') + __slots__ = ('_destination_cls', '_invariant_error_codes', '_missing_fields', '_factory_fields', '_ignore_extra') - def __init__(self, cls, original_pmap, _factory_fields=None): + def __init__(self, cls, original_pmap, _factory_fields=None, _ignore_extra=False): super(_PRecordEvolver, self).__init__(original_pmap) self._destination_cls = cls self._invariant_error_codes = [] self._missing_fields = [] self._factory_fields = _factory_fields + self._ignore_extra = _ignore_extra def __setitem__(self, key, original_value): self.set(key, original_value) @@ -123,7 +126,10 @@ if field: if self._factory_fields is None or field in self._factory_fields: try: - value = field.factory(original_value) + if is_field_ignore_extra_complaint(PRecord, field, self._ignore_extra): + value = field.factory(original_value, ignore_extra=self._ignore_extra) + else: + value = field.factory(original_value) except InvariantException as e: self._invariant_error_codes += e.invariant_errors self._missing_fields += e.missing_fields @@ -161,4 +167,3 @@ check_global_invariants(result, cls._precord_invariants) return result - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/pyrsistent/_transformations.py new/pyrsistent-0.15.2/pyrsistent/_transformations.py --- old/pyrsistent-0.14.11/pyrsistent/_transformations.py 2018-07-07 15:53:48.000000000 +0200 +++ new/pyrsistent-0.15.2/pyrsistent/_transformations.py 2019-05-11 08:24:44.000000000 +0200 @@ -10,6 +10,9 @@ from inspect import getargspec +_EMPTY_SENTINEL = object() + + def inc(x): """ Add one to the current value """ return x + 1 @@ -39,6 +42,7 @@ """ Matcher that matches any value """ return True + # Support functions def _chunks(l, n): for i in range(0, len(l), n): @@ -80,7 +84,6 @@ def _get_keys_and_values(structure, key_spec): - from pyrsistent._pmap import pmap if callable(key_spec): # Support predicates as callable objects in the path arity = _get_arity(key_spec) @@ -99,7 +102,7 @@ ) # Non-callables are used as-is as a key. - return [(key_spec, _get(structure, key_spec, pmap()))] + return [(key_spec, _get(structure, key_spec, _EMPTY_SENTINEL))] if signature is None: @@ -116,7 +119,9 @@ and p.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD) ) + def _update_structure(structure, kvs, path, command): + from pyrsistent._pmap import pmap e = structure.evolver() if not path and command is discard: # Do this in reverse to avoid index problems with vectors. See #92. @@ -124,8 +129,15 @@ discard(e, k) else: for k, v in kvs: + is_empty = False + if v is _EMPTY_SENTINEL: + # Allow expansion of structure but make sure to cover the case + # when an empty pmap is added as leaf node. See #154. + is_empty = True + v = pmap() + result = _do_to_path(v, path, command) - if result is not v: + if result is not v or is_empty: e[k] = result return e.persistent() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/pyrsistent/typing.py new/pyrsistent-0.15.2/pyrsistent/typing.py --- old/pyrsistent-0.14.11/pyrsistent/typing.py 2018-11-17 06:52:33.000000000 +0100 +++ new/pyrsistent-0.15.2/pyrsistent/typing.py 2019-04-25 21:49:19.000000000 +0200 @@ -22,12 +22,25 @@ from typing import Sized from typing import TypeVar - __all__ = ['CheckedPSet', 'CheckedPVector', 'PBag', 'PDeque', 'PList', 'PMap', 'PSet', 'PVector'] + __all__ = [ + 'CheckedPMap', + 'CheckedPSet', + 'CheckedPVector', + 'PBag', + 'PDeque', + 'PList', + 'PMap', + 'PSet', + 'PVector', + ] T = TypeVar('T') KT = TypeVar('KT') VT = TypeVar('VT') + class CheckedPMap(Mapping[KT, VT], Hashable): + pass + # PSet.add and PSet.discard have different type signatures than that of Set. class CheckedPSet(Generic[T], Hashable): pass @@ -53,5 +66,14 @@ class PVector(Sequence[T], Hashable): pass + + class PVectorEvolver(Generic[T]): + pass + + class PMapEvolver(Generic[KT, VT]): + pass + + class PSetEvolver(Generic[T]): + pass except ImportError: pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/pyrsistent/typing.pyi new/pyrsistent-0.15.2/pyrsistent/typing.pyi --- old/pyrsistent-0.14.11/pyrsistent/typing.pyi 2018-11-20 22:52:04.000000000 +0100 +++ new/pyrsistent-0.15.2/pyrsistent/typing.pyi 2019-04-25 21:49:19.000000000 +0200 @@ -4,18 +4,22 @@ # from typing import Any from typing import Callable +from typing import Dict from typing import Generic from typing import Hashable from typing import Iterator from typing import Iterable +from typing import List from typing import Mapping from typing import MutableMapping from typing import Optional from typing import Sequence from typing import AbstractSet from typing import Sized +from typing import Set from typing import Tuple from typing import TypeVar +from typing import Type from typing import Union from typing import overload @@ -23,15 +27,17 @@ KT = TypeVar('KT') VT = TypeVar('VT') + class PMap(Mapping[KT, VT], Hashable): def __add__(self, other: PMap[KT, VT]) -> PMap[KT, VT]: ... def __getitem__(self, key: KT) -> VT: ... + def __getattr__(self, key: str) -> VT: ... def __hash__(self) -> int: ... def __iter__(self) -> Iterator[KT]: ... def __len__(self) -> int: ... def copy(self) -> PMap[KT, VT]: ... def discard(self, key: KT) -> PMap[KT, VT]: ... - def evolver(self) -> 'PMapEvolver[KT, VT]': ... + def evolver(self) -> PMapEvolver[KT, VT]: ... def iteritems(self) -> Iterable[Tuple[KT, VT]]: ... def iterkeys(self) -> Iterable[KT]: ... def itervalues(self) -> Iterable[VT]: ... @@ -65,8 +71,9 @@ def __mul__(self, other: PVector[T]) -> PVector[T]: ... def append(self, val: T) -> PVector[T]: ... def delete(self, index: int, stop: Optional[int]) -> PVector[T]: ... - def evolver(self) -> 'PVectorEvolver[T]': ... + def evolver(self) -> PVectorEvolver[T]: ... def extend(self, obj: Iterable[T]) -> PVector[T]: ... + def tolist(self) -> List[T]: ... def mset(self, *args: Iterable[Union[T, int]]) -> PVector[T]: ... def remove(self, value: T) -> PVector[T]: ... # Not compatible with MutableSequence @@ -94,13 +101,13 @@ class PSet(AbstractSet[T], Hashable): def __contains__(self, element: object) -> bool: ... def __hash__(self) -> int: ... - def __iter__(self) -> Iterator[KT]: ... + def __iter__(self) -> Iterator[T]: ... def __len__(self) -> int: ... def add(self, element: T) -> PSet[T]: ... def copy(self) -> PSet[T]: ... def difference(self, iterable: Iterable) -> PSet[T]: ... def discard(self, element: T) -> PSet[T]: ... - def evolver(self) -> 'PSetEvolver[T]': ... + def evolver(self) -> PSetEvolver[T]: ... def intersection(self, iterable: Iterable) -> PSet[T]: ... def issubset(self, iterable: Iterable) -> bool: ... def issuperset(self, iterable: Iterable) -> bool: ... @@ -168,21 +175,120 @@ def __getitem__(self, index: slice) -> PList[T]: ... def __hash__(self) -> int: ... def __len__(self) -> int: ... - def __lt__(self, other: PDeque[T]) -> bool: ... + def __lt__(self, other: PList[T]) -> bool: ... + def __gt__(self, other: PList[T]) -> bool: ... def cons(self, elem: T) -> PList[T]: ... + @property + def first(self) -> T: ... def mcons(self, iterable: Iterable[T]) -> PList[T]: ... def remove(self, elem: T) -> PList[T]: ... + @property + def rest(self) -> PList[T]: ... def reverse(self) -> PList[T]: ... def split(self, index: int) -> Tuple[PList[T], PList[T]]: ... +T_PClass = TypeVar('T_PClass', bound='PClass') + +class PClass(Hashable): + def __new__(cls, **kwargs: Any): ... + def set(self: T_PClass, *args: Any, **kwargs: Any) -> T_PClass: ... + @classmethod + def create( + cls: Type[T_PClass], + kwargs: Any, + _factory_fields: Optional[Any] = ..., + ignore_extra: bool = ..., + ) -> T_PClass: ... + def serialize(self, format: Optional[Any] = ...): ... + def transform(self, *transformations: Any): ... + def __eq__(self, other: object): ... + def __ne__(self, other: object): ... + def __hash__(self): ... + def __reduce__(self): ... + def evolver(self) -> PClassEvolver: ... + def remove(self: T_PClass, name: Any) -> T_PClass: ... + +class PClassEvolver: + def __init__(self, original: Any, initial_dict: Any) -> None: ... + def __getitem__(self, item: Any): ... + def set(self, key: Any, value: Any): ... + def __setitem__(self, key: Any, value: Any) -> None: ... + def remove(self, item: Any): ... + def __delitem__(self, item: Any) -> None: ... + def persistent(self) -> PClass: ... + def __getattr__(self, item: Any): ... + + class CheckedPMap(PMap[KT, VT]): - pass + __key_type__: Type[KT] + __value_type__: Type[VT] + def __new__(cls, source: Mapping[KT, VT] = ..., size: int = ...) -> None: ... + @classmethod + def create(cls, source_data: Mapping[KT, VT], _factory_fields: Any = ...) -> CheckedPMap[KT, VT]: ... + def serialize(self, format: Optional[Any] = ...) -> Dict[KT, VT]: ... class CheckedPVector(PVector[T]): - pass + __type__: Type[T] + def __new__(self, initial: Iterable[T] = ...) -> None: ... + @classmethod + def create(cls, source_data: Iterable[T], _factory_fields: Any = ...) -> CheckedPVector[T]: ... + def serialize(self, format: Optional[Any] = ...) -> List[T]: ... class CheckedPSet(PSet[T]): - pass + __type__: Type[T] + def __new__(cls, initial: Iterable[T] = ...) -> None: ... + @classmethod + def create(cls, source_data: Iterable[T], _factory_fields: Any = ...) -> CheckedPSet[T]: ... + def serialize(self, format: Optional[Any] = ...) -> Set[T]: ... + + +class InvariantException(Exception): + invariant_errors: Tuple[Any, ...] = ... # possibly nested tuple + missing_fields: Tuple[str, ...] = ... + def __init__( + self, + error_codes: Any = ..., + missing_fields: Any = ..., + *args: Any, + **kwargs: Any + ) -> None: ... + + +class CheckedTypeError(TypeError): + source_class: Type[Any] + expected_types: Tuple[Any, ...] + actual_type: Type[Any] + actual_value: Any + def __init__( + self, + source_class: Any, + expected_types: Any, + actual_type: Any, + actual_value: Any, + *args: Any, + **kwargs: Any + ) -> None: ... + + +class CheckedKeyTypeError(CheckedTypeError): ... +class CheckedValueTypeError(CheckedTypeError): ... +class CheckedType: ... + + +class PTypeError(TypeError): + source_class: Type[Any] = ... + field: str = ... + expected_types: Tuple[Any, ...] = ... + actual_type: Type[Any] = ... + def __init__( + self, + source_class: Any, + field: Any, + expected_types: Any, + actual_type: Any, + *args: Any, + **kwargs: Any + ) -> None: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/pyrsistent.egg-info/PKG-INFO new/pyrsistent-0.15.2/pyrsistent.egg-info/PKG-INFO --- old/pyrsistent-0.14.11/pyrsistent.egg-info/PKG-INFO 2019-02-21 22:31:40.000000000 +0100 +++ new/pyrsistent-0.15.2/pyrsistent.egg-info/PKG-INFO 2019-05-12 14:14:06.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pyrsistent -Version: 0.14.11 +Version: 0.15.2 Summary: Persistent/Functional/Immutable data structures Home-page: http://github.com/tobgu/pyrsistent/ Author: Tobias Gustafsson @@ -570,7 +570,7 @@ Compatibility ------------- - Pyrsistent is developed and tested on Python 2.6, 2.7, 3.4, 3.5, 3.6 and PyPy (Python 2 and 3 compatible). It will most + Pyrsistent is developed and tested on Python 2.7, 3.5, 3.6, 3.7 and PyPy (Python 2 and 3 compatible). It will most likely work on all other versions >= 3.4 but no guarantees are given. :) Compatibility issues @@ -674,6 +674,14 @@ benrg https://github.com/benrg + Jere Lahelma https://github.com/je-l + + Max Taggart https://github.com/MaxTaggart + + Vincent Philippon https://github.com/vphilippon + + Semen Zhydenko https://github.com/ss18 + Contributing ------------ @@ -719,7 +727,6 @@ Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/setup.py new/pyrsistent-0.15.2/setup.py --- old/pyrsistent-0.14.11/setup.py 2019-02-21 22:21:41.000000000 +0100 +++ new/pyrsistent-0.15.2/setup.py 2019-05-11 08:02:14.000000000 +0200 @@ -9,6 +9,11 @@ from distutils.errors import DistutilsPlatformError, DistutilsExecError from _pyrsistent_version import __version__ +try: + FileNotFoundError +except NameError: # Python 2 + FileNotFoundError = IOError + readme_path = os.path.join(os.path.dirname(__file__), 'README.rst') with codecs.open(readme_path, encoding='utf8') as f: @@ -36,7 +41,7 @@ def run(self): try: build_ext.run(self) - except (CCompilerError, DistutilsExecError, DistutilsPlatformError): + except (CCompilerError, DistutilsExecError, DistutilsPlatformError, FileNotFoundError): e = sys.exc_info()[1] sys.stdout.write('%s\n' % str(e)) warnings.warn(self.warning_message % ("Extension modules", @@ -48,7 +53,7 @@ name = ext.name try: build_ext.build_extension(self, ext) - except (CCompilerError, DistutilsExecError, DistutilsPlatformError): + except (CCompilerError, DistutilsExecError, DistutilsPlatformError, FileNotFoundError): e = sys.exc_info()[1] sys.stdout.write('%s\n' % str(e)) warnings.warn(self.warning_message % ("The %s extension " @@ -73,14 +78,13 @@ 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: PyPy', ], test_suite='tests', - tests_require=['pytest','hypothesis<5'], + tests_require=['pytest', 'hypothesis<5'], scripts=[], setup_requires=pytest_runner, ext_modules=extensions, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/tests/bag_test.py new/pyrsistent-0.15.2/tests/bag_test.py --- old/pyrsistent-0.14.11/tests/bag_test.py 2018-07-07 15:53:48.000000000 +0200 +++ new/pyrsistent-0.15.2/tests/bag_test.py 2019-04-25 21:49:19.000000000 +0200 @@ -32,7 +32,7 @@ assert repr(b(1, 2)) in ('pbag([1, 2])', 'pbag([2, 1])') -def test_add(): +def test_add_empty(): assert b().add(1) == b(1) def test_remove_final(): @@ -105,27 +105,27 @@ assert b(1, 2, 3, 3) - b(3, 4) == b(1, 2, 3) def test_or(): - assert b(1, 2, 2, 3, 3, 3) | b(1, 2, 3, 4, 4) == b(1, - 2, 2, + assert b(1, 2, 2, 3, 3, 3) | b(1, 2, 3, 4, 4) == b(1, + 2, 2, 3, 3, 3, 4, 4) - + def test_and(): assert b(1, 2, 2, 3, 3, 3) & b(2, 3, 3, 4) == b(2, 3, 3) def test_pbag_is_unorderable(): with pytest.raises(TypeError): - _ = b(1) < b(2) + _ = b(1) < b(2) # type: ignore with pytest.raises(TypeError): - _ = b(1) <= b(2) + _ = b(1) <= b(2) # type: ignore with pytest.raises(TypeError): - _ = b(1) > b(2) + _ = b(1) > b(2) # type: ignore with pytest.raises(TypeError): - _ = b(1) >= b(2) + _ = b(1) >= b(2) # type: ignore def test_supports_weakref(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/tests/class_test.py new/pyrsistent-0.15.2/tests/class_test.py --- old/pyrsistent-0.14.11/tests/class_test.py 2018-11-17 06:52:33.000000000 +0100 +++ new/pyrsistent-0.15.2/tests/class_test.py 2019-05-12 14:07:03.000000000 +0200 @@ -15,6 +15,10 @@ z = field(type=int, initial=0) +class Hierarchy(PClass): + point = field(type=Point) + + class TypedContainerObj(PClass): map = pmap_field(str, str) set = pset_field(str) @@ -39,6 +43,13 @@ _ = Point.create({'x': 5, 'y': 10, 'z': 15, 'a': 0}) +def test_create_ignore_extra_true(): + h = Hierarchy.create( + {'point': {'x': 5, 'y': 10, 'z': 15, 'extra_field_0': 'extra_data_0'}, 'extra_field_1': 'extra_data_1'}, + ignore_extra=True) + assert isinstance(h, Hierarchy) + + def test_evolve_pclass_instance(): p = Point(x=1, y=2) p2 = p.set(x=p.x+2) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/tests/list_test.py new/pyrsistent-0.15.2/tests/list_test.py --- old/pyrsistent-0.14.11/tests/list_test.py 2018-07-07 15:53:48.000000000 +0200 +++ new/pyrsistent-0.15.2/tests/list_test.py 2019-04-25 21:49:19.000000000 +0200 @@ -88,7 +88,7 @@ def test_index_invalid_type(): with pytest.raises(TypeError) as e: - plist([1, 2, 3])['foo'] + plist([1, 2, 3])['foo'] # type: ignore assert 'cannot be interpreted' in str(e) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/tests/record_test.py new/pyrsistent-0.15.2/tests/record_test.py --- old/pyrsistent-0.14.11/tests/record_test.py 2018-11-17 06:52:33.000000000 +0100 +++ new/pyrsistent-0.15.2/tests/record_test.py 2019-05-12 14:07:03.000000000 +0200 @@ -14,6 +14,11 @@ y = field() +class Hierarchy(PRecord): + point1 = field(ARecord) + point2 = field(ARecord) + + class RecordContainingContainers(PRecord): map = pmap_field(str, str) vec = pvector_field(str) @@ -30,6 +35,15 @@ class Another(object): pass +def test_create_ignore_extra_true(): + h = Hierarchy.create( + {'point1': {'x': 1, 'y': 'foo', 'extra_field_0': 'extra_data_0'}, + 'point2': {'x': 1, 'y': 'foo', 'extra_field_1': 'extra_data_1'}, + 'extra_field_2': 'extra_data_2', + }, ignore_extra=True + ) + assert h + def test_create(): r = ARecord(x=1, y='foo') assert r.x == 1 @@ -204,7 +218,7 @@ def test_invariant_must_be_callable(): with pytest.raises(TypeError): class BRecord(PRecord): - x = field(invariant='foo') + x = field(invariant='foo') # type: ignore def test_global_invariants_are_inherited(): @@ -244,7 +258,7 @@ def test_factory_must_be_callable(): with pytest.raises(TypeError): class BRecord(PRecord): - x = field(type=int, factory=1) + x = field(type=int, factory=1) # type: ignore def test_nested_record_construction(): @@ -326,7 +340,7 @@ def test_serializer_must_be_callable(): with pytest.raises(TypeError): class CRecord(PRecord): - x = field(serializer=1) + x = field(serializer=1) # type: ignore def test_transform_without_update_returns_same_precord(): @@ -395,7 +409,7 @@ value = pset_field(int) record = Record(value=[1, 2]) with pytest.raises(TypeError): - record.value.add("hello") + record.value.add("hello") # type: ignore def test_pset_field_checked_vector_multiple_types(): """ @@ -531,7 +545,7 @@ value = pvector_field(int) record = Record(value=[1, 2]) with pytest.raises(TypeError): - record.value.append("hello") + record.value.append("hello") # type: ignore def test_pvector_field_checked_vector_multiple_types(): """ @@ -669,7 +683,7 @@ value = pmap_field(int, type(None)) record = Record(value={1: None}) with pytest.raises(TypeError): - record.value.set("hello", None) + record.value.set("hello", None) # type: ignore def test_pmap_field_checked_map_value(): """ @@ -679,7 +693,7 @@ value = pmap_field(int, type(None)) record = Record(value={1: None}) with pytest.raises(TypeError): - record.value.set(2, 4) + record.value.set(2, 4) # type: ignore def test_pmap_field_checked_map_key_multiple_types(): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.14.11/tests/transform_test.py new/pyrsistent-0.15.2/tests/transform_test.py --- old/pyrsistent-0.14.11/tests/transform_test.py 2018-07-07 15:53:48.000000000 +0200 +++ new/pyrsistent-0.15.2/tests/transform_test.py 2019-05-11 08:22:20.000000000 +0200 @@ -1,4 +1,4 @@ -from pyrsistent import freeze, inc, discard, rex, ny, field, PClass +from pyrsistent import freeze, inc, discard, rex, ny, field, PClass, pmap def test_callable_command(): @@ -110,3 +110,8 @@ def test_discard_multiple_elements_in_pvector(): assert freeze([0, 1, 2, 3, 4]).transform([lambda i: i % 2], discard) == freeze([0, 2, 4]) + + +def test_transform_insert_empty_pmap(): + m = pmap().transform(['123'], pmap()) + assert m == pmap({'123': pmap()})