Hello community, here is the log from the commit of package python-pyrsistent for openSUSE:Factory checked in at 2018-08-12 20:55:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pyrsistent (Old) and /work/SRC/openSUSE:Factory/.python-pyrsistent.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyrsistent" Sun Aug 12 20:55:48 2018 rev:2 rq:628726 version:0.14.4 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pyrsistent/python-pyrsistent.changes 2017-09-11 16:18:30.392803896 +0200 +++ /work/SRC/openSUSE:Factory/.python-pyrsistent.new/python-pyrsistent.changes 2018-08-12 20:55:52.429528308 +0200 @@ -1,0 +2,17 @@ +Sat Aug 11 06:34:31 UTC 2018 - tchva...@suse.com + +- Drop the test conditional +- Use %license macro + +------------------------------------------------------------------- +Fri Aug 10 23:33:25 UTC 2018 - jsi...@suse.de + +- Version jump to 0.14.4 + * Fix PClass double-factory-ing in a few situations. + * Fix #121, regression in PClass.set() + * Fix regression where type names break sequence fields + * Allowing 'set' as a valid container for multiple fields. + * Fix #131 Clarify that dot-notation can be used for element access on PMaps + * Fix #133 Python 3.7 compatibility + +------------------------------------------------------------------- Old: ---- pyrsistent-0.13.0.tar.gz New: ---- pyrsistent-0.14.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pyrsistent.spec ++++++ --- /var/tmp/diff_new_pack.CPMraf/_old 2018-08-12 20:55:52.929529324 +0200 +++ /var/tmp/diff_new_pack.CPMraf/_new 2018-08-12 20:55:52.929529324 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-pyrsistent # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -13,29 +13,26 @@ # published by the Open Source Initiative. # Please submit bugfixes or comments via http://bugs.opensuse.org/ +# %{?!python_module:%define python_module() python-%{**} python3-%{**}} -%bcond_without test Name: python-pyrsistent -Version: 0.13.0 +Version: 0.14.4 Release: 0 -License: MIT Summary: Persistent, Functional, Immutable data structures -Url: http://github.com/tobgu/pyrsistent/ +License: MIT Group: Development/Languages/Python +URL: http://github.com/tobgu/pyrsistent/ Source: https://files.pythonhosted.org/packages/source/p/pyrsistent/pyrsistent-%{version}.tar.gz -BuildRequires: fdupes -BuildRequires: python-rpm-macros BuildRequires: %{python_module devel} -BuildRequires: %{python_module setuptools} -%if %{with test} BuildRequires: %{python_module hypothesis} BuildRequires: %{python_module pytest} +BuildRequires: %{python_module setuptools} BuildRequires: %{python_module six} -%endif +BuildRequires: fdupes +BuildRequires: python-rpm-macros Requires: python-six - %python_subpackages %description @@ -59,16 +56,14 @@ %python_install %python_expand %fdupes %{buildroot}%{$python_sitearch} -%if %{with test} %check %{python_expand export PYTHONPATH=%{buildroot}%{$python_sitearch} py.test-%{$python_bin_suffix} } -%endif %files %{python_files} -%defattr(-,root,root,-) -%doc CHANGES.txt LICENCE.mit README README.rst +%license LICENCE.mit +%doc CHANGES.txt README README.rst %{python_sitearch}/* %changelog ++++++ pyrsistent-0.13.0.tar.gz -> pyrsistent-0.14.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/CHANGES.txt new/pyrsistent-0.14.4/CHANGES.txt --- old/pyrsistent-0.13.0/CHANGES.txt 2017-09-01 11:51:36.000000000 +0200 +++ new/pyrsistent-0.14.4/CHANGES.txt 2018-07-08 11:51:59.000000000 +0200 @@ -1,5 +1,28 @@ Revision history ---------------- +v0.14.4, 2018-07-08 + * Fix #133, minor Python 3.7 compatibility issue. Pyrsistent is now officially Python 3.7 compliant! + +v0.14.3, 2018-06-11 + * Fix #123 regression where type names break sequence fields. Thanks @doozr for this! + * Fix #124 using the class name to make AttributeError on __getattr__ more informative for PRecords. + Thanks @neilvyas for this! + * Fix #125 how fields handle type arguments. Thanks @neilvyas for this! + +v0.14.2, 2017-12-06 + * Fix #121, regression in PClass.set() introduced in 0.14.1. + +v0.14.1, 2017-11-27 + * Equality check performance improvements for pvectors and pmaps. Thanks @dtomas for this! + * Avoid calling factories multiple times for fields that do not change, see PR #120 for for + details. Thanks @teepark for this! + +v0.14.0, 2017-10-08 + * Fix #117, pmap now accepts iterators as input to constructor. Thanks @Julian for this! + * Drop support for Python 2.6. Nothing has been done in this release that will explicitly + break pyrsistent for 2.6 but it will not be considered moving forward. Dropping 2.6 + support is the reason for stepping the second decimal instead of the third. + v0.13.0, 2017-09-01 * Fix #113, Skip field factories when loading pickled objects. There is a minor backwards incompatibilty in the behaviour because of this. Thanks diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/LICENCE.mit new/pyrsistent-0.14.4/LICENCE.mit --- old/pyrsistent-0.13.0/LICENCE.mit 2017-01-06 18:46:49.000000000 +0100 +++ new/pyrsistent-0.14.4/LICENCE.mit 2018-07-07 15:53:48.000000000 +0200 @@ -1,4 +1,4 @@ -Copyright (c) 2017 Tobias Gustafsson +Copyright (c) 2018 Tobias Gustafsson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/PKG-INFO new/pyrsistent-0.14.4/PKG-INFO --- old/pyrsistent-0.13.0/PKG-INFO 2017-09-01 13:11:16.000000000 +0200 +++ new/pyrsistent-0.14.4/PKG-INFO 2018-07-08 11:57:12.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pyrsistent -Version: 0.13.0 +Version: 0.14.4 Summary: Persistent/Functional/Immutable data structures Home-page: http://github.com/tobgu/pyrsistent/ Author: Tobias Gustafsson @@ -570,8 +570,8 @@ Compatibility ------------- - Pyrsistent is developed and tested on Python 2.6, 2.7, 3.4, 3.5 and PyPy (Python 2.7 compatible). It will most likely work - on all other versions >= 3.4 but no guarantees are given. :) + 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 + likely work on all other versions >= 3.4 but no guarantees are given. :) Compatibility issues ~~~~~~~~~~~~~~~~~~~~ @@ -643,18 +643,50 @@ Julian Berman https://github.com/Julian + Dennis Tomas https://github.com/dtomas + + Neil Vyas https://github.com/neilvyas + + doozr https://github.com/doozr + Contributing ------------ - If you experience problems please log them on GitHub. If you want to contribute code, please fork the code and submit a pull request. + Want to contribute? That's great! If you experience problems please log them on GitHub. If you want to contribute code, + please fork the repository and submit a pull request. + + Run tests + ~~~~~~~~~ + .. _tox: https://tox.readthedocs.io/en/latest/ + + Tests can be executed using tox_. + + Install tox: ``pip install tox`` + + Run test for Python 2.7: ``tox -epy27`` + + Project status + -------------- + Pyrsistent can be considered stable and mature (who knows, there may even be a 1.0 some day :-)). The project is + maintained, bugs fixed, PRs reviewed and merged and new releases made. I currently do not have time for development + of new features or functionality which I don't have use for myself. I'm more than happy to take PRs for new + functionality though! + + There are a bunch of issues marked with ``enhancement`` and ``help wanted`` that contain requests for new functionality + that would be nice to include. The level of difficulty and extend of the issues varies, please reach out to me if you're + interested in working on any of them. + + If you feel that you have a grand master plan for where you would like Pyrsistent to go and have the time to put into + it please don't hesitate to discuss this with me and submit PRs for it. If all goes well I'd be more than happy to add + additional maintainers to the project! Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 2.6 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 Classifier: Programming Language :: Python :: Implementation :: PyPy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/README new/pyrsistent-0.14.4/README --- old/pyrsistent-0.13.0/README 2017-09-01 11:53:08.000000000 +0200 +++ new/pyrsistent-0.14.4/README 2018-07-07 15:53:48.000000000 +0200 @@ -562,8 +562,8 @@ Compatibility ------------- -Pyrsistent is developed and tested on Python 2.6, 2.7, 3.4, 3.5 and PyPy (Python 2.7 compatible). It will most likely work -on all other versions >= 3.4 but no guarantees are given. :) +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 +likely work on all other versions >= 3.4 but no guarantees are given. :) Compatibility issues ~~~~~~~~~~~~~~~~~~~~ @@ -635,7 +635,39 @@ Julian Berman https://github.com/Julian +Dennis Tomas https://github.com/dtomas + +Neil Vyas https://github.com/neilvyas + +doozr https://github.com/doozr + Contributing ------------ -If you experience problems please log them on GitHub. If you want to contribute code, please fork the code and submit a pull request. +Want to contribute? That's great! If you experience problems please log them on GitHub. If you want to contribute code, +please fork the repository and submit a pull request. + +Run tests +~~~~~~~~~ +.. _tox: https://tox.readthedocs.io/en/latest/ + +Tests can be executed using tox_. + +Install tox: ``pip install tox`` + +Run test for Python 2.7: ``tox -epy27`` + +Project status +-------------- +Pyrsistent can be considered stable and mature (who knows, there may even be a 1.0 some day :-)). The project is +maintained, bugs fixed, PRs reviewed and merged and new releases made. I currently do not have time for development +of new features or functionality which I don't have use for myself. I'm more than happy to take PRs for new +functionality though! + +There are a bunch of issues marked with ``enhancement`` and ``help wanted`` that contain requests for new functionality +that would be nice to include. The level of difficulty and extend of the issues varies, please reach out to me if you're +interested in working on any of them. + +If you feel that you have a grand master plan for where you would like Pyrsistent to go and have the time to put into +it please don't hesitate to discuss this with me and submit PRs for it. If all goes well I'd be more than happy to add +additional maintainers to the project! diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/README.rst new/pyrsistent-0.14.4/README.rst --- old/pyrsistent-0.13.0/README.rst 2017-09-01 11:53:08.000000000 +0200 +++ new/pyrsistent-0.14.4/README.rst 2018-07-07 15:53:48.000000000 +0200 @@ -562,8 +562,8 @@ Compatibility ------------- -Pyrsistent is developed and tested on Python 2.6, 2.7, 3.4, 3.5 and PyPy (Python 2.7 compatible). It will most likely work -on all other versions >= 3.4 but no guarantees are given. :) +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 +likely work on all other versions >= 3.4 but no guarantees are given. :) Compatibility issues ~~~~~~~~~~~~~~~~~~~~ @@ -635,7 +635,39 @@ Julian Berman https://github.com/Julian +Dennis Tomas https://github.com/dtomas + +Neil Vyas https://github.com/neilvyas + +doozr https://github.com/doozr + Contributing ------------ -If you experience problems please log them on GitHub. If you want to contribute code, please fork the code and submit a pull request. +Want to contribute? That's great! If you experience problems please log them on GitHub. If you want to contribute code, +please fork the repository and submit a pull request. + +Run tests +~~~~~~~~~ +.. _tox: https://tox.readthedocs.io/en/latest/ + +Tests can be executed using tox_. + +Install tox: ``pip install tox`` + +Run test for Python 2.7: ``tox -epy27`` + +Project status +-------------- +Pyrsistent can be considered stable and mature (who knows, there may even be a 1.0 some day :-)). The project is +maintained, bugs fixed, PRs reviewed and merged and new releases made. I currently do not have time for development +of new features or functionality which I don't have use for myself. I'm more than happy to take PRs for new +functionality though! + +There are a bunch of issues marked with ``enhancement`` and ``help wanted`` that contain requests for new functionality +that would be nice to include. The level of difficulty and extend of the issues varies, please reach out to me if you're +interested in working on any of them. + +If you feel that you have a grand master plan for where you would like Pyrsistent to go and have the time to put into +it please don't hesitate to discuss this with me and submit PRs for it. If all goes well I'd be more than happy to add +additional maintainers to the project! diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/_pyrsistent_version.py new/pyrsistent-0.14.4/_pyrsistent_version.py --- old/pyrsistent-0.13.0/_pyrsistent_version.py 2017-09-01 11:51:54.000000000 +0200 +++ new/pyrsistent-0.14.4/_pyrsistent_version.py 2018-07-08 11:51:36.000000000 +0200 @@ -1 +1 @@ -__version__ = '0.13.0' +__version__ = '0.14.4' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/pvectorcmodule.c new/pyrsistent-0.14.4/pvectorcmodule.c --- old/pyrsistent-0.13.0/pvectorcmodule.c 2016-04-03 08:41:08.000000000 +0200 +++ new/pyrsistent-0.14.4/pvectorcmodule.c 2018-07-07 15:53:48.000000000 +0200 @@ -365,6 +365,16 @@ vlen = vt->count; wlen = wt->count; + if (vlen != wlen) { + if (op == Py_EQ) { + Py_INCREF(Py_False); + return Py_False; + } else if (op == Py_NE) { + Py_INCREF(Py_True); + return Py_True; + } + } + /* Search for the first index where items are different. */ PyObject *left = NULL; PyObject *right = NULL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/pyrsistent/_checked_types.py new/pyrsistent-0.14.4/pyrsistent/_checked_types.py --- old/pyrsistent-0.13.0/pyrsistent/_checked_types.py 2017-09-01 11:44:01.000000000 +0200 +++ new/pyrsistent-0.14.4/pyrsistent/_checked_types.py 2018-07-07 15:53:48.000000000 +0200 @@ -1,5 +1,7 @@ from collections import Iterable import six + +from pyrsistent._compat import Enum, string_types from pyrsistent._pmap import PMap, pmap from pyrsistent._pset import PSet, pset from pyrsistent._pvector import PythonPVector, python_pvector @@ -12,7 +14,7 @@ __slots__ = () @classmethod - def create(cls, source_data, _bypass_factories=False): + def create(cls, source_data, _factory_fields=None): raise NotImplementedError() def serialize(self, format=None): @@ -20,7 +22,7 @@ def _restore_pickle(cls, data): - return cls.create(data, _bypass_factories=True) + return cls.create(data, _factory_fields=set()) class InvariantException(Exception): @@ -45,17 +47,61 @@ missing_fields=', '.join(self.missing_fields)) +_preserved_iterable_types = ( + Enum, +) +"""Some types are themselves iterable, but we want to use the type itself and +not its members for the type specification. This defines a set of such types +that we explicitly preserve. + +Note that strings are not such types because the string inputs we pass in are +values, not types. +""" + + +def maybe_parse_user_type(t): + """Try to coerce a user-supplied type directive into a list of types. + + This function should be used in all places where a user specifies a type, + for consistency. + + The policy for what defines valid user input should be clear from the implementation. + """ + is_type = isinstance(t, type) + is_preserved = isinstance(t, type) and issubclass(t, _preserved_iterable_types) + is_string = isinstance(t, string_types) + is_iterable = isinstance(t, Iterable) + + if is_preserved: + return [t] + elif is_string: + return [t] + elif is_type and not is_iterable: + return [t] + elif is_iterable: + # Recur to validate contained types as well. + ts = t + return tuple(e for t in ts for e in maybe_parse_user_type(t)) + else: + # If this raises because `t` cannot be formatted, so be it. + raise TypeError( + 'Type specifications must be types or strings. Input: {}'.format(t) + ) + + +def maybe_parse_many_user_types(ts): + # Just a different name to communicate that you're parsing multiple user + # inputs. `maybe_parse_user_type` handles the iterable case anyway. + return maybe_parse_user_type(ts) + + def _store_types(dct, bases, destination_name, source_name): - def to_list(elem): - if not isinstance(elem, Iterable) or isinstance(elem, six.string_types): - return [elem] - return list(elem) - - dct[destination_name] = to_list(dct[source_name]) if source_name in dct else [] - dct[destination_name] += sum([to_list(b.__dict__[source_name]) for b in bases if source_name in b.__dict__], []) - dct[destination_name] = tuple(dct[destination_name]) - if not all(isinstance(t, type) or isinstance(t, six.string_types) for t in dct[destination_name]): - raise TypeError('Type specifications must be types or strings') + maybe_types = maybe_parse_many_user_types([ + d[source_name] + for d in ([dct] + [b.__dict__ for b in bases]) if source_name in d + ]) + + dct[destination_name] = maybe_types def _merge_invariant_results(result): @@ -207,20 +253,20 @@ return tuple(typs) + (type(None),) -def _checked_type_create(cls, source_data, _bypass_factories=False): - if isinstance(source_data, cls): - return source_data - - # Recursively apply create methods of checked types if the types of the supplied data - # does not match any of the valid types. - types = get_types(cls._checked_types) - checked_type = next((t for t in types if issubclass(t, CheckedType)), None) - if checked_type: - return cls([checked_type.create(data) - if not any(isinstance(data, t) for t in types) else data - for data in source_data]) +def _checked_type_create(cls, source_data, _factory_fields=None): + if isinstance(source_data, cls): + return source_data + + # Recursively apply create methods of checked types if the types of the supplied data + # does not match any of the valid types. + types = get_types(cls._checked_types) + checked_type = next((t for t in types if issubclass(t, CheckedType)), None) + if checked_type: + return cls([checked_type.create(data) + if not any(isinstance(data, t) for t in types) else data + for data in source_data]) - return cls(source_data) + return cls(source_data) @six.add_metaclass(_CheckedTypeMeta) class CheckedPVector(PythonPVector, CheckedType): @@ -447,7 +493,7 @@ return dict(serializer(format, k, v) for k, v in self.items()) @classmethod - def create(cls, source_data, _bypass_factories=False): + def create(cls, source_data, _factory_fields=None): if isinstance(source_data, cls): return source_data diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/pyrsistent/_compat.py new/pyrsistent-0.14.4/pyrsistent/_compat.py --- old/pyrsistent-0.13.0/pyrsistent/_compat.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyrsistent-0.14.4/pyrsistent/_compat.py 2018-07-07 15:53:48.000000000 +0200 @@ -0,0 +1,9 @@ +from six import string_types + + +# enum compat +try: + from enum import Enum +except: + class Enum(object): pass + # no objects will be instances of this class diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/pyrsistent/_field_common.py new/pyrsistent-0.14.4/pyrsistent/_field_common.py --- old/pyrsistent-0.13.0/pyrsistent/_field_common.py 2017-06-04 10:28:26.000000000 +0200 +++ new/pyrsistent-0.14.4/pyrsistent/_field_common.py 2018-07-07 15:53:48.000000000 +0200 @@ -1,23 +1,20 @@ from collections import Iterable import six + from pyrsistent._checked_types import ( - CheckedType, CheckedPSet, CheckedPMap, CheckedPVector, - optional as optional_type, InvariantException, get_type, wrap_invariant, - _restore_pickle) - - -try: - from enum import Enum as _Enum -except: - class _Enum(object): pass - # no objects will be instances of this class - - -def isenum(type_): - try: - return issubclass(type_, _Enum) - except TypeError: - return False # type_ is not a class + CheckedPMap, + CheckedPSet, + CheckedPVector, + CheckedType, + InvariantException, + _restore_pickle, + get_type, + maybe_parse_user_type, + maybe_parse_many_user_types, +) +from pyrsistent._checked_types import optional as optional_type +from pyrsistent._checked_types import wrap_invariant +from pyrsistent._compat import Enum def set_fields(dct, bases, name): @@ -91,11 +88,19 @@ :param serializer: function that returns a serialized version of the field """ - if isinstance(type, Iterable) and not isinstance(type, six.string_types) and not isenum(type): - # Enums and strings are iterable types - types = set(type) + # NB: We have to check this predicate separately from the predicates in + # `maybe_parse_user_type` et al. because this one is related to supporting + # the argspec for `field`, while those are related to supporting the valid + # ways to specify types. + + # Multiple types must be passed in one of the following containers. Note + # that a type that is a subclass of one of these containers, like a + # `collections.namedtuple`, will work as expected, since we check + # `isinstance` and not `issubclass`. + if isinstance(type, (list, set, tuple)): + types = set(maybe_parse_many_user_types(type)) else: - types = set([type]) + types = set(maybe_parse_user_type(type)) invariant_function = wrap_invariant(invariant) if invariant != PFIELD_NO_INVARIANT and callable(invariant) else invariant field = _PField(type=types, invariant=invariant_function, initial=initial, @@ -159,7 +164,7 @@ def _types_to_names(types): """Convert a tuple of types to a human-readable string.""" - return "".join(typ.__name__.capitalize() for typ in types) + return "".join(get_type(typ).__name__.capitalize() for typ in types) def _make_seq_field_type(checked_class, item_type): """Create a subclass of the given checked class with the given item type.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/pyrsistent/_immutable.py new/pyrsistent-0.14.4/pyrsistent/_immutable.py --- old/pyrsistent-0.13.0/pyrsistent/_immutable.py 2015-09-28 20:48:39.000000000 +0200 +++ new/pyrsistent-0.14.4/pyrsistent/_immutable.py 2018-07-08 09:16:39.000000000 +0200 @@ -1,3 +1,5 @@ +import sys + import six @@ -60,9 +62,14 @@ return '' + verbose_string = "" + if sys.version_info < (3, 7): + # Verbose is no longer supported in Python 3.7 + verbose_string = ", verbose={verbose}".format(verbose=verbose) + quoted_members = ', '.join("'%s'" % m for m in members) template = """ -class {class_name}(namedtuple('ImmutableBase', [{quoted_members}], verbose={verbose})): +class {class_name}(namedtuple('ImmutableBase', [{quoted_members}]{verbose_string})): __slots__ = tuple() def __repr__(self): @@ -82,7 +89,7 @@ """.format(quoted_members=quoted_members, member_set="set([%s])" % quoted_members if quoted_members else 'set()', frozen_member_test=frozen_member_test(), - verbose=verbose, + verbose_string=verbose_string, class_name=name) if verbose: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/pyrsistent/_pclass.py new/pyrsistent-0.14.4/pyrsistent/_pclass.py --- old/pyrsistent-0.13.0/pyrsistent/_pclass.py 2017-09-01 11:44:01.000000000 +0200 +++ new/pyrsistent-0.14.4/pyrsistent/_pclass.py 2018-07-07 15:53:48.000000000 +0200 @@ -45,12 +45,12 @@ """ def __new__(cls, **kwargs): # Support *args? result = super(PClass, cls).__new__(cls) - bypass_factories = kwargs.pop('_bypass_factories', False) + factory_fields = kwargs.pop('_factory_fields', None) missing_fields = [] invariant_errors = [] for name, field in cls._pclass_fields.items(): if name in kwargs: - if not bypass_factories: + if factory_fields is None or name in factory_fields: value = field.factory(kwargs[name]) else: value = kwargs[name] @@ -97,16 +97,18 @@ if args: kwargs[args[0]] = args[1] + factory_fields = set(kwargs) + for key in self._pclass_fields: if key not in kwargs: value = getattr(self, key, _MISSING_VALUE) if value is not _MISSING_VALUE: kwargs[key] = value - return self.__class__(**kwargs) + return self.__class__(_factory_fields=factory_fields, **kwargs) @classmethod - def create(cls, kwargs, _bypass_factories=False): + def create(cls, kwargs, _factory_fields=None): """ Factory method. Will create a new PClass of the current type and assign the values specified in kwargs. @@ -114,7 +116,7 @@ if isinstance(kwargs, cls): return kwargs - return cls(_bypass_factories=_bypass_factories, **kwargs) + return cls(_factory_fields=_factory_fields, **kwargs) def serialize(self, format=None): """ @@ -199,12 +201,13 @@ class _PClassEvolver(object): - __slots__ = ('_pclass_evolver_original', '_pclass_evolver_data', '_pclass_evolver_data_is_dirty') + __slots__ = ('_pclass_evolver_original', '_pclass_evolver_data', '_pclass_evolver_data_is_dirty', '_factory_fields') def __init__(self, original, initial_dict): self._pclass_evolver_original = original self._pclass_evolver_data = initial_dict self._pclass_evolver_data_is_dirty = False + self._factory_fields = set() def __getitem__(self, item): return self._pclass_evolver_data[item] @@ -212,6 +215,7 @@ def set(self, key, value): if self._pclass_evolver_data.get(key, _MISSING_VALUE) is not value: self._pclass_evolver_data[key] = value + self._factory_fields.add(key) self._pclass_evolver_data_is_dirty = True return self @@ -222,6 +226,7 @@ def remove(self, item): if item in self._pclass_evolver_data: del self._pclass_evolver_data[item] + self._factory_fields.discard(item) self._pclass_evolver_data_is_dirty = True return self @@ -232,7 +237,8 @@ def persistent(self): if self._pclass_evolver_data_is_dirty: - return self._pclass_evolver_original.__class__(**self._pclass_evolver_data) + return self._pclass_evolver_original.__class__(_factory_fields=self._factory_fields, + **self._pclass_evolver_data) return self._pclass_evolver_original diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/pyrsistent/_pmap.py new/pyrsistent-0.14.4/pyrsistent/_pmap.py --- old/pyrsistent-0.13.0/pyrsistent/_pmap.py 2017-02-26 14:48:27.000000000 +0100 +++ new/pyrsistent-0.14.4/pyrsistent/_pmap.py 2018-07-07 15:53:48.000000000 +0200 @@ -22,7 +22,8 @@ semantics are the same (more or less) the same function names have been used but for some cases it is not possible, for example assignments and deletion of values. - PMap implements the Mapping protocol and is Hashable. + PMap implements the Mapping protocol and is Hashable. It also supports dot-notation for + element access. Random access and insert is log32(n) where n is the size of the map. @@ -39,6 +40,8 @@ pmap({'c': 3, 'b': 3}) >>> m3['c'] 3 + >>> m3.c + 3 """ __slots__ = ('_size', '_buckets', '__weakref__', '_cached_hash') @@ -91,7 +94,9 @@ try: return self[key] except KeyError: - raise AttributeError("PMap has no attribute '{0}'".format(key)) + raise AttributeError( + "{0} has no attribute '{1}'".format(type(self).__name__, key) + ) def iterkeys(self): for k, _ in self.iteritems(): @@ -125,7 +130,24 @@ def __repr__(self): return 'pmap({0})'.format(str(dict(self))) - __eq__ = Mapping.__eq__ + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, Mapping): + return NotImplemented + if len(self) != len(other): + return False + if isinstance(other, PMap): + if (hasattr(self, '_cached_hash') and hasattr(other, '_cached_hash') + and self._cached_hash != other._cached_hash): + return False + if self._buckets == other._buckets: + return True + return dict(self.iteritems()) == dict(other.iteritems()) + elif isinstance(other, dict): + return dict(self.iteritems()) == other + return dict(self.iteritems()) == dict(six.iteritems(other)) + __ne__ = Mapping.__ne__ def __lt__(self, other): @@ -378,7 +400,16 @@ def _turbo_mapping(initial, pre_size): - size = pre_size or (2 * len(initial)) or 8 + if pre_size: + size = pre_size + else: + try: + size = 2 * len(initial) or 8 + except Exception: + # Guess we can't figure out the length. Give up on length hinting, + # we can always reallocate later. + size = 8 + buckets = size * [None] if not isinstance(initial, Mapping): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/pyrsistent/_precord.py new/pyrsistent-0.14.4/pyrsistent/_precord.py --- old/pyrsistent-0.13.0/pyrsistent/_precord.py 2017-09-01 11:44:01.000000000 +0200 +++ new/pyrsistent-0.14.4/pyrsistent/_precord.py 2018-07-07 15:53:48.000000000 +0200 @@ -38,7 +38,7 @@ if '_precord_size' in kwargs and '_precord_buckets' in kwargs: return super(PRecord, cls).__new__(cls, kwargs['_precord_size'], kwargs['_precord_buckets']) - bypass_factories = kwargs.pop('_bypass_factories', False) + factory_fields = kwargs.pop('_factory_fields', None) initial_values = kwargs if cls._precord_initial_values: @@ -46,7 +46,7 @@ for k, v in cls._precord_initial_values.items()) initial_values.update(kwargs) - e = _PRecordEvolver(cls, pmap(), _bypass_factories=bypass_factories) + e = _PRecordEvolver(cls, pmap(), _factory_fields=factory_fields) for k, v in initial_values.items(): e[k] = v @@ -77,7 +77,7 @@ ', '.join('{0}={1}'.format(k, repr(v)) for k, v in self.items())) @classmethod - def create(cls, kwargs, _bypass_factories=False): + def create(cls, kwargs, _factory_fields=None): """ Factory method. Will create a new PRecord of the current type and assign the values specified in kwargs. @@ -85,7 +85,7 @@ if isinstance(kwargs, cls): return kwargs - return cls(_bypass_factories=_bypass_factories, **kwargs) + return cls(_factory_fields=_factory_fields, **kwargs) def __reduce__(self): # Pickling support @@ -100,14 +100,14 @@ class _PRecordEvolver(PMap._Evolver): - __slots__ = ('_destination_cls', '_invariant_error_codes', '_missing_fields', '_bypass_factories') + __slots__ = ('_destination_cls', '_invariant_error_codes', '_missing_fields', '_factory_fields') - def __init__(self, cls, original_pmap, _bypass_factories=False): + def __init__(self, cls, original_pmap, _factory_fields=None): super(_PRecordEvolver, self).__init__(original_pmap) self._destination_cls = cls self._invariant_error_codes = [] self._missing_fields = [] - self._bypass_factories = _bypass_factories + self._factory_fields = _factory_fields def __setitem__(self, key, original_value): self.set(key, original_value) @@ -115,7 +115,7 @@ def set(self, key, original_value): field = self._destination_cls._precord_fields.get(key) if field: - if not self._bypass_factories: + if self._factory_fields is None or field in self._factory_fields: try: value = field.factory(original_value) except InvariantException as e: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/pyrsistent/_pvector.py new/pyrsistent-0.14.4/pyrsistent/_pvector.py --- old/pyrsistent-0.13.0/pyrsistent/_pvector.py 2015-11-01 21:27:28.000000000 +0100 +++ new/pyrsistent-0.14.4/pyrsistent/_pvector.py 2018-07-07 15:53:48.000000000 +0200 @@ -76,10 +76,10 @@ return iter(self.tolist()) def __ne__(self, other): - return compare_pvector(self, other, operator.ne) + return self._count != len(other) or compare_pvector(self, other, operator.ne) def __eq__(self, other): - return self is other or compare_pvector(self, other, operator.eq) + return self is other or self._count == len(other) and compare_pvector(self, other, operator.eq) def __gt__(self, other): return compare_pvector(self, other, operator.gt) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/pyrsistent.egg-info/PKG-INFO new/pyrsistent-0.14.4/pyrsistent.egg-info/PKG-INFO --- old/pyrsistent-0.13.0/pyrsistent.egg-info/PKG-INFO 2017-09-01 13:11:16.000000000 +0200 +++ new/pyrsistent-0.14.4/pyrsistent.egg-info/PKG-INFO 2018-07-08 11:57:12.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pyrsistent -Version: 0.13.0 +Version: 0.14.4 Summary: Persistent/Functional/Immutable data structures Home-page: http://github.com/tobgu/pyrsistent/ Author: Tobias Gustafsson @@ -570,8 +570,8 @@ Compatibility ------------- - Pyrsistent is developed and tested on Python 2.6, 2.7, 3.4, 3.5 and PyPy (Python 2.7 compatible). It will most likely work - on all other versions >= 3.4 but no guarantees are given. :) + 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 + likely work on all other versions >= 3.4 but no guarantees are given. :) Compatibility issues ~~~~~~~~~~~~~~~~~~~~ @@ -643,18 +643,50 @@ Julian Berman https://github.com/Julian + Dennis Tomas https://github.com/dtomas + + Neil Vyas https://github.com/neilvyas + + doozr https://github.com/doozr + Contributing ------------ - If you experience problems please log them on GitHub. If you want to contribute code, please fork the code and submit a pull request. + Want to contribute? That's great! If you experience problems please log them on GitHub. If you want to contribute code, + please fork the repository and submit a pull request. + + Run tests + ~~~~~~~~~ + .. _tox: https://tox.readthedocs.io/en/latest/ + + Tests can be executed using tox_. + + Install tox: ``pip install tox`` + + Run test for Python 2.7: ``tox -epy27`` + + Project status + -------------- + Pyrsistent can be considered stable and mature (who knows, there may even be a 1.0 some day :-)). The project is + maintained, bugs fixed, PRs reviewed and merged and new releases made. I currently do not have time for development + of new features or functionality which I don't have use for myself. I'm more than happy to take PRs for new + functionality though! + + There are a bunch of issues marked with ``enhancement`` and ``help wanted`` that contain requests for new functionality + that would be nice to include. The level of difficulty and extend of the issues varies, please reach out to me if you're + interested in working on any of them. + + If you feel that you have a grand master plan for where you would like Pyrsistent to go and have the time to put into + it please don't hesitate to discuss this with me and submit PRs for it. If all goes well I'd be more than happy to add + additional maintainers to the project! Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 2.6 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 Classifier: Programming Language :: Python :: Implementation :: PyPy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/pyrsistent.egg-info/SOURCES.txt new/pyrsistent-0.14.4/pyrsistent.egg-info/SOURCES.txt --- old/pyrsistent-0.13.0/pyrsistent.egg-info/SOURCES.txt 2017-09-01 13:11:16.000000000 +0200 +++ new/pyrsistent-0.14.4/pyrsistent.egg-info/SOURCES.txt 2018-07-08 11:57:12.000000000 +0200 @@ -8,6 +8,7 @@ setup.py pyrsistent/__init__.py pyrsistent/_checked_types.py +pyrsistent/_compat.py pyrsistent/_field_common.py pyrsistent/_helpers.py pyrsistent/_immutable.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/setup.py new/pyrsistent-0.14.4/setup.py --- old/pyrsistent-0.13.0/setup.py 2017-02-26 18:20:54.000000000 +0100 +++ new/pyrsistent-0.14.4/setup.py 2018-07-08 10:28:47.000000000 +0200 @@ -71,11 +71,11 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.6', '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', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/tests/bag_test.py new/pyrsistent-0.14.4/tests/bag_test.py --- old/pyrsistent-0.13.0/tests/bag_test.py 2017-01-06 18:12:59.000000000 +0100 +++ new/pyrsistent-0.14.4/tests/bag_test.py 2018-07-07 15:53:48.000000000 +0200 @@ -140,3 +140,11 @@ def test_update_no_elements(): b = pbag([1, 2, 2]) assert b.update([]) is b + + +def test_iterable(): + """ + PBags can be created from iterables even though they can't be len() hinted. + """ + + assert pbag(iter("a")) == pbag(iter("a")) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/tests/class_test.py new/pyrsistent-0.14.4/tests/class_test.py --- old/pyrsistent-0.13.0/tests/class_test.py 2017-09-01 11:44:01.000000000 +0200 +++ new/pyrsistent-0.14.4/tests/class_test.py 2018-07-07 15:53:48.000000000 +0200 @@ -23,6 +23,7 @@ class UniqueThing(PClass): id = field(type=uuid.UUID, factory=uuid.UUID) + x = field(type=int) def test_evolve_pclass_instance(): @@ -407,6 +408,46 @@ MyClass2() + def test_pickle_with_one_way_factory(): thing = UniqueThing(id='25544626-86da-4bce-b6b6-9186c0804d64') assert pickle.loads(pickle.dumps(thing)) == thing + + +def test_evolver_with_one_way_factory(): + thing = UniqueThing(id='cc65249a-56fe-4995-8719-ea02e124b234') + ev = thing.evolver() + ev.x = 5 # necessary to prevent persistent() returning the original + assert ev.persistent() == UniqueThing(id=str(thing.id), x=5) + + +def test_set_doesnt_trigger_other_factories(): + thing = UniqueThing(id='b413b280-de76-4e28-a8e3-5470ca83ea2c') + thing.set(x=5) + + +def test_set_does_trigger_factories(): + class SquaredPoint(PClass): + x = field(factory=lambda x: x ** 2) + y = field() + + sp = SquaredPoint(x=3, y=10) + assert (sp.x, sp.y) == (9, 10) + + sp2 = sp.set(x=4) + assert (sp2.x, sp2.y) == (16, 10) + + +def test_value_can_be_overridden_in_subclass_new(): + class X(PClass): + y = pvector_field(int) + + def __new__(cls, **kwargs): + items = kwargs.get('y', None) + if items is None: + kwargs['y'] = () + return super(X, cls).__new__(cls, **kwargs) + + a = X(y=[]) + b = a.set(y=None) + assert a == b diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/tests/deque_test.py new/pyrsistent-0.14.4/tests/deque_test.py --- old/pyrsistent-0.13.0/tests/deque_test.py 2017-09-01 11:44:01.000000000 +0200 +++ new/pyrsistent-0.14.4/tests/deque_test.py 2018-07-07 15:53:48.000000000 +0200 @@ -282,3 +282,12 @@ def test_supports_weakref(): import weakref weakref.ref(dq(1, 2)) + + +def test_iterable(): + """ + PDeques can be created from iterables even though they can't be len() + hinted. + """ + + assert pdeque(iter("a")) == pdeque(iter("a")) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/tests/field_test.py new/pyrsistent-0.14.4/tests/field_test.py --- old/pyrsistent-0.13.0/tests/field_test.py 2017-06-04 10:28:26.000000000 +0200 +++ new/pyrsistent-0.14.4/tests/field_test.py 2018-07-07 15:53:48.000000000 +0200 @@ -1,18 +1,27 @@ -from pyrsistent import field +from pyrsistent._compat import Enum +from pyrsistent import field, pvector_field -def test_enum(): - try: - from enum import Enum - except ImportError: - # skip enum test if Enums are not available - return - - class TestEnum(Enum): - x = 1 - y = 2 +# NB: This derives from the internal `pyrsistent._compat.Enum` in order to +# simplify coverage across python versions. Since we use +# `pyrsistent._compat.Enum` in `pyrsistent`'s implementation, it's useful to +# use it in the test coverage as well, for consistency. +class TestEnum(Enum): + x = 1 + y = 2 + + +def test_enum(): f = field(type=TestEnum) assert TestEnum in f.type assert len(f.type) == 1 + + +# This is meant to exercise `_seq_field`. +def test_pvector_field_enum_type(): + f = pvector_field(TestEnum) + + assert len(f.type) == 1 + assert TestEnum is list(f.type)[0].__type__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/tests/list_test.py new/pyrsistent-0.14.4/tests/list_test.py --- old/pyrsistent-0.13.0/tests/list_test.py 2015-09-28 20:40:21.000000000 +0200 +++ new/pyrsistent-0.14.4/tests/list_test.py 2018-07-07 15:53:48.000000000 +0200 @@ -197,4 +197,13 @@ def test_supports_weakref(): import weakref weakref.ref(plist()) - weakref.ref(plist([1, 2])) \ No newline at end of file + weakref.ref(plist([1, 2])) + + +def test_iterable(): + """ + PLists can be created from iterables even though they can't be len() + hinted. + """ + + assert plist(iter("a")) == plist(iter("a")) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/tests/map_test.py new/pyrsistent-0.14.4/tests/map_test.py --- old/pyrsistent-0.13.0/tests/map_test.py 2017-02-26 14:48:27.000000000 +0100 +++ new/pyrsistent-0.14.4/tests/map_test.py 2018-07-07 15:53:48.000000000 +0200 @@ -117,13 +117,11 @@ assert map2['a'] == 3 -def test_supports_hash_and_equals(): +def test_hash(): x = m(a=1, b=2, c=3) y = m(a=1, b=2, c=3) - + assert hash(x) == hash(y) - assert x == y - assert not (x != y) def test_same_hash_when_content_the_same_but_underlying_vector_size_differs(): @@ -156,6 +154,72 @@ hash(x) +def test_equal(): + x = m(a=1, b=2, c=3) + y = m(a=1, b=2, c=3) + + assert x == y + assert not (x != y) + + assert y == x + assert not (y != x) + + +def test_equal_to_dict(): + x = m(a=1, b=2, c=3) + y = dict(a=1, b=2, c=3) + + assert x == y + assert not (x != y) + + assert y == x + assert not (y != x) + + +def test_equal_with_different_bucket_sizes(): + x = pmap({'a': 1, 'b': 2}, 50) + y = pmap({'a': 1, 'b': 2}, 10) + + assert x == y + assert not (x != y) + + assert y == x + assert not (y != x) + + +def test_equal_with_different_insertion_order(): + x = pmap([(i, i) for i in range(50)], 10) + y = pmap([(i, i) for i in range(49, -1, -1)], 10) + + assert x == y + assert not (x != y) + + assert y == x + assert not (y != x) + + +def test_not_equal(): + x = m(a=1, b=2, c=3) + y = m(a=1, b=2) + + assert x != y + assert not (x == y) + + assert y != x + assert not (y == x) + + +def test_not_equal_to_dict(): + x = m(a=1, b=2, c=3) + y = dict(a=1, b=2, d=4) + + assert x != y + assert not (x == y) + + assert y != x + assert not (y == x) + + def test_update_with_multiple_arguments(): # If same value is present in multiple sources, the rightmost is used. x = m(a=1, b=2, c=3) @@ -399,7 +463,10 @@ with pytest.raises(AttributeError) as error: m1.b - assert "'b'" in str(error.value) + error_message = str(error.value) + + assert "'b'" in error_message + assert type(m1).__name__ in error_message def test_pmap_unorderable(): @@ -419,3 +486,11 @@ def test_supports_weakref(): import weakref weakref.ref(m(a=1)) + + +def test_iterable(): + """ + PMaps can be created from iterables even though they can't be len() hinted. + """ + + assert pmap(iter([("a", "b")])) == pmap([("a", "b")]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/tests/record_test.py new/pyrsistent-0.14.4/tests/record_test.py --- old/pyrsistent-0.13.0/tests/record_test.py 2017-09-01 11:44:01.000000000 +0200 +++ new/pyrsistent-0.14.4/tests/record_test.py 2018-07-07 15:53:48.000000000 +0200 @@ -24,6 +24,12 @@ id = field(type=uuid.UUID, factory=uuid.UUID) +class Something(object): + pass + +class Another(object): + pass + def test_create(): r = ARecord(x=1, y='foo') assert r.x == 1 @@ -443,9 +449,6 @@ """ The created set class name is based on the type of items in the set. """ - class Something(object): - pass - class Record(PRecord): value = pset_field(Something) value2 = pset_field(int) @@ -458,15 +461,31 @@ The created set class name is based on the multiple given types of items in the set. """ - class Something(object): - pass - class Record(PRecord): value = pset_field((Something, int)) assert (Record().value.__class__.__name__ == "SomethingIntPSet") +def test_pset_field_name_string_type(): + """ + The created set class name is based on the type of items specified by name + """ + class Record(PRecord): + value = pset_field("record_test.Something") + assert Record().value.__class__.__name__ == "SomethingPSet" + + +def test_pset_multiple_string_types_field_name(): + """ + The created set class name is based on the multiple given types of + items in the set specified by name + """ + class Record(PRecord): + value = pset_field(("record_test.Something", "record_test.Another")) + + assert Record().value.__class__.__name__ == "SomethingAnotherPSet" + def test_pvector_field_initial_value(): """ ``pvector_field`` results in initial value that is empty. @@ -566,9 +585,6 @@ """ The created set class name is based on the type of items in the set. """ - class Something(object): - pass - class Record(PRecord): value = pvector_field(Something) value2 = pvector_field(int) @@ -581,15 +597,31 @@ The created vector class name is based on the multiple given types of items in the vector. """ - class Something(object): - pass - class Record(PRecord): value = pvector_field((Something, int)) assert (Record().value.__class__.__name__ == "SomethingIntPVector") +def test_pvector_field_name_string_type(): + """ + The created set class name is based on the type of items in the set + specified by name. + """ + class Record(PRecord): + value = pvector_field("record_test.Something") + assert Record().value.__class__.__name__ == "SomethingPVector" + +def test_pvector_multiple_string_types_field_name(): + """ + The created vector class name is based on the multiple given types of + items in the vector. + """ + class Record(PRecord): + value = pvector_field(("record_test.Something", "record_test.Another")) + + assert Record().value.__class__.__name__ == "SomethingAnotherPVector" + def test_pvector_field_create_from_nested_serialized_data(): class Foo(PRecord): foo = field(type=str) @@ -696,19 +728,13 @@ """ class Record(PRecord): value = pmap_field(int, int, optional=True) - assert ((Record(value={1: 2}).value, Record(value=None).value) == - pmap({1: 2}), None) + assert (Record(value={1: 2}).value, Record(value=None).value) == \ + (pmap({1: 2}), None) def test_pmap_field_name(): """ The created map class name is based on the types of items in the map. """ - class Something(object): - pass - - class Another(object): - pass - class Record(PRecord): value = pmap_field(Something, Another) value2 = pmap_field(int, float) @@ -721,12 +747,6 @@ The created map class name is based on the types of items in the map, including when there are multiple supported types. """ - class Something(object): - pass - - class Another(object): - pass - class Record(PRecord): value = pmap_field((Something, Another), int) value2 = pmap_field(str, (int, float)) @@ -734,6 +754,27 @@ Record().value2.__class__.__name__) == ("SomethingAnotherToIntPMap", "StrToIntFloatPMap")) +def test_pmap_field_name_string_type(): + """ + The created map class name is based on the types of items in the map + specified by name. + """ + class Record(PRecord): + value = pmap_field("record_test.Something", "record_test.Another") + assert Record().value.__class__.__name__ == "SomethingToAnotherPMap" + +def test_pmap_field_name_multiple_string_types(): + """ + The created map class name is based on the types of items in the map, + including when there are multiple supported types. + """ + class Record(PRecord): + value = pmap_field(("record_test.Something", "record_test.Another"), int) + value2 = pmap_field(str, ("record_test.Something", "record_test.Another")) + assert ((Record().value.__class__.__name__, + Record().value2.__class__.__name__) == + ("SomethingAnotherToIntPMap", "StrToSomethingAnotherPMap")) + def test_pmap_field_invariant(): """ The ``invariant`` parameter is passed through to ``field``. @@ -774,6 +815,7 @@ assert MyRecord() == MyRecord(a=2) + def test_pickle_with_one_way_factory(): """ A field factory isn't called when restoring from pickle. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/tests/set_test.py new/pyrsistent-0.14.4/tests/set_test.py --- old/pyrsistent-0.13.0/tests/set_test.py 2017-01-06 18:16:36.000000000 +0100 +++ new/pyrsistent-0.14.4/tests/set_test.py 2018-07-07 15:53:48.000000000 +0200 @@ -168,3 +168,11 @@ def test_update_no_elements(): s1 = s(1, 2) assert s1.update([]) is s1 + + +def test_iterable(): + """ + PSets can be created from iterables even though they can't be len() hinted. + """ + + assert pset(iter("a")) == pset(iter("a")) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyrsistent-0.13.0/tests/vector_test.py new/pyrsistent-0.14.4/tests/vector_test.py --- old/pyrsistent-0.13.0/tests/vector_test.py 2017-02-26 18:11:22.000000000 +0100 +++ new/pyrsistent-0.14.4/tests/vector_test.py 2018-07-07 15:53:48.000000000 +0200 @@ -907,3 +907,12 @@ with pytest.raises(ValueError): repr(pvector([A()])) + + +def test_iterable(pvector): + """ + PVectors can be created from iterables even though they can't be len() + hinted. + """ + + assert pvector(iter("a")) == pvector(iter("a"))