Hello community, here is the log from the commit of package python-toolz for openSUSE:Factory checked in at 2019-07-22 17:19:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-toolz (Old) and /work/SRC/openSUSE:Factory/.python-toolz.new.4126 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-toolz" Mon Jul 22 17:19:38 2019 rev:5 rq:717570 version:0.10.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-toolz/python-toolz.changes 2019-06-07 18:02:08.937781993 +0200 +++ /work/SRC/openSUSE:Factory/.python-toolz.new.4126/python-toolz.changes 2019-07-22 17:19:39.725906411 +0200 @@ -1,0 +2,6 @@ +Mon Jul 22 12:35:27 UTC 2019 - Tomáš Chvátal <tchva...@suse.com> + +- Update to 0.10.0: + * no upstream changelog + +------------------------------------------------------------------- Old: ---- toolz-0.9.0.tar.gz New: ---- toolz-0.10.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-toolz.spec ++++++ --- /var/tmp/diff_new_pack.rYf2m5/_old 2019-07-22 17:19:40.389906229 +0200 +++ /var/tmp/diff_new_pack.rYf2m5/_new 2019-07-22 17:19:40.389906229 +0200 @@ -18,12 +18,12 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-toolz -Version: 0.9.0 +Version: 0.10.0 Release: 0 Summary: List processing tools and functional utilities for python License: BSD-3-Clause Group: Development/Languages/Python -URL: http://github.com/pytoolz/toolz/ +URL: https://github.com/pytoolz/toolz/ Source: https://files.pythonhosted.org/packages/source/t/toolz/toolz-%{version}.tar.gz BuildRequires: %{python_module setuptools} BuildRequires: fdupes ++++++ toolz-0.9.0.tar.gz -> toolz-0.10.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/PKG-INFO new/toolz-0.10.0/PKG-INFO --- old/toolz-0.9.0/PKG-INFO 2017-12-17 17:40:27.000000000 +0100 +++ new/toolz-0.10.0/PKG-INFO 2019-07-11 22:22:27.000000000 +0200 @@ -1,16 +1,16 @@ -Metadata-Version: 1.1 +Metadata-Version: 1.2 Name: toolz -Version: 0.9.0 +Version: 0.10.0 Summary: List processing tools and functional utilities -Home-page: http://github.com/pytoolz/toolz/ -Author: Matthew Rocklin -Author-email: mrock...@gmail.com +Home-page: https://github.com/pytoolz/toolz/ +Author: https://raw.github.com/pytoolz/toolz/master/AUTHORS.md +Maintainer: Matthew Rocklin +Maintainer-email: mrock...@gmail.com License: BSD -Description-Content-Type: UNKNOWN Description: Toolz ===== - |Build Status| |Coverage Status| |Version Status| |Downloads| + |Build Status| |Coverage Status| |Version Status| A set of utility functions for iterators, functions, and dictionaries. @@ -39,7 +39,7 @@ ``unique``, ``interpose``, |literal functoolz|_, for higher-order functions. Examples: ``memoize``, - ``curry``, ``compose`` + ``curry``, ``compose``, |literal dicttoolz|_, for operations on dictionaries. Examples: ``assoc``, ``update-in``, ``merge``. @@ -82,11 +82,11 @@ Dependencies ------------ - ``toolz`` supports Python 2.6+ and Python 3.3+ with a common codebase. + ``toolz`` supports Python 2.7 and Python 3.4+ with a common codebase. It is pure Python and requires no dependencies beyond the standard library. - It is, in short, a light weight dependency. + It is, in short, a lightweight dependency. CyToolz @@ -95,21 +95,21 @@ The ``toolz`` project has been reimplemented in `Cython <http://cython.org>`__. The ``cytoolz`` project is a drop-in replacement for the Pure Python implementation. - See `CyToolz Github Page <https://github.com/pytoolz/cytoolz/>`__ for more + See `CyToolz GitHub Page <https://github.com/pytoolz/cytoolz/>`__ for more details. See Also -------- - - `Underscore.js <http://underscorejs.org>`__: A similar library for + - `Underscore.js <https://underscorejs.org/>`__: A similar library for JavaScript - - `Enumerable <http://ruby-doc.org/core-2.0.0/Enumerable.html>`__: A + - `Enumerable <https://ruby-doc.org/core-2.0.0/Enumerable.html>`__: A similar library for Ruby - - `Clojure <http://clojure.org>`__: A functional language whose + - `Clojure <https://clojure.org/>`__: A functional language whose standard library has several counterparts in ``toolz`` - - `itertools <http://docs.python.org/2/library/itertools.html>`__: The + - `itertools <https://docs.python.org/2/library/itertools.html>`__: The Python standard library for iterator tools - - `functools <http://docs.python.org/2/library/functools.html>`__: The + - `functools <https://docs.python.org/2/library/functools.html>`__: The Python standard library for function tools Contributions Welcome @@ -138,21 +138,20 @@ .. |Coverage Status| image:: https://coveralls.io/repos/pytoolz/toolz/badge.svg?branch=master :target: https://coveralls.io/r/pytoolz/toolz .. |Version Status| image:: https://badge.fury.io/py/toolz.svg - :target: http://badge.fury.io/py/toolz - .. |Downloads| image:: https://img.shields.io/pypi/dm/toolz.svg - :target: https://pypi.python.org/pypi/toolz/ + :target: https://badge.fury.io/py/toolz Keywords: functional utility itertools functools Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 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 :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/README.rst new/toolz-0.10.0/README.rst --- old/toolz-0.9.0/README.rst 2016-11-03 16:51:39.000000000 +0100 +++ new/toolz-0.10.0/README.rst 2019-06-22 18:08:05.000000000 +0200 @@ -1,7 +1,7 @@ Toolz ===== -|Build Status| |Coverage Status| |Version Status| |Downloads| +|Build Status| |Coverage Status| |Version Status| A set of utility functions for iterators, functions, and dictionaries. @@ -30,7 +30,7 @@ ``unique``, ``interpose``, |literal functoolz|_, for higher-order functions. Examples: ``memoize``, -``curry``, ``compose`` +``curry``, ``compose``, |literal dicttoolz|_, for operations on dictionaries. Examples: ``assoc``, ``update-in``, ``merge``. @@ -73,11 +73,11 @@ Dependencies ------------ -``toolz`` supports Python 2.6+ and Python 3.3+ with a common codebase. +``toolz`` supports Python 2.7 and Python 3.4+ with a common codebase. It is pure Python and requires no dependencies beyond the standard library. -It is, in short, a light weight dependency. +It is, in short, a lightweight dependency. CyToolz @@ -86,21 +86,21 @@ The ``toolz`` project has been reimplemented in `Cython <http://cython.org>`__. The ``cytoolz`` project is a drop-in replacement for the Pure Python implementation. -See `CyToolz Github Page <https://github.com/pytoolz/cytoolz/>`__ for more +See `CyToolz GitHub Page <https://github.com/pytoolz/cytoolz/>`__ for more details. See Also -------- -- `Underscore.js <http://underscorejs.org>`__: A similar library for +- `Underscore.js <https://underscorejs.org/>`__: A similar library for JavaScript -- `Enumerable <http://ruby-doc.org/core-2.0.0/Enumerable.html>`__: A +- `Enumerable <https://ruby-doc.org/core-2.0.0/Enumerable.html>`__: A similar library for Ruby -- `Clojure <http://clojure.org>`__: A functional language whose +- `Clojure <https://clojure.org/>`__: A functional language whose standard library has several counterparts in ``toolz`` -- `itertools <http://docs.python.org/2/library/itertools.html>`__: The +- `itertools <https://docs.python.org/2/library/itertools.html>`__: The Python standard library for iterator tools -- `functools <http://docs.python.org/2/library/functools.html>`__: The +- `functools <https://docs.python.org/2/library/functools.html>`__: The Python standard library for function tools Contributions Welcome @@ -129,6 +129,4 @@ .. |Coverage Status| image:: https://coveralls.io/repos/pytoolz/toolz/badge.svg?branch=master :target: https://coveralls.io/r/pytoolz/toolz .. |Version Status| image:: https://badge.fury.io/py/toolz.svg - :target: http://badge.fury.io/py/toolz -.. |Downloads| image:: https://img.shields.io/pypi/dm/toolz.svg - :target: https://pypi.python.org/pypi/toolz/ + :target: https://badge.fury.io/py/toolz diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/setup.py new/toolz-0.10.0/setup.py --- old/toolz-0.9.0/setup.py 2017-01-16 07:11:54.000000000 +0100 +++ new/toolz-0.10.0/setup.py 2019-06-22 18:08:05.000000000 +0200 @@ -7,7 +7,7 @@ setup(name='toolz', version=toolz.__version__, description='List processing tools and functional utilities', - url='http://github.com/pytoolz/toolz/', + url='https://github.com/pytoolz/toolz/', author='https://raw.github.com/pytoolz/toolz/master/AUTHORS.md', maintainer='Matthew Rocklin', maintainer_email='mrock...@gmail.com', @@ -21,16 +21,17 @@ long_description=(open('README.rst').read() if exists('README.rst') else ''), zip_safe=False, + python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", classifiers=[ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: BSD License", "Programming Language :: Python", - "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy"]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/tlz/_build_tlz.py new/toolz-0.10.0/tlz/_build_tlz.py --- old/toolz-0.9.0/tlz/_build_tlz.py 2016-11-03 16:58:17.000000000 +0100 +++ new/toolz-0.10.0/tlz/_build_tlz.py 2019-06-22 18:08:05.000000000 +0200 @@ -1,15 +1,15 @@ import sys import types import toolz -from toolz.compatibility import import_module +from importlib import import_module class TlzLoader(object): """ Finds and loads ``tlz`` modules when added to sys.meta_path""" def __init__(self): - self.always_from_toolz = set([ + self.always_from_toolz = { toolz.pipe, - ]) + } def _load_toolz(self, fullname): rv = {} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/__init__.py new/toolz-0.10.0/toolz/__init__.py --- old/toolz-0.9.0/toolz/__init__.py 2017-12-17 17:37:52.000000000 +0100 +++ new/toolz-0.10.0/toolz/__init__.py 2019-07-11 22:08:55.000000000 +0200 @@ -8,8 +8,6 @@ from .compatibility import map, filter -from . import sandbox - from functools import partial, reduce sorted = sorted @@ -17,6 +15,8 @@ # Aliases comp = compose +from . import curried, sandbox + functoolz._sigs.create_signature_registry() -__version__ = '0.9.0' +__version__ = '0.10.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/_signatures.py new/toolz-0.10.0/toolz/_signatures.py --- old/toolz-0.9.0/toolz/_signatures.py 2016-11-03 16:51:39.000000000 +0100 +++ new/toolz-0.10.0/toolz/_signatures.py 2019-07-10 18:12:28.000000000 +0200 @@ -16,8 +16,9 @@ import inspect import itertools import operator +from importlib import import_module -from .compatibility import PY3, import_module +from .compatibility import PY3 from .functoolz import (is_partial_args, is_arity, has_varargs, has_keywords, num_required_args) @@ -236,6 +237,8 @@ if PY3: # pragma: py2 no cover module_info[builtins].update( + breakpoint=[ + lambda *args, **kws: None], bytes=[ lambda: None, lambda int: None, @@ -640,8 +643,8 @@ def signature_or_spec(func): try: return inspect.signature(func) - except (ValueError, TypeError) as e: - return e + except (ValueError, TypeError): + return None else: # pragma: py3 no cover def num_pos_args(sigspec): @@ -662,8 +665,8 @@ def signature_or_spec(func): try: return inspect.getargspec(func) - except TypeError as e: - return e + except TypeError: + return None def expand_sig(sig): @@ -697,7 +700,7 @@ num_pos_only = num_pos_args(sigspec) keyword_only = () keyword_exclude = get_exclude_keywords(num_pos_only, sigspec) - return (num_pos_only, func, keyword_only + keyword_exclude, sigspec) + return num_pos_only, func, keyword_only + keyword_exclude, sigspec signatures = {} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/compatibility.py new/toolz-0.10.0/toolz/compatibility.py --- old/toolz-0.9.0/toolz/compatibility.py 2016-11-03 16:51:39.000000000 +0100 +++ new/toolz-0.10.0/toolz/compatibility.py 2019-06-22 18:08:05.000000000 +0200 @@ -1,13 +1,12 @@ import operator import sys PY3 = sys.version_info[0] > 2 -PY33 = sys.version_info[0] == 3 and sys.version_info[1] == 3 PY34 = sys.version_info[0] == 3 and sys.version_info[1] == 4 PYPY = hasattr(sys, 'pypy_version_info') __all__ = ('map', 'filter', 'range', 'zip', 'reduce', 'zip_longest', 'iteritems', 'iterkeys', 'itervalues', 'filterfalse', - 'PY3', 'PY34', 'PYPY', 'import_module') + 'PY3', 'PY34', 'PYPY') if PY3: map = map @@ -20,6 +19,7 @@ iteritems = operator.methodcaller('items') iterkeys = operator.methodcaller('keys') itervalues = operator.methodcaller('values') + from collections.abc import Sequence else: range = xrange reduce = reduce @@ -31,10 +31,4 @@ iteritems = operator.methodcaller('iteritems') iterkeys = operator.methodcaller('iterkeys') itervalues = operator.methodcaller('itervalues') - -try: - from importlib import import_module -except ImportError: - def import_module(name): - __import__(name) - return sys.modules[name] + from collections import Sequence diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/curried/__init__.py new/toolz-0.10.0/toolz/curried/__init__.py --- old/toolz-0.9.0/toolz/curried/__init__.py 2016-11-03 19:33:43.000000000 +0100 +++ new/toolz-0.10.0/toolz/curried/__init__.py 2019-07-10 17:30:32.000000000 +0200 @@ -26,15 +26,16 @@ import toolz from . import operator from toolz import ( + apply, comp, complement, compose, + compose_left, concat, concatv, count, curry, diff, - dissoc, first, flip, frequencies, @@ -59,6 +60,7 @@ assoc_in = toolz.curry(toolz.assoc_in) cons = toolz.curry(toolz.cons) countby = toolz.curry(toolz.countby) +dissoc = toolz.curry(toolz.dissoc) do = toolz.curry(toolz.do) drop = toolz.curry(toolz.drop) excepts = toolz.curry(toolz.excepts) @@ -80,6 +82,7 @@ partition = toolz.curry(toolz.partition) partition_all = toolz.curry(toolz.partition_all) partitionby = toolz.curry(toolz.partitionby) +peekn = toolz.curry(toolz.peekn) pluck = toolz.curry(toolz.pluck) random_sample = toolz.curry(toolz.random_sample) reduce = toolz.curry(toolz.reduce) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/curried/operator.py new/toolz-0.10.0/toolz/curried/operator.py --- old/toolz-0.9.0/toolz/curried/operator.py 2016-06-12 17:40:05.000000000 +0200 +++ new/toolz-0.10.0/toolz/curried/operator.py 2019-06-22 18:08:05.000000000 +0200 @@ -11,8 +11,8 @@ locals().update( - dict((name, curry(f) if should_curry(f) else f) - for name, f in vars(operator).items() if callable(f)), + {name: curry(f) if should_curry(f) else f + for name, f in vars(operator).items() if callable(f)}, ) # Clean up the namespace. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/dicttoolz.py new/toolz-0.10.0/toolz/dicttoolz.py --- old/toolz-0.9.0/toolz/dicttoolz.py 2016-11-24 21:37:56.000000000 +0100 +++ new/toolz-0.10.0/toolz/dicttoolz.py 2019-07-09 16:35:35.000000000 +0200 @@ -1,4 +1,3 @@ -import copy import operator from toolz.compatibility import (map, zip, iteritems, iterkeys, itervalues, reduce) @@ -11,8 +10,8 @@ def _get_factory(f, kwargs): factory = kwargs.pop('factory', dict) if kwargs: - raise TypeError("{0}() got an unexpected keyword argument " - "'{1}'".format(f.__name__, kwargs.popitem()[0])) + raise TypeError("{}() got an unexpected keyword argument " + "'{}'".format(f.__name__, kwargs.popitem()[0])) return factory @@ -192,11 +191,12 @@ {'x': 1, 'y': 3} """ d2 = factory() + d2.update(d) d2[key] = value - return merge(d, d2, factory=factory) + return d2 -def dissoc(d, *keys): +def dissoc(d, *keys, **kwargs): """ Return a new dict with the given key(s) removed. New dict has d[key] deleted for each supplied key. @@ -209,10 +209,19 @@ >>> dissoc({'x': 1}, 'y') # Ignores missing keys {'x': 1} """ - d2 = copy.copy(d) - for key in keys: - if key in d2: - del d2[key] + factory = _get_factory(dissoc, kwargs) + d2 = factory() + + if len(keys) < len(d) * .6: + d2.update(d) + for key in keys: + if key in d2: + del d2[key] + else: + remaining = set(d) + remaining.difference_update(keys) + for k in remaining: + d2[k] = d[k] return d2 @@ -265,15 +274,28 @@ >>> update_in({1: 'foo'}, [2, 3, 4], inc, 0) {1: 'foo', 2: {3: {4: 1}}} """ - assert len(keys) > 0 - k, ks = keys[0], keys[1:] - if ks: - return assoc(d, k, update_in(d[k] if (k in d) else factory(), - ks, func, default, factory), - factory) + ks = iter(keys) + k = next(ks) + + rv = inner = factory() + rv.update(d) + + for key in ks: + if k in d: + d = d[k] + dtemp = factory() + dtemp.update(d) + else: + d = dtemp = factory() + + inner[k] = inner = dtemp + k = key + + if k in d: + inner[k] = func(d[k]) else: - innermost = func(d[k]) if (k in d) else func(default) - return assoc(d, k, innermost, factory) + inner[k] = func(default) + return rv def get_in(keys, coll, default=None, no_default=False): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/functoolz.py new/toolz-0.10.0/toolz/functoolz.py --- old/toolz-0.9.0/toolz/functoolz.py 2017-12-17 06:16:26.000000000 +0100 +++ new/toolz-0.10.0/toolz/functoolz.py 2019-07-10 17:30:32.000000000 +0200 @@ -2,14 +2,17 @@ import inspect import operator from operator import attrgetter +from importlib import import_module from textwrap import dedent +from types import MethodType -from .compatibility import PY3, PY33, PY34, PYPY, import_module +from .compatibility import PY3, PY34, PYPY from .utils import no_default -__all__ = ('identity', 'thread_first', 'thread_last', 'memoize', 'compose', - 'pipe', 'complement', 'juxt', 'do', 'curry', 'flip', 'excepts') +__all__ = ('identity', 'apply', 'thread_first', 'thread_last', 'memoize', + 'compose', 'compose_left', 'pipe', 'complement', 'juxt', 'do', + 'curry', 'flip', 'excepts') def identity(x): @@ -21,6 +24,23 @@ return x +def apply(*func_and_args, **kwargs): + """ Applies a function and returns the results + + >>> def double(x): return 2*x + >>> def inc(x): return x + 1 + >>> apply(double, 5) + 10 + + >>> tuple(map(apply, [double, inc, double], [10, 500, 8000])) + (20, 501, 16000) + """ + if not func_and_args: + raise TypeError('func argument is required') + func, args = func_and_args[0], func_and_args[1:] + return func(*args, **kwargs) + + def thread_first(val, *forms): """ Thread value through a sequence of functions/forms @@ -351,7 +371,7 @@ if k not in ('_partial', '_sigspec')) state = (type(self), func, self.args, self.keywords, userdict, is_decorated) - return (_restore_curry, state) + return _restore_curry, state def _restore_curry(cls, func, args, kwargs, userdict, is_decorated): @@ -498,11 +518,50 @@ def __name__(self): try: return '_of_'.join( - f.__name__ for f in reversed((self.first,) + self.funcs) + (f.__name__ for f in reversed((self.first,) + self.funcs)) ) except AttributeError: return type(self).__name__ + def __repr__(self): + return '{.__class__.__name__}{!r}'.format( + self, tuple(reversed((self.first, ) + self.funcs))) + + def __eq__(self, other): + if isinstance(other, Compose): + return other.first == self.first and other.funcs == self.funcs + return NotImplemented + + def __ne__(self, other): + equality = self.__eq__(other) + return NotImplemented if equality is NotImplemented else not equality + + def __hash__(self): + return hash(self.first) ^ hash(self.funcs) + + # Mimic the descriptor behavior of python functions. + # i.e. let Compose be called as a method when bound to a class. + if PY3: # pragma: py2 no cover + # adapted from + # docs.python.org/3/howto/descriptor.html#functions-and-methods + def __get__(self, obj, objtype=None): + return self if obj is None else MethodType(self, obj) + else: # pragma: py3 no cover + # adapted from + # docs.python.org/2/howto/descriptor.html#functions-and-methods + def __get__(self, obj, objtype=None): + return self if obj is None else MethodType(self, obj, objtype) + + # introspection with Signature is only possible from py3.3+ + if PY3: # pragma: py2 no cover + @instanceproperty + def __signature__(self): + base = inspect.signature(self.first) + last = inspect.signature(self.funcs[-1]) + return base.replace(return_annotation=last.return_annotation) + + __wrapped__ = instanceproperty(attrgetter('first')) + def compose(*funcs): """ Compose functions to operate in series. @@ -519,6 +578,7 @@ '4' See Also: + compose_left pipe """ if not funcs: @@ -529,6 +589,27 @@ return Compose(funcs) +def compose_left(*funcs): + """ Compose functions to operate in series. + + Returns a function that applies other functions in sequence. + + Functions are applied from left to right so that + ``compose_left(f, g, h)(x, y)`` is the same as ``h(g(f(x, y)))``. + + If no arguments are provided, the identity function (f(x) = x) is returned. + + >>> inc = lambda i: i + 1 + >>> compose_left(inc, str)(3) + '4' + + See Also: + compose + pipe + """ + return compose(*reversed(funcs)) + + def pipe(data, *funcs): """ Pipe a value through a sequence of functions @@ -545,6 +626,7 @@ See Also: compose + compose_left thread_first thread_last """ @@ -758,11 +840,6 @@ and (( hasattr(func, '__signature__') and hasattr(func.__signature__, '__get__') - ) or ( - PY33 - and hasattr(func, '__wrapped__') - and hasattr(func.__wrapped__, '__get__') - and not callable(func.__wrapped__) )) ): # pragma: no cover (not covered in Python 3.4) val = builtin_func(*builtin_args) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/itertoolz.py new/toolz-0.10.0/toolz/itertoolz.py --- old/toolz-0.9.0/toolz/itertoolz.py 2017-12-16 17:18:22.000000000 +0100 +++ new/toolz-0.10.0/toolz/itertoolz.py 2019-07-09 16:35:35.000000000 +0200 @@ -5,7 +5,7 @@ from functools import partial from random import Random from toolz.compatibility import (map, filterfalse, zip, zip_longest, iteritems, - filter) + filter, Sequence) from toolz.utils import no_default @@ -14,7 +14,7 @@ 'first', 'second', 'nth', 'last', 'get', 'concat', 'concatv', 'mapcat', 'cons', 'interpose', 'frequencies', 'reduceby', 'iterate', 'sliding_window', 'partition', 'partition_all', 'count', 'pluck', - 'join', 'tail', 'diff', 'topk', 'peek', 'random_sample') + 'join', 'tail', 'diff', 'topk', 'peek', 'peekn', 'random_sample') def remove(predicate, seq): @@ -56,7 +56,13 @@ itertools.accumulate : In standard itertools for Python 3.2+ """ seq = iter(seq) - result = next(seq) if initial == no_default else initial + if initial == no_default: + try: + result = next(seq) + except StopIteration: + return + else: + result = initial yield result for elem in seq: result = binop(result, elem) @@ -83,6 +89,8 @@ 'M': [{'gender': 'M', 'name': 'Bob'}, {'gender': 'M', 'name': 'Charlie'}]} + Not to be confused with ``itertools.groupby`` + See Also: countby """ @@ -374,7 +382,9 @@ >>> second('ABC') 'B' """ - return next(itertools.islice(seq, 1, None)) + seq = iter(seq) + next(seq) + return next(seq) def nth(n, seq): @@ -383,7 +393,7 @@ >>> nth(1, 'ABC') 'B' """ - if isinstance(seq, (tuple, list, collections.Sequence)): + if isinstance(seq, (tuple, list, Sequence)): return seq[n] else: return next(itertools.islice(seq, n, None)) @@ -449,7 +459,7 @@ if len(ind) > 1: return operator.itemgetter(*ind)(seq) elif ind: - return (seq[ind[0]],) + return seq[ind[0]], else: return () else: @@ -720,7 +730,23 @@ yield prev prev = item if prev[-1] is no_pad: - yield prev[:prev.index(no_pad)] + try: + # If seq defines __len__, then + # we can quickly calculate where no_pad starts + yield prev[:len(seq) % n] + except TypeError: + # Get first index of no_pad without using .index() + # https://github.com/pytoolz/toolz/issues/387 + # Binary search from CPython's bisect module, + # modified for identity testing. + lo, hi = 0, n + while lo < hi: + mid = (lo + hi) // 2 + if prev[mid] is no_pad: + hi = mid + else: + lo = mid + 1 + yield prev[:lo] else: yield prev @@ -792,6 +818,8 @@ This is a semi-streaming operation. The LEFT sequence is fully evaluated and placed into memory. The RIGHT sequence is evaluated lazily and so can be arbitrarily large. + (Note: If right_default is defined, then unique keys of rightseq + will also be stored in memory.) >>> friends = [('Alice', 'Edith'), ... ('Alice', 'Zhao'), @@ -834,7 +862,10 @@ Usually the key arguments are callables to be applied to the sequences. If the keys are not obviously callable then it is assumed that indexing was - intended, e.g. the following is a legal change + intended, e.g. the following is a legal change. + The join is implemented as a hash join and the keys of leftseq must be + hashable. Additionally, if right_default is defined, then keys of rightseq + must also be hashable. >>> # result = join(second, friends, first, cities) >>> result = join(1, friends, 0, cities) # doctest: +SKIP @@ -845,22 +876,47 @@ rightkey = getter(rightkey) d = groupby(leftkey, leftseq) - seen_keys = set() - left_default_is_no_default = (left_default == no_default) - for item in rightseq: - key = rightkey(item) - seen_keys.add(key) - try: - left_matches = d[key] - for match in left_matches: - yield (match, item) - except KeyError: - if not left_default_is_no_default: + if left_default == no_default and right_default == no_default: + # Inner Join + for item in rightseq: + key = rightkey(item) + if key in d: + for left_match in d[key]: + yield (left_match, item) + elif left_default != no_default and right_default == no_default: + # Right Join + for item in rightseq: + key = rightkey(item) + if key in d: + for left_match in d[key]: + yield (left_match, item) + else: yield (left_default, item) + elif right_default != no_default: + seen_keys = set() + seen = seen_keys.add + + if left_default == no_default: + # Left Join + for item in rightseq: + key = rightkey(item) + seen(key) + if key in d: + for left_match in d[key]: + yield (left_match, item) + else: + # Full Join + for item in rightseq: + key = rightkey(item) + seen(key) + if key in d: + for left_match in d[key]: + yield (left_match, item) + else: + yield (left_default, item) - if right_default != no_default: - for key, matches in d.items(): + for key, matches in iteritems(d): if key not in seen_keys: for match in matches: yield (match, right_default) @@ -942,7 +998,25 @@ """ iterator = iter(seq) item = next(iterator) - return item, itertools.chain([item], iterator) + return item, itertools.chain((item,), iterator) + + +def peekn(n, seq): + """ Retrieve the next n elements of a sequence + + Returns a tuple of the first n elements and an iterable equivalent + to the original, still having the elements retrieved. + + >>> seq = [0, 1, 2, 3, 4] + >>> first_two, seq = peekn(2, seq) + >>> first_two + (0, 1) + >>> list(seq) + [0, 1, 2, 3, 4] + """ + iterator = iter(seq) + peeked = tuple(take(n, iterator)) + return peeked, itertools.chain(iter(peeked), iterator) def random_sample(prob, seq, random_state=None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/sandbox/core.py new/toolz-0.10.0/toolz/sandbox/core.py --- old/toolz-0.9.0/toolz/sandbox/core.py 2016-01-19 05:24:57.000000000 +0100 +++ new/toolz-0.10.0/toolz/sandbox/core.py 2019-06-22 18:08:05.000000000 +0200 @@ -105,7 +105,7 @@ [1, 2] Unlike the naive implementation ``def unzip(seq): zip(*seq)`` this - implementation can handle a finite sequence of infinite sequences. + implementation can handle an infinite sequence ``seq``. Caveats: @@ -113,7 +113,8 @@ of auxiliary storage if the resulting iterators are consumed at different times. - * The top level sequence cannot be infinite. + * The inner sequence cannot be infinite. In Python 3 ``zip(*seq)`` can be + used if ``seq`` is a finite sequence of infinite sequences. """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/sandbox/parallel.py new/toolz-0.10.0/toolz/sandbox/parallel.py --- old/toolz-0.9.0/toolz/sandbox/parallel.py 2016-06-03 00:37:48.000000000 +0200 +++ new/toolz-0.10.0/toolz/sandbox/parallel.py 2019-07-09 17:54:53.000000000 +0200 @@ -1,8 +1,16 @@ +import functools from toolz.itertoolz import partition_all from toolz.compatibility import reduce, map from toolz.utils import no_default +def _reduce(func, seq, initial=None): + if initial is None: + return functools.reduce(func, seq) + else: + return functools.reduce(func, seq, initial) + + def fold(binop, seq, default=no_default, map=map, chunksize=128, combine=None): """ Reduce without guarantee of ordered reduction. @@ -43,6 +51,8 @@ >>> fold(add, [1, 2, 3, 4], chunksize=2, map=map) 10 """ + assert chunksize > 1 + if combine is None: combine = binop @@ -50,9 +60,13 @@ # Evaluate sequence in chunks via map if default == no_default: - results = map(lambda chunk: reduce(binop, chunk), chunks) + results = map( + functools.partial(_reduce, binop), + chunks) else: - results = map(lambda chunk: reduce(binop, chunk, default), chunks) + results = map( + functools.partial(_reduce, binop, initial=default), + chunks) results = list(results) # TODO: Support complete laziness diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/tests/test_curried.py new/toolz-0.10.0/toolz/tests/test_curried.py --- old/toolz-0.9.0/toolz/tests/test_curried.py 2016-11-03 19:33:43.000000000 +0100 +++ new/toolz-0.10.0/toolz/tests/test_curried.py 2019-07-10 02:50:48.000000000 +0200 @@ -2,8 +2,8 @@ import toolz.curried from toolz.curried import (take, first, second, sorted, merge_with, reduce, merge, operator as cop) -from toolz.compatibility import import_module from collections import defaultdict +from importlib import import_module from operator import add @@ -62,7 +62,7 @@ ) # Make sure this isn't totally empty. - assert len(set(vars(cop)) & set(['add', 'sub', 'mul'])) == 3 + assert len(set(vars(cop)) & {'add', 'sub', 'mul'}) == 3 def test_curried_namespace(): @@ -79,10 +79,10 @@ def curry_namespace(ns): - return dict( - (name, toolz.curry(f) if should_curry(f) else f) + return { + name: toolz.curry(f) if should_curry(f) else f for name, f in ns.items() if '__' not in name - ) + } from_toolz = curry_namespace(vars(toolz)) from_exceptions = curry_namespace(vars(exceptions)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/tests/test_dicttoolz.py new/toolz-0.10.0/toolz/tests/test_dicttoolz.py --- old/toolz-0.9.0/toolz/tests/test_dicttoolz.py 2016-01-19 05:24:57.000000000 +0100 +++ new/toolz-0.10.0/toolz/tests/test_dicttoolz.py 2019-07-11 21:26:11.000000000 +0200 @@ -1,7 +1,9 @@ from collections import defaultdict as _defaultdict +import os from toolz.dicttoolz import (merge, merge_with, valmap, keymap, update_in, assoc, dissoc, keyfilter, valfilter, itemmap, itemfilter, assoc_in) +from toolz.functoolz import identity from toolz.utils import raises from toolz.compatibility import PY3 @@ -90,16 +92,16 @@ def test_dissoc(self): D, kw = self.D, self.kw - assert dissoc(D({"a": 1}), "a") == D({}) - assert dissoc(D({"a": 1, "b": 2}), "a") == D({"b": 2}) - assert dissoc(D({"a": 1, "b": 2}), "b") == D({"a": 1}) - assert dissoc(D({"a": 1, "b": 2}), "a", "b") == D({}) - assert dissoc(D({"a": 1}), "a") == dissoc(dissoc(D({"a": 1}), "a"), "a") + assert dissoc(D({"a": 1}), "a", **kw) == D({}) + assert dissoc(D({"a": 1, "b": 2}), "a", **kw) == D({"b": 2}) + assert dissoc(D({"a": 1, "b": 2}), "b", **kw) == D({"a": 1}) + assert dissoc(D({"a": 1, "b": 2}), "a", "b", **kw) == D({}) + assert dissoc(D({"a": 1}), "a", **kw) == dissoc(dissoc(D({"a": 1}), "a", **kw), "a", **kw) # Verify immutability: d = D({'x': 1}) oldd = d - d2 = dissoc(d, 'x') + d2 = dissoc(d, 'x', **kw) assert d is oldd assert d2 is not oldd @@ -250,3 +252,10 @@ """ D = CustomMapping kw = {'factory': lambda: CustomMapping()} + + +def test_environ(): + # See: https://github.com/pytoolz/cytoolz/issues/127 + assert keymap(identity, os.environ) == os.environ + assert valmap(identity, os.environ) == os.environ + assert itemmap(identity, os.environ) == os.environ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/tests/test_functoolz.py new/toolz-0.10.0/toolz/tests/test_functoolz.py --- old/toolz-0.9.0/toolz/tests/test_functoolz.py 2016-12-11 22:04:13.000000000 +0100 +++ new/toolz-0.10.0/toolz/tests/test_functoolz.py 2019-07-10 17:30:32.000000000 +0200 @@ -1,7 +1,9 @@ -import platform - +import inspect +import toolz from toolz.functoolz import (thread_first, thread_last, memoize, curry, - compose, pipe, complement, do, juxt, flip, excepts) + compose, compose_left, pipe, complement, do, juxt, + flip, excepts, apply) +from toolz.compatibility import PY3 from operator import add, mul, itemgetter from toolz.utils import raises from functools import partial @@ -23,6 +25,32 @@ return 2 * x +class AlwaysEquals(object): + """useful to test correct __eq__ implementation of other objects""" + + def __eq__(self, other): + return True + + def __ne__(self, other): + return False + + +class NeverEquals(object): + """useful to test correct __eq__ implementation of other objects""" + + def __eq__(self, other): + return False + + def __ne__(self, other): + return True + + +def test_apply(): + assert apply(double, 5) == 10 + assert tuple(map(apply, [double, inc, double], [10, 500, 8000])) == (20, 501, 16000) + assert raises(TypeError, apply) + + def test_thread_first(): assert thread_first(2) == 2 assert thread_first(2, inc) == 3 @@ -201,12 +229,10 @@ def h(x, func=int): return func(x) - if platform.python_implementation() != 'PyPy'\ - or platform.python_version_tuple()[0] != '3': # Bug on PyPy3<2.5 - # __init__ must not pick func as positional arg - assert curry(h)(0.0) == 0 - assert curry(h)(func=str)(0.0) == '0.0' - assert curry(h, func=str)(0.0) == '0.0' + # __init__ must not pick func as positional arg + assert curry(h)(0.0) == 0 + assert curry(h)(func=str)(0.0) == '0.0' + assert curry(h, func=str)(0.0) == '0.0' def test_curry_passes_errors(): @@ -328,7 +354,7 @@ b1 = curry(bar, 1, c=2) assert b1 != f1 - assert set([f1, f2, g1, h1, h2, h3, b1, b1()]) == set([f1, g1, h1, b1]) + assert {f1, f2, g1, h1, h2, h3, b1, b1()} == {f1, g1, h1, b1} # test unhashable input unhash1 = curry(foo, []) @@ -499,17 +525,54 @@ """ -def test_compose(): - assert compose()(0) == 0 - assert compose(inc)(0) == 1 - assert compose(double, inc)(0) == 2 - assert compose(str, iseven, inc, double)(3) == "False" - assert compose(str, add)(1, 2) == '3' +def generate_compose_test_cases(): + """ + Generate test cases for parametrized tests of the compose function. + """ - def f(a, b, c=10): + def add_then_multiply(a, b, c=10): return (a + b) * c - assert compose(str, inc, f)(1, 2, c=3) == '10' + return ( + ( + (), # arguments to compose() + (0,), {}, # positional and keyword args to the Composed object + 0 # expected result + ), + ( + (inc,), + (0,), {}, + 1 + ), + ( + (double, inc), + (0,), {}, + 2 + ), + ( + (str, iseven, inc, double), + (3,), {}, + "False" + ), + ( + (str, add), + (1, 2), {}, + '3' + ), + ( + (str, inc, add_then_multiply), + (1, 2), {"c": 3}, + '10' + ), + ) + + +def test_compose(): + for (compose_args, args, kw, expected) in generate_compose_test_cases(): + assert compose(*compose_args)(*args, **kw) == expected + + +def test_compose_metadata(): # Define two functions with different names def f(a): @@ -529,6 +592,94 @@ assert composed.__name__ == 'Compose' assert composed.__doc__ == 'A composition of functions' + assert repr(composed) == 'Compose({!r}, {!r})'.format(f, h) + + assert composed == compose(f, h) + assert composed == AlwaysEquals() + assert not composed == compose(h, f) + assert not composed == object() + assert not composed == NeverEquals() + + assert composed != compose(h, f) + assert composed != NeverEquals() + assert composed != object() + assert not composed != compose(f, h) + assert not composed != AlwaysEquals() + + assert hash(composed) == hash(compose(f, h)) + assert hash(composed) != hash(compose(h, f)) + + bindable = compose(str, lambda x: x*2, lambda x, y=0: int(x) + y) + + class MyClass: + + def __int__(self): + return 8 + + my_method = bindable + my_static_method = staticmethod(bindable) + + assert MyClass.my_method(3) == '6' + assert MyClass.my_method(3, y=2) == '10' + assert MyClass.my_static_method(2) == '4' + assert MyClass().my_method() == '16' + assert MyClass().my_method(y=3) == '22' + assert MyClass().my_static_method(0) == '0' + assert MyClass().my_static_method(0, 1) == '2' + + assert compose(f, h).__wrapped__ is h + if hasattr(toolz, 'sandbox'): # only test this with Python version (i.e., not Cython) + assert compose(f, h).__class__.__wrapped__ is None + + # __signature__ is python3 only + if PY3: + + def myfunc(a, b, c, *d, **e): + return 4 + + def otherfunc(f): + return 'result: {}'.format(f) + + # set annotations compatibly with python2 syntax + myfunc.__annotations__ = { + 'a': int, + 'b': str, + 'c': float, + 'd': int, + 'e': bool, + 'return': int, + } + otherfunc.__annotations__ = {'f': int, 'return': str} + + composed = compose(otherfunc, myfunc) + sig = inspect.signature(composed) + assert sig.parameters == inspect.signature(myfunc).parameters + assert sig.return_annotation == str + + class MyClass: + method = composed + + assert len(inspect.signature(MyClass().method).parameters) == 4 + + +def generate_compose_left_test_cases(): + """ + Generate test cases for parametrized tests of the compose function. + + These are based on, and equivalent to, those produced by + enerate_compose_test_cases(). + """ + return tuple( + (tuple(reversed(compose_args)), args, kwargs, expected) + for (compose_args, args, kwargs, expected) + in generate_compose_test_cases() + ) + + +def test_compose_left(): + for (compose_left_args, args, kw, expected) in generate_compose_left_test_cases(): + assert compose_left(*compose_left_args)(*args, **kw) == expected + def test_pipe(): assert pipe(1, inc) == 2 @@ -645,4 +796,3 @@ excepting = excepts(object(), object(), object()) assert excepting.__name__ == 'excepting' assert excepting.__doc__ == excepts.__doc__ - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/tests/test_inspect_args.py new/toolz-0.10.0/toolz/tests/test_inspect_args.py --- old/toolz-0.9.0/toolz/tests/test_inspect_args.py 2017-12-17 06:16:26.000000000 +0100 +++ new/toolz-0.10.0/toolz/tests/test_inspect_args.py 2019-07-10 18:12:28.000000000 +0200 @@ -7,7 +7,7 @@ num_required_args, has_varargs, has_keywords) from toolz._signatures import builtins import toolz._signatures as _sigs -from toolz.compatibility import PY3, PY33 +from toolz.compatibility import PY3 from toolz.utils import raises @@ -402,7 +402,6 @@ blacklist.add(getattr(mod, attr)) add_blacklist(builtins, 'basestring') - add_blacklist(builtins, 'breakpoint') add_blacklist(builtins, 'NoneType') add_blacklist(builtins, '__metaclass__') add_blacklist(builtins, 'sequenceiterator') @@ -437,7 +436,7 @@ if missing: messages = [] for modname, names in sorted(missing.items()): - msg = '{0}:\n {1}'.format(modname, '\n '.join(sorted(names))) + msg = '{}:\n {}'.format(modname, '\n '.join(sorted(names))) messages.append(msg) message = 'Missing introspection for the following callables:\n\n' raise AssertionError(message + '\n\n'.join(messages)) @@ -495,6 +494,6 @@ if PY3: assert inspect.signature(func) == inspect.signature(wrapped) - assert num_required_args(Wrapped) == (False if PY33 else None) + assert num_required_args(Wrapped) is None _sigs.signatures[Wrapped] = (_sigs.expand_sig((0, lambda func: None)),) assert num_required_args(Wrapped) == 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/tests/test_itertoolz.py new/toolz-0.10.0/toolz/tests/test_itertoolz.py --- old/toolz-0.9.0/toolz/tests/test_itertoolz.py 2016-11-03 16:51:39.000000000 +0100 +++ new/toolz-0.10.0/toolz/tests/test_itertoolz.py 2019-07-10 18:14:42.000000000 +0200 @@ -13,7 +13,7 @@ reduceby, iterate, accumulate, sliding_window, count, partition, partition_all, take_nth, pluck, join, - diff, topk, peek, random_sample) + diff, topk, peek, peekn, random_sample) from toolz.compatibility import range, filter from operator import add, mul @@ -271,7 +271,7 @@ return s assert reduceby(iseven, set_add, [1, 2, 3, 4, 1, 2], set) == \ - {True: set([2, 4]), False: set([1, 3])} + {True: {2, 4}, False: {1, 3}} def test_iterate(): @@ -289,6 +289,7 @@ start = object() assert list(accumulate(binop, [], start)) == [start] + assert list(accumulate(binop, [])) == [] assert list(accumulate(add, [1, 2, 3], no_default2)) == [1, 3, 6] @@ -303,6 +304,7 @@ def test_sliding_window_of_short_iterator(): assert list(sliding_window(3, [1, 2])) == [] + assert list(sliding_window(7, [1, 2])) == [] def test_partition(): @@ -318,6 +320,17 @@ assert list(partition_all(3, range(5))) == [(0, 1, 2), (3, 4)] assert list(partition_all(2, [])) == [] + # Regression test: https://github.com/pytoolz/toolz/issues/387 + class NoCompare(object): + def __eq__(self, other): + if self.__class__ == other.__class__: + return True + raise ValueError() + obj = NoCompare() + result = [(obj, obj, obj, obj), (obj, obj, obj)] + assert list(partition_all(4, [obj]*7)) == result + assert list(partition_all(4, iter([obj]*7))) == result + def test_count(): assert count((1, 2, 3)) == 3 @@ -356,10 +369,10 @@ result = set(starmap(add, join(first, names, second, fruit))) - expected = set([((1, 'one', 'apple', 1)), - ((1, 'one', 'orange', 1)), - ((2, 'two', 'banana', 2)), - ((2, 'two', 'coconut', 2))]) + expected = {(1, 'one', 'apple', 1), + (1, 'one', 'orange', 1), + (2, 'two', 'banana', 2), + (2, 'two', 'coconut', 2)} assert result == expected @@ -397,14 +410,14 @@ result = set(starmap(add, join(first, names, second, fruit))) - expected = set([((1, 'one', 'apple', 1)), - ((1, 'one', 'orange', 1)), - ((2, 'two', 'banana', 2)), - ((2, 'two', 'coconut', 2)), - ((1, 'uno', 'apple', 1)), - ((1, 'uno', 'orange', 1)), - ((2, 'dos', 'banana', 2)), - ((2, 'dos', 'coconut', 2))]) + expected = {(1, 'one', 'apple', 1), + (1, 'one', 'orange', 1), + (2, 'two', 'banana', 2), + (2, 'two', 'coconut', 2), + (1, 'uno', 'apple', 1), + (1, 'uno', 'orange', 1), + (2, 'dos', 'banana', 2), + (2, 'dos', 'coconut', 2)} assert result == expected @@ -415,21 +428,21 @@ result = set(starmap(add, join(first, names, second, fruit))) - expected = set([((1, 'one', 'orange', 1))]) + expected = {(1, 'one', 'orange', 1)} assert result == expected def test_left_outer_join(): result = set(join(identity, [1, 2], identity, [2, 3], left_default=None)) - expected = set([(2, 2), (None, 3)]) + expected = {(2, 2), (None, 3)} assert result == expected def test_right_outer_join(): result = set(join(identity, [1, 2], identity, [2, 3], right_default=None)) - expected = set([(2, 2), (1, None)]) + expected = {(2, 2), (1, None)} assert result == expected @@ -437,7 +450,7 @@ def test_outer_join(): result = set(join(identity, [1, 2], identity, [2, 3], left_default=None, right_default=None)) - expected = set([(2, 2), (1, None), (None, 3)]) + expected = {(2, 2), (1, None), (None, 3)} assert result == expected @@ -496,12 +509,23 @@ def test_peek(): alist = ["Alice", "Bob", "Carol"] element, blist = peek(alist) - element == alist[0] + assert element == alist[0] assert list(blist) == alist assert raises(StopIteration, lambda: peek([])) +def test_peekn(): + alist = ("Alice", "Bob", "Carol") + elements, blist = peekn(2, alist) + assert elements == alist[:2] + assert tuple(blist) == alist + + elements, blist = peekn(len(alist) * 4, alist) + assert elements == alist + assert tuple(blist) == alist + + def test_random_sample(): alist = list(range(100)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz/tests/test_serialization.py new/toolz-0.10.0/toolz/tests/test_serialization.py --- old/toolz-0.9.0/toolz/tests/test_serialization.py 2017-01-16 07:11:54.000000000 +0100 +++ new/toolz-0.10.0/toolz/tests/test_serialization.py 2019-06-22 18:08:05.000000000 +0200 @@ -2,7 +2,7 @@ import toolz import toolz.curried import pickle -from toolz.compatibility import PY3, PY33, PY34 +from toolz.compatibility import PY3 from toolz.utils import raises @@ -81,7 +81,7 @@ def __reduce__(self): """Allow us to serialize instances of GlobalCurried""" - return (GlobalCurried, (self.x, self.y)) + return GlobalCurried, (self.x, self.y) @toolz.curry class NestedCurried(object): @@ -98,7 +98,7 @@ def __reduce__(self): """Allow us to serialize instances of NestedCurried""" - return (GlobalCurried.NestedCurried, (self.x, self.y)) + return GlobalCurried.NestedCurried, (self.x, self.y) class Nested(object): def __init__(self, x, y): @@ -148,7 +148,7 @@ # If we add `curry.__getattr__` forwarding, the following tests will pass - # if not PY33 and not PY34: + # if not PY34: # assert preserves_identity(GlobalCurried.func.g1) # assert preserves_identity(GlobalCurried.func.NestedCurried.func.g2) # assert preserves_identity(GlobalCurried.func.Nested) @@ -159,7 +159,7 @@ # assert preserves_identity(GlobalCurried.NestedCurried) # assert preserves_identity(GlobalCurried.NestedCurried.f2) # assert preserves_identity(GlobalCurried.Nested.f3) - # if not PY33 and not PY34: + # if not PY34: # assert preserves_identity(GlobalCurried.g1) # assert preserves_identity(GlobalCurried.NestedCurried.g2) # assert preserves_identity(GlobalCurried.Nested) @@ -175,7 +175,7 @@ # assert func1 is not func2 # assert func1(4) == func2(4) == 10 # - # if not PY33 and not PY34: + # if not PY34: # nested3 = GlobalCurried.func.Nested(1, 2) # nested4 = pickle.loads(pickle.dumps(nested3)) # assert nested3 is not nested4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/toolz-0.9.0/toolz.egg-info/PKG-INFO new/toolz-0.10.0/toolz.egg-info/PKG-INFO --- old/toolz-0.9.0/toolz.egg-info/PKG-INFO 2017-12-17 17:40:27.000000000 +0100 +++ new/toolz-0.10.0/toolz.egg-info/PKG-INFO 2019-07-11 22:22:27.000000000 +0200 @@ -1,16 +1,16 @@ -Metadata-Version: 1.1 +Metadata-Version: 1.2 Name: toolz -Version: 0.9.0 +Version: 0.10.0 Summary: List processing tools and functional utilities -Home-page: http://github.com/pytoolz/toolz/ -Author: Matthew Rocklin -Author-email: mrock...@gmail.com +Home-page: https://github.com/pytoolz/toolz/ +Author: https://raw.github.com/pytoolz/toolz/master/AUTHORS.md +Maintainer: Matthew Rocklin +Maintainer-email: mrock...@gmail.com License: BSD -Description-Content-Type: UNKNOWN Description: Toolz ===== - |Build Status| |Coverage Status| |Version Status| |Downloads| + |Build Status| |Coverage Status| |Version Status| A set of utility functions for iterators, functions, and dictionaries. @@ -39,7 +39,7 @@ ``unique``, ``interpose``, |literal functoolz|_, for higher-order functions. Examples: ``memoize``, - ``curry``, ``compose`` + ``curry``, ``compose``, |literal dicttoolz|_, for operations on dictionaries. Examples: ``assoc``, ``update-in``, ``merge``. @@ -82,11 +82,11 @@ Dependencies ------------ - ``toolz`` supports Python 2.6+ and Python 3.3+ with a common codebase. + ``toolz`` supports Python 2.7 and Python 3.4+ with a common codebase. It is pure Python and requires no dependencies beyond the standard library. - It is, in short, a light weight dependency. + It is, in short, a lightweight dependency. CyToolz @@ -95,21 +95,21 @@ The ``toolz`` project has been reimplemented in `Cython <http://cython.org>`__. The ``cytoolz`` project is a drop-in replacement for the Pure Python implementation. - See `CyToolz Github Page <https://github.com/pytoolz/cytoolz/>`__ for more + See `CyToolz GitHub Page <https://github.com/pytoolz/cytoolz/>`__ for more details. See Also -------- - - `Underscore.js <http://underscorejs.org>`__: A similar library for + - `Underscore.js <https://underscorejs.org/>`__: A similar library for JavaScript - - `Enumerable <http://ruby-doc.org/core-2.0.0/Enumerable.html>`__: A + - `Enumerable <https://ruby-doc.org/core-2.0.0/Enumerable.html>`__: A similar library for Ruby - - `Clojure <http://clojure.org>`__: A functional language whose + - `Clojure <https://clojure.org/>`__: A functional language whose standard library has several counterparts in ``toolz`` - - `itertools <http://docs.python.org/2/library/itertools.html>`__: The + - `itertools <https://docs.python.org/2/library/itertools.html>`__: The Python standard library for iterator tools - - `functools <http://docs.python.org/2/library/functools.html>`__: The + - `functools <https://docs.python.org/2/library/functools.html>`__: The Python standard library for function tools Contributions Welcome @@ -138,21 +138,20 @@ .. |Coverage Status| image:: https://coveralls.io/repos/pytoolz/toolz/badge.svg?branch=master :target: https://coveralls.io/r/pytoolz/toolz .. |Version Status| image:: https://badge.fury.io/py/toolz.svg - :target: http://badge.fury.io/py/toolz - .. |Downloads| image:: https://img.shields.io/pypi/dm/toolz.svg - :target: https://pypi.python.org/pypi/toolz/ + :target: https://badge.fury.io/py/toolz Keywords: functional utility itertools functools Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: BSD License Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 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 :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*