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 <[email protected]>
+
+- 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()})