[Python-modules-commits] [toolz] 02/04: Import toolz_0.8.1.orig.tar.gz

Diane Trout diane at moszumanska.debian.org
Thu Dec 8 23:16:07 UTC 2016


This is an automated email from the git hooks/post-receive script.

diane pushed a commit to branch master
in repository toolz.

commit 8cb366cd24053493af87325dd2191bf5b507de02
Author: Diane Trout <diane at ghic.org>
Date:   Thu Dec 8 14:41:33 2016 -0800

    Import toolz_0.8.1.orig.tar.gz
---
 .travis.yml                          |   2 +-
 AUTHORS.md                           |   2 +
 MANIFEST.in                          |   2 +
 README.rst                           |  10 +-
 conda.recipe/meta.yaml               |   4 +-
 doc/source/control.rst               |   5 +-
 doc/source/streaming-analytics.rst   |  16 ++--
 setup.py                             |  17 +++-
 tlz/__init__.py                      |   9 ++
 tlz/_build_tlz.py                    | 100 +++++++++++++++++++
 toolz/__init__.py                    |   2 +-
 toolz/_signatures.py                 |   7 +-
 toolz/compatibility.py               |   9 +-
 toolz/curried/__init__.py            |  95 +++++++++++++-----
 toolz/curried/exceptions.py          |   1 +
 toolz/dicttoolz.py                   |   2 +-
 toolz/functoolz.py                   |  44 ++++++---
 toolz/itertoolz.py                   | 180 +++++++++++++++++++++--------------
 toolz/tests/test_curried.py          |  49 ++++++++++
 toolz/tests/test_curried_doctests.py |  11 +++
 toolz/tests/test_itertoolz.py        |  12 ++-
 toolz/tests/test_serialization.py    |  15 +++
 toolz/tests/test_tlz.py              |  55 +++++++++++
 23 files changed, 505 insertions(+), 144 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 2a24ac3..883d5fe 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,7 +21,7 @@ install:
 script:
     - export MAJOR_PYTHON_VERSION=`echo $TRAVIS_PYTHON_VERSION | cut -c 1`
     - coverage run --source=toolz $(which nosetests)
-                   --with-doctest
+                   --with-doctest toolz/
     - if [[ $TRAVIS_PYTHON_VERSION != pypy* ]]; then coverage report --show-missing --fail-under=100 ; fi
     - if [[ $TRAVIS_PYTHON_VERSION != pypy* ]]; then pep8 --ignore=$PEP8_IGNORE --exclude=conf.py,tests,examples,bench -r --show-source . ; fi
 
