Commit ccec2957 authored by Andreas Tille's avatar Andreas Tille

New upstream version 0.10.0

parent 4adf9099
Metadata-Version: 1.1
Metadata-Version: 1.2
Name: cytoolz
Version: 0.9.0.1
Version: 0.10.0
Summary: Cython implementation of Toolz: High performance functional utilities
Home-page: https://github.com/pytoolz/cytoolz
Author: Erik Welch
Author: https://raw.github.com/pytoolz/cytoolz/master/AUTHORS.md
Author-email: erik.n.welch@gmail.com
Maintainer: Erik Welch
Maintainer-email: erik.n.welch@gmail.com
License: BSD
Description: CyToolz
=======
......@@ -56,7 +58,7 @@ Description: CyToolz
Dependencies
------------
``cytoolz`` supports Python 2.6+ and Python 3.3+ with a common codebase.
``cytoolz`` supports Python 2.7+ and Python 3.4+ with a common codebase.
It is developed in Cython, but requires no dependecies other than CPython
and a C compiler. Like ``toolz``, it is a light weight dependency.
......@@ -96,13 +98,12 @@ Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Cython
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
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: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Software Development
......
......@@ -48,7 +48,7 @@ Install
Dependencies
------------
``cytoolz`` supports Python 2.6+ and Python 3.3+ with a common codebase.
``cytoolz`` supports Python 2.7+ and Python 3.4+ with a common codebase.
It is developed in Cython, but requires no dependecies other than CPython
and a C compiler. Like ``toolz``, it is a light weight dependency.
......
Metadata-Version: 1.1
Metadata-Version: 1.2
Name: cytoolz
Version: 0.9.0.1
Version: 0.10.0
Summary: Cython implementation of Toolz: High performance functional utilities
Home-page: https://github.com/pytoolz/cytoolz
Author: Erik Welch
Author: https://raw.github.com/pytoolz/cytoolz/master/AUTHORS.md
Author-email: erik.n.welch@gmail.com
Maintainer: Erik Welch
Maintainer-email: erik.n.welch@gmail.com
License: BSD
Description: CyToolz
=======
......@@ -56,7 +58,7 @@ Description: CyToolz
Dependencies
------------
``cytoolz`` supports Python 2.6+ and Python 3.3+ with a common codebase.
``cytoolz`` supports Python 2.7+ and Python 3.4+ with a common codebase.
It is developed in Cython, but requires no dependecies other than CPython
and a C compiler. Like ``toolz``, it is a light weight dependency.
......@@ -96,13 +98,12 @@ Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Cython
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
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: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Software Development
......
......@@ -8,8 +8,6 @@ from .recipes import *
from .compatibility import map, filter
# from . import sandbox
from functools import partial, reduce
sorted = sorted
......@@ -21,6 +19,8 @@ comp = compose
flip = functoolz.flip = curry(functoolz.flip)
memoize = functoolz.memoize = curry(functoolz.memoize)
from . import curried # sandbox
functoolz._sigs.update_signature_registry()
from ._version import __version__, __toolz_version__
......@@ -10,7 +10,7 @@ cytoolz_info['cytoolz.dicttoolz'] = dict(
assoc_in=[
lambda d, keys, value, factory=dict: None],
dissoc=[
lambda d, *keys: None],
lambda d, *keys, **kwargs: None],
get_in=[
lambda keys, coll, default=None, no_default=False: None],
itemfilter=[
......@@ -34,12 +34,16 @@ cytoolz_info['cytoolz.dicttoolz'] = dict(
)
cytoolz_info['cytoolz.functoolz'] = dict(
apply=[
lambda *func_and_args, **kwargs: None],
Compose=[
lambda *funcs: None],
complement=[
lambda func: None],
compose=[
lambda *funcs: None],
compose_left=[
lambda *funcs: None],
curry=[
lambda *args, **kwargs: None],
do=[
......@@ -125,6 +129,8 @@ cytoolz_info['cytoolz.itertoolz'] = dict(
lambda n, seq: None],
peek=[
lambda seq: None],
peekn=[
lambda n, seq: None],
pluck=[
lambda ind, seqs, default=None: None],
random_sample=[
......
__version__ = '0.9.0.1'
__toolz_version__ = '0.9.0'
__version__ = '0.10.0'
__toolz_version__ = '0.10.0'
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
__all__ = ['PY3', 'map', 'filter', 'range', 'zip', 'reduce', 'zip_longest',
......
......@@ -26,15 +26,16 @@ See Also:
import cytoolz
from . import operator
from cytoolz import (
apply,
comp,
complement,
compose,
compose_left,
concat,
concatv,
count,
curry,
diff,
dissoc,
first,
flip,
frequencies,
......@@ -59,6 +60,7 @@ assoc = cytoolz.curry(cytoolz.assoc)
assoc_in = cytoolz.curry(cytoolz.assoc_in)
cons = cytoolz.curry(cytoolz.cons)
countby = cytoolz.curry(cytoolz.countby)
dissoc = cytoolz.curry(cytoolz.dissoc)
do = cytoolz.curry(cytoolz.do)
drop = cytoolz.curry(cytoolz.drop)
excepts = cytoolz.curry(cytoolz.excepts)
......@@ -80,6 +82,7 @@ partial = cytoolz.curry(cytoolz.partial)
partition = cytoolz.curry(cytoolz.partition)
partition_all = cytoolz.curry(cytoolz.partition_all)
partitionby = cytoolz.curry(cytoolz.partitionby)
peekn = cytoolz.curry(cytoolz.peekn)
pluck = cytoolz.curry(cytoolz.pluck)
random_sample = cytoolz.curry(cytoolz.random_sample)
reduce = cytoolz.curry(cytoolz.reduce)
......
This diff is collapsed.
from cpython.ref cimport PyObject
# utility functions to perform iteration over dicts or generic mapping
cdef class _iter_mapping:
cdef object it
cdef object cur
ctypedef int (*f_map_next)(object p, Py_ssize_t *ppos, PyObject* *pkey, PyObject* *pval) except -1
cdef f_map_next get_map_iter(object d, PyObject* *ptr) except NULL
......@@ -38,7 +42,7 @@ cpdef object assoc(object d, object key, object value, object factory=*)
cpdef object assoc_in(object d, object keys, object value, object factory=*)
cdef object c_dissoc(object d, object keys)
cdef object c_dissoc(object d, object keys, object factory=*)
cpdef object update_in(object d, object keys, object func, object default=*, object factory=*)
......
......@@ -16,6 +16,20 @@ __all__ = ['merge', 'merge_with', 'valmap', 'keymap', 'itemmap', 'valfilter',
'update_in']
cdef class _iter_mapping:
""" Keep a handle on the current item to prevent memory clean up too early"""
def __cinit__(self, object it):
self.it = it
self.cur = None
def __iter__(self):
return self
def __next__(self):
self.cur = next(self.it)
return self.cur
cdef int PyMapping_Next(object p, Py_ssize_t *ppos, PyObject* *pkey, PyObject* *pval) except -1:
"""Mimic "PyDict_Next" interface, but for any mapping"""
cdef PyObject *obj
......@@ -24,7 +38,7 @@ cdef int PyMapping_Next(object p, Py_ssize_t *ppos, PyObject* *pkey, PyObject* *
return 0
pkey[0] = <PyObject*>(<object>obj)[0]
pval[0] = <PyObject*>(<object>obj)[1]
Py_XDECREF(obj)
Py_XDECREF(obj) # removing this results in memory leak
return 1
......@@ -53,10 +67,10 @@ cdef f_map_next get_map_iter(object d, PyObject* *ptr) except NULL:
val = d
rv = &PyDict_Next_Compat
elif hasattr(d, 'iteritems'):
val = iter(d.iteritems())
val = _iter_mapping(iter(d.iteritems()))
rv = &PyMapping_Next
else:
val = iter(d.items())
val = _iter_mapping(iter(d.items()))
rv = &PyMapping_Next
Py_INCREF(val)
ptr[0] = <PyObject*>val
......@@ -414,16 +428,24 @@ cpdef object assoc_in(object d, object keys, object value, object factory=dict):
return rv
cdef object c_dissoc(object d, object keys):
cdef object rv, key
rv = copy(d)
for key in keys:
if key in rv:
del rv[key]
cdef object c_dissoc(object d, object keys, object factory=dict):
# implementation copied from toolz. Not benchmarked.
cdef object rv
rv = factory()
if len(keys) < len(d) * 0.6:
rv.update(d)
for key in keys:
if key in rv:
del rv[key]
else:
remaining = set(d)
remaining.difference_update(keys)
for k in remaining:
rv[k] = d[k]
return rv
def dissoc(d, *keys):
def dissoc(d, *keys, **kwargs):
"""
Return a new dict with the given key(s) removed.
......@@ -437,7 +459,7 @@ def dissoc(d, *keys):
>>> dissoc({'x': 1}, 'y') # Ignores missing keys
{'x': 1}
"""
return c_dissoc(d, keys)
return c_dissoc(d, keys, get_factory('dissoc', kwargs))
cpdef object update_in(object d, object keys, object func, object default=None, object factory=dict):
......
This diff is collapsed.
......@@ -38,6 +38,9 @@ cdef class Compose:
cdef object c_compose(object funcs)
cdef object c_compose_left(object funcs)
cdef object c_pipe(object data, object funcs)
......
......@@ -3,8 +3,9 @@ import sys
from functools import partial
from operator import attrgetter
from textwrap import dedent
from types import MethodType
from cytoolz.utils import no_default
from cytoolz.compatibility import PY3, PY33, PY34, filter as ifilter, map as imap, reduce, import_module
from cytoolz.compatibility import PY3, PY34, filter as ifilter, map as imap, reduce, import_module
import cytoolz._signatures as _sigs
from toolz.functoolz import (InstanceProperty, instanceproperty, is_arity,
......@@ -21,15 +22,32 @@ from cpython.set cimport PyFrozenSet_New
from cpython.tuple cimport PyTuple_Check, PyTuple_GET_SIZE
__all__ = ['identity', 'thread_first', 'thread_last', 'memoize', 'compose',
__all__ = ['identity', 'thread_first', 'thread_last', 'memoize', 'compose', 'compose_left',
'pipe', 'complement', 'juxt', 'do', 'curry', 'memoize', 'flip',
'excepts']
'excepts', 'apply']
cpdef object identity(object x):
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')
return func_and_args[0](*func_and_args[1:], **kwargs)
cdef object c_thread_first(object val, object forms):
cdef object form, func
cdef tuple args
......@@ -294,13 +312,7 @@ cdef class curry:
property __signature__:
def __get__(self):
try:
sig = inspect.signature(self.func)
except TypeError:
if PY33 and (getattr(self.func, '__module__') or '').startswith('cytoolz.'):
raise ValueError('callable %r is not supported by signature' % self.func)
raise
sig = inspect.signature(self.func)
args = self.args or ()
keywords = self.keywords or {}
if is_partial_args(self.func, args, keywords, sig) is False:
......@@ -499,6 +511,41 @@ cdef class Compose:
def __setstate__(self, state):
self.funcs = state
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):
if isinstance(other, Compose):
return other.first != self.first or other.funcs != self.funcs
return NotImplemented
def __hash__(self):
return hash(self.first) ^ hash(self.funcs)
def __get__(self, obj, objtype):
if obj is None:
return self
elif PY3:
return MethodType(self, obj)
else:
return MethodType(self, obj, objtype)
property __wrapped__:
def __get__(self):
return self.first
property __signature__:
def __get__(self):
base = inspect.signature(self.first)
last = inspect.signature(self.funcs[-1])
return base.replace(return_annotation=last.return_annotation)
property __name__:
def __get__(self):
try:
......@@ -554,11 +601,43 @@ def compose(*funcs):
'4'
See Also:
compose_left
pipe
"""
return c_compose(funcs)
cdef object c_compose_left(object funcs):
if not funcs:
return identity
elif len(funcs) == 1:
return funcs[0]
else:
return Compose(*reversed(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 c_compose_left(funcs)
cdef object c_pipe(object data, object funcs):
cdef object func
for func in funcs:
......@@ -583,6 +662,7 @@ def pipe(data, *funcs):
See Also:
compose
compose_left
thread_first
thread_last
"""
......
This diff is collapsed.
......@@ -272,6 +272,9 @@ cpdef object topk(Py_ssize_t k, object seq, object key=*)
cpdef object peek(object seq)
cpdef object peekn(Py_ssize_t n, object seq)
cdef class random_sample:
cdef object iter_seq
cdef object prob
......
......@@ -24,7 +24,7 @@ __all__ = ['remove', 'accumulate', 'groupby', 'merge_sorted', 'interleave',
'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']
cpdef object identity(object x):
......@@ -136,6 +136,8 @@ cpdef dict groupby(object key, object seq):
'M': [{'gender': 'M', 'name': 'Bob'},
{'gender': 'M', 'name': 'Charlie'}]}
Not to be confused with ``itertools.groupby``
See Also:
countby
"""
......@@ -962,8 +964,7 @@ cdef class sliding_window:
cdef Py_ssize_t i
self.iterseq = iter(seq)
self.prev = PyTuple_New(n)
for i in range(1, n):
seq = next(self.iterseq)
for i, seq in enumerate(islice(self.iterseq, n-1), 1):
Py_INCREF(seq)
PyTuple_SET_ITEM(self.prev, i, seq)
self.n = n
......@@ -975,14 +976,15 @@ cdef class sliding_window:
cdef tuple current
cdef object item
cdef Py_ssize_t i
item = next(self.iterseq)
current = PyTuple_New(self.n)
Py_INCREF(item)
PyTuple_SET_ITEM(current, self.n-1, item)
for i in range(1, self.n):
item = self.prev[i]
Py_INCREF(item)
PyTuple_SET_ITEM(current, i-1, item)
item = next(self.iterseq)
Py_INCREF(item)
PyTuple_SET_ITEM(current, self.n-1, item)
self.prev = current
return current
......@@ -1252,6 +1254,8 @@ cpdef object join(object leftkey, object leftseq,
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'),
......@@ -1294,7 +1298,10 @@ cpdef object join(object leftkey, object leftseq,
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
......@@ -1727,6 +1734,25 @@ cpdef object peek(object seq):
return item, chain((item,), iterator)
cpdef object peekn(Py_ssize_t n, object 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, chain(iter(peeked), iterator)
cdef class random_sample:
""" random_sample(prob, seq, random_state=None)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2,8 +2,8 @@ import cytoolz
import cytoolz.curried
from cytoolz.curried import (take, first, second, sorted, merge_with, reduce,
merge, operator as cop)
from cytoolz.compatibility import import_module
from collections import defaultdict
from importlib import import_module
from operator import add
......@@ -62,7 +62,7 @@ def test_curried_operator():
)
# 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 test_curried_namespace():
def curry_namespace(ns):
return dict(
(name, cytoolz.curry(f) if should_curry(f) else f)
return {
name: cytoolz.curry(f) if should_curry(f) else f
for name, f in ns.items() if '__' not in name
)
}
from_cytoolz = curry_namespace(vars(cytoolz))
from_exceptions = curry_namespace(vars(exceptions))
......
from collections import defaultdict as _defaultdict
import os
from cytoolz.dicttoolz import (merge, merge_with, valmap, keymap, update_in,
assoc, dissoc, keyfilter, valfilter, itemmap,
itemfilter, assoc_in)
from cytoolz.functoolz import identity
from cytoolz.utils import raises
from cytoolz.compatibility import PY3
......@@ -90,16 +92,16 @@ class TestDict(object):
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 @@ class TestCustomMapping(TestDict):
"""
D = CustomMapping
kw = {'factory': lambda: CustomMapping()}
def test_environ():
# See: https://github.com/pycytoolz/cycytoolz/issues/127
assert keymap(identity, os.environ) == os.environ
assert valmap(identity, os.environ) == os.environ
assert itemmap(identity, os.environ) == os.environ
import platform
import inspect
import cytoolz
from cytoolz.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 cytoolz.compatibility import PY3
from operator import add, mul, itemgetter
from cytoolz.utils import raises
from functools import partial
......@@ -23,6 +25,32 @@ def double(x):
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 test_curry_kwargs():
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 @@ def test_curry_comparable():
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_curry_subclassable():
"""
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),