diff --git a/AUTHORS.md b/AUTHORS.md
index 469670d..bd4a563 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -29,3 +29,5 @@ Joe Jevnik                                      [@llllllllll](https://github.com
 Rory Kirchner                                      [@roryk](https://github.com/roryk)
 
 [Steven Cutting](http://steven-cutting.github.io) [@steven_cutting](https://github.com/steven-cutting)
+
+Aric Coady                                      [@coady](https://github.com/coady)
diff --git a/MANIFEST.in b/MANIFEST.in
index 19e672b..1d3c829 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1 +1,3 @@
+include LICENSE.txt
+
 include toolz/tests/*.py
diff --git a/README.rst b/README.rst
index 1f06c04..ee1628c 100644
--- a/README.rst
+++ b/README.rst
@@ -5,7 +5,7 @@ Toolz
 
 A set of utility functions for iterators, functions, and dictionaries.
 
-See the PyToolz documentation at http://toolz.readthedocs.org
+See the PyToolz documentation at https://toolz.readthedocs.io
 
 LICENSE
 -------
@@ -21,12 +21,6 @@ Install
 
     pip install toolz
 
-or
-
-::
-
-    easy_install toolz
-
 Structure and Heritage
 ----------------------
 
@@ -54,7 +48,7 @@ These functions come from the legacy of functional languages for list
 processing. They interoperate well to accomplish common complex tasks.
 
 Read our `API
-Documentation <http://toolz.readthedocs.org/en/latest/api.html>`__ for
+Documentation <https://toolz.readthedocs.io/en/latest/api.html>`__ for
 more details.
 
 Example
diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml
index 51bbf1f..96e4208 100644
--- a/conda.recipe/meta.yaml
+++ b/conda.recipe/meta.yaml
@@ -1,6 +1,6 @@
 package:
     name: toolz
-    version: "0.8.0"
+    version: "0.8.1"
 
 build:
     number: {{environ.get('BINSTAR_BUILD', 1)}}
@@ -22,5 +22,5 @@ test:
       - py.test -x --doctest-modules --pyargs toolz
 
 about:
-    home: http://toolz.readthedocs.org/
+    home: https://toolz.readthedocs.io/
     license: BSD
diff --git a/doc/source/control.rst b/doc/source/control.rst
index 1f5e23e..fe58556 100644
--- a/doc/source/control.rst
+++ b/doc/source/control.rst
@@ -8,9 +8,8 @@ need for simultaneous thought is restricted to only a few elements at a time.
 
 All modern languages provide mechanisms to build data into data structures and
 to build functions out of other functions.  The third element of programming,
-besides data and functions is control flow.  Control flow is the third
-element of programming, after data and functions.  Building complex control
-flow out of simple control flow presents deeper challenges.
+besides data and functions, is control flow.  Building complex control flow
+out of simple control flow presents deeper challenges.
 
 
 What?
diff --git a/doc/source/streaming-analytics.rst b/doc/source/streaming-analytics.rst
index c6c9c43..9a8cdd0 100644
--- a/doc/source/streaming-analytics.rst
+++ b/doc/source/streaming-analytics.rst
@@ -34,7 +34,7 @@ These functions correspond to the SQL commands ``SELECT`` and ``WHERE``.
 .. code::
 
    >>> from toolz.curried import pipe, map, filter, get
-   >>> pipe(accounts, filter(lambda (id, name, balance, gender): balance > 150),
+   >>> pipe(accounts, filter(lambda acc: acc[2] > 150),
    ...                map(get([1, 2])),
    ...                list)
 
@@ -150,9 +150,9 @@ that adds up the balances for each group:
 
 .. code::
 
-   >>> binop = lambda total, (id, name, bal, gend): total + bal
+   >>> binop = lambda total, account: total + account[2]
 
-   >>> reduceby(get(3), binop, accounts)
+   >>> reduceby(get(3), binop, accounts, 0)
    {'F': 400, 'M': 400}
 
 
@@ -307,9 +307,9 @@ composition, ...) users interested in data analytics might be better served by
 using projects specific to data analytics like Pandas_ or SQLAlchemy.
 
 
-.. _groupby: http://toolz.readthedocs.org/en/latest/api.html#toolz.itertoolz.groupby
-.. _join: http://toolz.readthedocs.org/en/latest/api.html#toolz.itertoolz.join
-.. _reduceby: http://toolz.readthedocs.org/en/latest/api.html#toolz.itertoolz.reduceby
-.. _valmap: http://toolz.readthedocs.org/en/latest/api.html#toolz.itertoolz.valmap
+.. _groupby: https://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.groupby
+.. _join: https://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.join
+.. _reduceby: https://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.reduceby
+.. _valmap: https://toolz.readthedocs.io/en/latest/api.html#toolz.dicttoolz.valmap
 .. _Pandas: http://pandas.pydata.org/pandas-docs/stable/groupby.html
-.. _curried: http://toolz.readthedocs.org/en/latest/curry.html
+.. _curried: https://toolz.readthedocs.io/en/latest/curry.html
diff --git a/setup.py b/setup.py
index a45d84c..0560e67 100755
--- a/setup.py
+++ b/setup.py
@@ -15,8 +15,21 @@ setup(name='toolz',
       keywords='functional utility itertools functools',
       packages=['toolz',
                 'toolz.sandbox',
-                'toolz.curried'],
+                'toolz.curried',
+                'tlz'],
       package_data={'toolz': ['tests/*.py']},
       long_description=(open('README.rst').read() if exists('README.rst')
                         else ''),
-      zip_safe=False)
+      zip_safe=False,
+      classifiers=[
+          "Development Status :: 5 - Production/Stable",
+          "License :: OSI Approved :: BSD License",
+          "Programming Language :: Python",
+          "Programming Language :: Python :: 2.6",
+          "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 :: Implementation :: CPython",
+          "Programming Language :: Python :: Implementation :: PyPy"])
diff --git a/tlz/__init__.py b/tlz/__init__.py
new file mode 100644
index 0000000..9c9c84a
--- /dev/null
+++ b/tlz/__init__.py
@@ -0,0 +1,9 @@
+"""``tlz`` mirrors the ``toolz`` API and uses ``cytoolz`` if possible.
+
+The ``tlz`` package is installed when ``toolz`` is installed.  It provides
+a convenient way to use functions from ``cytoolz``--a faster Cython
+implementation of ``toolz``--if it is installed, otherwise it uses
+functions from ``toolz``.
+"""
+
+from . import _build_tlz
diff --git a/tlz/_build_tlz.py b/tlz/_build_tlz.py
new file mode 100644
index 0000000..34eac60
--- /dev/null
+++ b/tlz/_build_tlz.py
@@ -0,0 +1,100 @@
+import sys
+import types
+import toolz
+from toolz.compatibility 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([
+            toolz.pipe,
+        ])
+
+    def _load_toolz(self, fullname):
+        rv = {}
+        package, dot, submodules = fullname.partition('.')
+        try:
+            module_name = ''.join(['cytoolz', dot, submodules])
+            rv['cytoolz'] = import_module(module_name)
+        except ImportError:
+            pass
+        try:
+            module_name = ''.join(['toolz', dot, submodules])
+            rv['toolz'] = import_module(module_name)
+        except ImportError:
+            pass
+        if not rv:
+            raise ImportError(fullname)
+        return rv
+
+    def find_module(self, fullname, path=None):  # pragma: py3 no cover
+        package, dot, submodules = fullname.partition('.')
+        if package == 'tlz':
+            return self
+
+    def load_module(self, fullname):  # pragma: py3 no cover
+        if fullname in sys.modules:  # pragma: no cover
+            return sys.modules[fullname]
+        spec = TlzSpec(fullname, self)
+        module = self.create_module(spec)
+        sys.modules[fullname] = module
+        self.exec_module(module)
+        return module
+
+    def find_spec(self, fullname, path, target=None):  # pragma: no cover
+        package, dot, submodules = fullname.partition('.')
+        if package == 'tlz':
+            return TlzSpec(fullname, self)
+
+    def create_module(self, spec):
+        return types.ModuleType(spec.name)
+
+    def exec_module(self, module):
+        toolz_mods = self._load_toolz(module.__name__)
+        fast_mod = toolz_mods.get('cytoolz') or toolz_mods['toolz']
+        slow_mod = toolz_mods.get('toolz') or toolz_mods['cytoolz']
+        module.__dict__.update(toolz.merge(fast_mod.__dict__, module.__dict__))
+        package = fast_mod.__package__
+        if package is not None:
+            package, dot, submodules = package.partition('.')
+            module.__package__ = ''.join(['tlz', dot, submodules])
+        if not module.__doc__:
+            module.__doc__ = fast_mod.__doc__
+
+        # show file from toolz during introspection
+        module.__file__ = slow_mod.__file__
+
+        for k, v in fast_mod.__dict__.items():
+            tv = slow_mod.__dict__.get(k)
+            try:
+                hash(tv)
+            except TypeError:
+                tv = None
+            if tv in self.always_from_toolz:
+                module.__dict__[k] = tv
+            elif (
+                isinstance(v, types.ModuleType)
+                and v.__package__ == fast_mod.__name__
+            ):
+                package, dot, submodules = v.__name__.partition('.')
+                module_name = ''.join(['tlz', dot, submodules])
+                submodule = import_module(module_name)
+                module.__dict__[k] = submodule
+
+
+class TlzSpec(object):
+    def __init__(self, name, loader):
+        self.name = name
+        self.loader = loader
+        self.origin = None
+        self.submodule_search_locations = []
+        self.loader_state = None
+        self.cached = None
+        self.parent = None
+        self.has_location = False
+
+
+tlz_loader = TlzLoader()
+sys.meta_path.append(tlz_loader)
+tlz_loader.exec_module(sys.modules['tlz'])
diff --git a/toolz/__init__.py b/toolz/__init__.py
index 43226df..1f4342c 100644
--- a/toolz/__init__.py
+++ b/toolz/__init__.py
@@ -19,4 +19,4 @@ comp = compose
 
 functoolz._sigs.create_signature_registry()
 
-__version__ = '0.8.0'
+__version__ = '0.8.1'
diff --git a/toolz/_signatures.py b/toolz/_signatures.py
index b84a2e1..28aa95c 100644
--- a/toolz/_signatures.py
+++ b/toolz/_signatures.py
@@ -17,7 +17,7 @@ import inspect
 import itertools
 import operator
 
-from .compatibility import PY3
+from .compatibility import PY3, import_module
 from .functoolz import (is_partial_args, is_arity, has_varargs,
                         has_keywords, num_required_args)
 
@@ -706,10 +706,7 @@ signatures = {}
 def create_signature_registry(module_info=module_info, signatures=signatures):
     for module, info in module_info.items():
         if isinstance(module, str):
-            modnames = module.split('.')
-            module = __import__(module)
-            for attr in modnames[1:]:
-                module = getattr(module, attr)
+            module = import_module(module)
         for name, sigs in info.items():
             if hasattr(module, name):
                 new_sigs = tuple(expand_sig(sig) for sig in sigs)
diff --git a/toolz/compatibility.py b/toolz/compatibility.py
index 0fe4018..6763502 100644
--- a/toolz/compatibility.py
+++ b/toolz/compatibility.py
@@ -7,7 +7,7 @@ PYPY = hasattr(sys, 'pypy_version_info')
 
 __all__ = ('map', 'filter', 'range', 'zip', 'reduce', 'zip_longest',
            'iteritems', 'iterkeys', 'itervalues', 'filterfalse',
-           'PY3', 'PY34', 'PYPY')
+           'PY3', 'PY34', 'PYPY', 'import_module')
 
 if PY3:
     map = map
@@ -31,3 +31,10 @@ else:
     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]
diff --git a/toolz/curried/__init__.py b/toolz/curried/__init__.py
index 6ab6fef..43aeffd 100644
--- a/toolz/curried/__init__.py
+++ b/toolz/curried/__init__.py
@@ -23,33 +23,78 @@ Example:
 See Also:
     toolz.functoolz.curry
 """
-from . import exceptions
-from . import operator
 import toolz
+from . import operator
+from toolz import (
+    comp,
+    complement,
+    compose,
+    concat,
+    concatv,
+    count,
+    curry,
+    diff,
+    dissoc,
+    first,
+    flip,
+    frequencies,
+    identity,
+    interleave,
+    isdistinct,
+    isiterable,
+    juxt,
+    last,
+    memoize,
+    merge_sorted,
+    peek,
+    pipe,
+    second,
+    thread_first,
+    thread_last,
+)
+from .exceptions import merge, merge_with
 
+accumulate = toolz.curry(toolz.accumulate)
+assoc = toolz.curry(toolz.assoc)
+assoc_in = toolz.curry(toolz.assoc_in)
+cons = toolz.curry(toolz.cons)
+countby = toolz.curry(toolz.countby)
+do = toolz.curry(toolz.do)
+drop = toolz.curry(toolz.drop)
+excepts = toolz.curry(toolz.excepts)
+filter = toolz.curry(toolz.filter)
+get = toolz.curry(toolz.get)
+get_in = toolz.curry(toolz.get_in)
+groupby = toolz.curry(toolz.groupby)
+interpose = toolz.curry(toolz.interpose)
+itemfilter = toolz.curry(toolz.itemfilter)
+itemmap = toolz.curry(toolz.itemmap)
+iterate = toolz.curry(toolz.iterate)
+join = toolz.curry(toolz.join)
+keyfilter = toolz.curry(toolz.keyfilter)
+keymap = toolz.curry(toolz.keymap)
+map = toolz.curry(toolz.map)
+mapcat = toolz.curry(toolz.mapcat)
+nth = toolz.curry(toolz.nth)
+partial = toolz.curry(toolz.partial)
+partition = toolz.curry(toolz.partition)
+partition_all = toolz.curry(toolz.partition_all)
+partitionby = toolz.curry(toolz.partitionby)
+pluck = toolz.curry(toolz.pluck)
+random_sample = toolz.curry(toolz.random_sample)
+reduce = toolz.curry(toolz.reduce)
+reduceby = toolz.curry(toolz.reduceby)
+remove = toolz.curry(toolz.remove)
+sliding_window = toolz.curry(toolz.sliding_window)
+sorted = toolz.curry(toolz.sorted)
+tail = toolz.curry(toolz.tail)
+take = toolz.curry(toolz.take)
+take_nth = toolz.curry(toolz.take_nth)
+topk = toolz.curry(toolz.topk)
+unique = toolz.curry(toolz.unique)
+update_in = toolz.curry(toolz.update_in)
+valfilter = toolz.curry(toolz.valfilter)
+valmap = toolz.curry(toolz.valmap)
 
-def _should_curry(func):
-    if not callable(func) or isinstance(func, toolz.curry):
-        return False
-    nargs = toolz.functoolz.num_required_args(func)
-    if nargs is None or nargs > 1:
-        return True
-    return nargs == 1 and toolz.functoolz.has_keywords(func)
-
-
-def _curry_namespace(ns):
-    return dict(
-        (name, toolz.curry(f) if _should_curry(f) else f)
-        for name, f in ns.items() if '__' not in name
-    )
-
-
-locals().update(toolz.merge(
-    _curry_namespace(vars(toolz)),
-    _curry_namespace(vars(exceptions)),
-))
-
-# Clean up the namespace.
-del _should_curry
 del exceptions
 del toolz
diff --git a/toolz/curried/exceptions.py b/toolz/curried/exceptions.py
index 15c5bc8..75a52bb 100644
--- a/toolz/curried/exceptions.py
+++ b/toolz/curried/exceptions.py
@@ -13,5 +13,6 @@ def merge_with(func, d, *dicts, **kwargs):
 def merge(d, *dicts, **kwargs):
     return toolz.merge(d, *dicts, **kwargs)
 
+
 merge_with.__doc__ = toolz.merge_with.__doc__
 merge.__doc__ = toolz.merge.__doc__
diff --git a/toolz/dicttoolz.py b/toolz/dicttoolz.py
index f6cfc38..f840e7f 100644
--- a/toolz/dicttoolz.py
+++ b/toolz/dicttoolz.py
@@ -226,7 +226,7 @@ def assoc_in(d, keys, value, factory=dict):
     >>> assoc_in(purchase, ['order', 'costs'], [0.25, 1.00]) # doctest: +SKIP
     {'credit card': '5555-1234-1234-1234',
      'name': 'Alice',
-     'purchase': {'costs': [0.25, 1.00], 'items': ['Apple', 'Orange']}}
+     'order': {'costs': [0.25, 1.00], 'items': ['Apple', 'Orange']}}
     """
     return update_in(d, keys, lambda x: value, value, factory)
 
diff --git a/toolz/functoolz.py b/toolz/functoolz.py
index 9081294..ed37c0a 100644
--- a/toolz/functoolz.py
+++ b/toolz/functoolz.py
@@ -4,7 +4,7 @@ import operator
 from operator import attrgetter
 from textwrap import dedent
 
-from .compatibility import PY3, PY33, PY34, PYPY
+from .compatibility import PY3, PY33, PY34, PYPY, import_module
 from .utils import no_default
 
 
@@ -169,7 +169,7 @@ class curry(object):
 
     See Also:
         toolz.curried - namespace of curried functions
-                        http://toolz.readthedocs.org/en/latest/curry.html
+                        https://toolz.readthedocs.io/en/latest/curry.html
     """
     def __init__(self, *args, **kwargs):
         if not args:
@@ -321,17 +321,33 @@ class curry(object):
             return self
         return curry(self, instance)
 
-    # pickle protocol because functools.partial objects can't be pickled
-    def __getstate__(self):
-        # dictoolz.keyfilter, I miss you!
+    def __reduce__(self):
+        func = self.func
+        modname = getattr(func, '__module__', None)
+        funcname = getattr(func, '__name__', None)
+        if modname and funcname:
+            module = import_module(modname)
+            obj = getattr(module, funcname, None)
+            if obj is self:
+                return funcname
+            elif isinstance(obj, curry) and obj.func is func:
+                func = '%s.%s' % (modname, funcname)
+
+        # functools.partial objects can't be pickled
         userdict = tuple((k, v) for k, v in self.__dict__.items()
                          if k != '_partial')
-        return self.func, self.args, self.keywords, userdict
+        state = (type(self), func, self.args, self.keywords, userdict)
+        return (_restore_curry, state)
 
-    def __setstate__(self, state):
-        func, args, kwargs, userdict = state
-        self.__init__(func, *args, **(kwargs or {}))
-        self.__dict__.update(userdict)
+
+def _restore_curry(cls, func, args, kwargs, userdict):
+    if isinstance(func, str):
+        modname, funcname = func.rsplit('.', 1)
+        module = import_module(modname)
+        func = getattr(module, funcname).func
+    obj = cls(func, *args, **(kwargs or {}))
+    obj.__dict__.update(userdict)
+    return obj
 
 
 @curry
@@ -601,13 +617,13 @@ def flip(func, a, b):
     This function is curried.
 
     >>> def div(a, b):
-    ...     return a / b
+    ...     return a // b
     ...
-    >>> flip(div, 2, 1)
-    0.5
+    >>> flip(div, 2, 6)
+    3
     >>> div_by_two = flip(div, 2)
     >>> div_by_two(4)
-    2.0
+    2
 
     This is particularly useful for built in functions and functions defined
     in C extensions that accept positional only arguments. For example:
diff --git a/toolz/itertoolz.py b/toolz/itertoolz.py
index 92f3a3f..2462c5f 100644
--- a/toolz/itertoolz.py
+++ b/toolz/itertoolz.py
@@ -113,59 +113,108 @@ def merge_sorted(*seqs, **kwargs):
     >>> list(merge_sorted([2, 3], [1, 3], key=lambda x: x // 3))
     [2, 1, 3, 3]
     """
+    if len(seqs) == 0:
+        return iter([])
+    elif len(seqs) == 1:
+        return iter(seqs[0])
+
     key = kwargs.get('key', None)
     if key is None:
-        # heapq.merge does what we do below except by val instead of key(val)
-        return heapq.merge(*seqs)
+        return _merge_sorted_binary(seqs)
     else:
-        return _merge_sorted_key(seqs, key)
-
+        return _merge_sorted_binary_key(seqs, key)
 
-def _merge_sorted_key(seqs, key):
-    # The commented code below shows an alternative (slower) implementation
-    # to apply a key function for sorting.
-    #
-    # mapper = lambda i, item: (key(item), i, item)
-    # keyiters = [map(partial(mapper, i), itr) for i, itr in
-    #             enumerate(seqs)]
-    # return (item for (item_key, i, item) in heapq.merge(*keyiters))
 
-    # binary heap as a priority queue
-    pq = []
+def _merge_sorted_binary(seqs):
+    mid = len(seqs) // 2
+    L1 = seqs[:mid]
+    if len(L1) == 1:
+        seq1 = iter(L1[0])
+    else:
+        seq1 = _merge_sorted_binary(L1)
+    L2 = seqs[mid:]
+    if len(L2) == 1:
+        seq2 = iter(L2[0])
+    else:
+        seq2 = _merge_sorted_binary(L2)
 
-    # Initial population
-    for itnum, it in enumerate(map(iter, seqs)):
-        try:
-            item = next(it)
-            pq.append([key(item), itnum, item, it])
-        except StopIteration:
-            pass
-    heapq.heapify(pq)
+    try:
+        val2 = next(seq2)
+    except StopIteration:
+        for val1 in seq1:
+            yield val1
+        return
+
+    for val1 in seq1:
+        if val2 < val1:
+            yield val2
+            for val2 in seq2:
+                if val2 < val1:
+                    yield val2
+                else:
+                    yield val1
+                    break
+            else:
+                break
+        else:
+            yield val1
+    else:
+        yield val2
+        for val2 in seq2:
+            yield val2
+        return
+    yield val1
+    for val1 in seq1:
+        yield val1
+
+
+def _merge_sorted_binary_key(seqs, key):
+    mid = len(seqs) // 2
+    L1 = seqs[:mid]
+    if len(L1) == 1:
+        seq1 = iter(L1[0])
+    else:
+        seq1 = _merge_sorted_binary_key(L1, key)
+    L2 = seqs[mid:]
+    if len(L2) == 1:
+        seq2 = iter(L2[0])
+    else:
+        seq2 = _merge_sorted_binary_key(L2, key)
 
-    # Repeatedly yield and then repopulate from the same iterator
-    heapreplace = heapq.heapreplace
-    heappop = heapq.heappop
-    while len(pq) > 1:
-        try:
-            while True:
-                # raises IndexError when pq is empty
-                _, itnum, item, it = s = pq[0]
-                yield item
-                item = next(it)  # raises StopIteration when exhausted
-                s[0] = key(item)
-                s[2] = item
-                heapreplace(pq, s)  # restore heap condition
-        except StopIteration:
-            heappop(pq)  # remove empty iterator
-    if pq:
-        # Much faster when only a single iterable remains
-        _, itnum, item, it = pq[0]
-        yield item
-        for item in it:
-            yield item
+    try:
+        val2 = next(seq2)
+    except StopIteration:
+        for val1 in seq1:
+            yield val1
+        return
+    key2 = key(val2)
+
+    for val1 in seq1:
+        key1 = key(val1)
+        if key2 < key1:
+            yield val2
+            for val2 in seq2:
+                key2 = key(val2)
+                if key2 < key1:
+                    yield val2
+                else:
+                    yield val1
+                    break
+            else:
+                break
+        else:
+            yield val1
+    else:
+        yield val2
+        for val2 in seq2:
+            yield val2
+        return
+    yield val1
+    for val1 in seq1:
+        yield val1
 
 
-def interleave(seqs, pass_exceptions=()):
+def interleave(seqs):
     """ Interleave a sequence of sequences
 
     >>> list(interleave([[1, 2], [3, 4]]))
@@ -178,16 +227,15 @@ def interleave(seqs, pass_exceptions=()):
 
     Returns a lazy iterator
     """
-    iters = map(iter, seqs)
-    while iters:
-        newiters = []
-        for itr in iters:
-            try:
+    iters = itertools.cycle(map(iter, seqs))
+    while True:
+        try:
+            for itr in iters:
                 yield next(itr)
-                newiters.append(itr)
-            except (StopIteration,) + tuple(pass_exceptions):
-                pass
-        iters = newiters
+            return
+        except StopIteration:
+            predicate = partial(operator.is_not, itr)
+            iters = itertools.cycle(itertools.takewhile(predicate, iters))
 
 
 def unique(seq, key=None):
@@ -423,7 +471,7 @@ def concat(seqs):
     An infinite sequence will prevent the rest of the arguments from
     being included.
 
-    We use chain.from_iterable rather than chain(*seqs) so that seqs
+    We use chain.from_iterable rather than ``chain(*seqs)`` so that seqs
     can be a generator.
 
     >>> list(concat([[], [1], [2, 3]]))
@@ -463,9 +511,7 @@ def cons(el, seq):
     >>> list(cons(1, [2, 3]))
     [1, 2, 3]
     """
-    yield el
-    for s in seq:
-        yield s
+    return itertools.chain([el], seq)
 
 
 def interpose(el, seq):
@@ -617,16 +663,8 @@ def sliding_window(n, seq):
     >>> list(map(mean, sliding_window(2, [1, 2, 3, 4])))
     [1.5, 2.5, 3.5]
     """
-    it = iter(seq)
-    # An efficient FIFO data structure with maximum length
-    d = collections.deque(itertools.islice(it, n), n)
-    if len(d) != n:
-        raise StopIteration()
-    d_append = d.append
-    for item in it:
-        yield tuple(d)
-        d_append(item)
-    yield tuple(d)
+    return zip(*(collections.deque(itertools.islice(it, i), 0) or it
+               for i, it in enumerate(itertools.tee(seq, n))))
 
 
 no_pad = '__no__pad__'
@@ -673,7 +711,10 @@ def partition_all(n, seq):
     """
     args = [iter(seq)] * n
     it = zip_longest(*args, fillvalue=no_pad)
-    prev = next(it)
+    try:
+        prev = next(it)
+    except StopIteration:
+        return
     for item in it:
         yield prev
         prev = item
@@ -706,8 +747,7 @@ def pluck(ind, seqs, default=no_default):
 
     This is equivalent to running `map(curried.get(ind), seqs)`
 
-    ``ind`` can be either a single string/index or a sequence of
-    strings/indices.
+    ``ind`` can be either a single string/index or a list of strings/indices.
     ``seqs`` should be sequence containing sequences or dicts.
 
     e.g.
diff --git a/toolz/tests/test_curried.py b/toolz/tests/test_curried.py
index 5c89149..2efb000 100644
--- a/toolz/tests/test_curried.py
+++ b/toolz/tests/test_curried.py
@@ -2,6 +2,7 @@ import toolz
 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 operator import add
 
@@ -62,3 +63,51 @@ def test_curried_operator():
 
     # Make sure this isn't totally empty.
     assert len(set(vars(cop)) & set(['add', 'sub', 'mul'])) == 3
+
+
+def test_curried_namespace():
+    exceptions = import_module('toolz.curried.exceptions')
+    namespace = {}
+
+    def should_curry(func):
+        if not callable(func) or isinstance(func, toolz.curry):
+            return False
+        nargs = toolz.functoolz.num_required_args(func)
+        if nargs is None or nargs > 1:
+            return True
+        return nargs == 1 and toolz.functoolz.has_keywords(func)
+
+
+    def curry_namespace(ns):
+        return dict(
+            (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))
+    namespace.update(toolz.merge(from_toolz, from_exceptions))
+
+    namespace = toolz.valfilter(callable, namespace)
+    curried_namespace = toolz.valfilter(callable, toolz.curried.__dict__)
+
+    if namespace != curried_namespace:
+        missing = set(namespace) - set(curried_namespace)
+        if missing:
+            raise AssertionError('There are missing functions in toolz.curried:\n    %s'
+                                 % '    \n'.join(sorted(missing)))
+        extra = set(curried_namespace) - set(namespace)
+        if extra:
+            raise AssertionError('There are extra functions in toolz.curried:\n    %s'
+                                 % '    \n'.join(sorted(extra)))
+        unequal = toolz.merge_with(list, namespace, curried_namespace)
+        unequal = toolz.valfilter(lambda x: x[0] != x[1], unequal)
+        messages = []
+        for name, (orig_func, auto_func) in sorted(unequal.items()):
+            if name in from_exceptions:
+                messages.append('%s should come from toolz.curried.exceptions' % name)
+            elif should_curry(getattr(toolz, name)):
+                messages.append('%s should be curried from toolz' % name)
+            else:
+                messages.append('%s should come from toolz and NOT be curried' % name)
+        raise AssertionError('\n'.join(messages))
diff --git a/toolz/tests/test_curried_doctests.py b/toolz/tests/test_curried_doctests.py
new file mode 100644
index 0000000..5fa0935
--- /dev/null
+++ b/toolz/tests/test_curried_doctests.py
@@ -0,0 +1,11 @@
+import doctest
+import toolz
+
+
+def test_doctests():
+    toolz.__test__ = {}
+    for name, func in vars(toolz).items():
+        if isinstance(func, toolz.curry):
+            toolz.__test__[name] = func.func
+    assert doctest.testmod(toolz).failed == 0
+    del toolz.__test__
diff --git a/toolz/tests/test_itertoolz.py b/toolz/tests/test_itertoolz.py
index bb8838d..93aa856 100644
--- a/toolz/tests/test_itertoolz.py
+++ b/toolz/tests/test_itertoolz.py
@@ -86,6 +86,13 @@ def test_merge_sorted():
             [(9, 1), (9, 8), (9, 9)]]
     assert list(merge_sorted(*data, key=lambda x: x[1])) == [
         (9, 1), (1, 2), (5, 3), (0, 4), (6, 5), (3, 6), (8, 8), (9, 8), (9, 9)]
+    assert list(merge_sorted()) == []
+    assert list(merge_sorted([1, 2, 3])) == [1, 2, 3]
+    assert list(merge_sorted([1, 4, 5], [2, 3])) == [1, 2, 3, 4, 5]
+    assert list(merge_sorted([1, 4, 5], [2, 3], key=identity)) == [
+        1, 2, 3, 4, 5]
+    assert list(merge_sorted([1, 5], [2], [4, 7], [3, 6], key=identity)) == [
+        1, 2, 3, 4, 5, 6, 7]
 
 
 def test_interleave():
@@ -328,11 +335,10 @@ def test_pluck():
 
     data = [{'id': 1, 'name': 'cheese'}, {'id': 2, 'name': 'pies', 'price': 1}]
     assert list(pluck('id', data)) == [1, 2]
-    assert list(pluck('price', data, None)) == [None, 1]
+    assert list(pluck('price', data, 0)) == [0, 1]
     assert list(pluck(['id', 'name'], data)) == [(1, 'cheese'), (2, 'pies')]
     assert list(pluck(['name'], data)) == [('cheese',), ('pies',)]
-    assert list(pluck(['price', 'other'], data, None)) == [(None, None),
-                                                           (1, None)]
+    assert list(pluck(['price', 'other'], data, 0)) == [(0, 0), (1, 0)]
 
     assert raises(IndexError, lambda: list(pluck(1, [[0]])))
     assert raises(KeyError, lambda: list(pluck('name', [{'id': 1}])))
diff --git a/toolz/tests/test_serialization.py b/toolz/tests/test_serialization.py
index 17e332b..a49f7a8 100644
--- a/toolz/tests/test_serialization.py
+++ b/toolz/tests/test_serialization.py
@@ -40,3 +40,18 @@ def test_instanceproperty():
     assert p2.__get__(None) is None
     assert p2.__get__(0) is False
     assert p2.__get__(1) is True
+
+
+def f(x, y):
+    return x, y
+
+
+def test_flip():
+    flip = pickle.loads(pickle.dumps(toolz.functoolz.flip))
+    assert flip is toolz.functoolz.flip
+    g1 = flip(f)
+    g2 = pickle.loads(pickle.dumps(g1))
+    assert g1(1, 2) == g2(1, 2) == f(2, 1)
+    g1 = flip(f)(1)
+    g2 = pickle.loads(pickle.dumps(g1))
+    assert g1(2) == g2(2) == f(2, 1)
diff --git a/toolz/tests/test_tlz.py b/toolz/tests/test_tlz.py
new file mode 100644
index 0000000..ed2646a
--- /dev/null
+++ b/toolz/tests/test_tlz.py
@@ -0,0 +1,55 @@
+import toolz
+
+
+def test_tlz():
+    import tlz
+    tlz.curry
+    tlz.functoolz.curry
+    assert tlz.__package__ == 'tlz'
+    assert tlz.__name__ == 'tlz'
+    import tlz.curried
+    assert tlz.curried.__package__ == 'tlz.curried'
+    assert tlz.curried.__name__ == 'tlz.curried'
+    tlz.curried.curry
+    import tlz.curried.operator
+    assert tlz.curried.operator.__package__ in (None, 'tlz.curried')
+    assert tlz.curried.operator.__name__ == 'tlz.curried.operator'
+    assert tlz.functoolz.__name__ == 'tlz.functoolz'
+    m1 = tlz.functoolz
+    import tlz.functoolz as m2
+    assert m1 is m2
+    import tlz.sandbox
... 34 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/toolz.git



More information about the Python-modules-commits mailing list