From 0684a4d91ba140a22a64aef85d4a7148b7f881a7 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:04:53 -0500 Subject: [PATCH 01/47] Remove Python 2/3 specific code. --- toolz/compatibility.py | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/toolz/compatibility.py b/toolz/compatibility.py index 51e3673f..5e8756fd 100644 --- a/toolz/compatibility.py +++ b/toolz/compatibility.py @@ -1,34 +1,5 @@ -import operator import sys -PY3 = sys.version_info[0] > 2 -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') +PYPY = hasattr(sys, 'pypy_version_info') -if PY3: - map = map - filter = filter - range = range - zip = zip - from functools import reduce - from itertools import zip_longest - from itertools import filterfalse - iteritems = operator.methodcaller('items') - iterkeys = operator.methodcaller('keys') - itervalues = operator.methodcaller('values') - from collections.abc import Sequence -else: - range = xrange - reduce = reduce - from itertools import imap as map - from itertools import ifilter as filter - from itertools import ifilterfalse as filterfalse - from itertools import izip as zip - from itertools import izip_longest as zip_longest - iteritems = operator.methodcaller('iteritems') - iterkeys = operator.methodcaller('iterkeys') - itervalues = operator.methodcaller('itervalues') - from collections import Sequence +__all__ = ('PYPY',) From b052943bc57d4193cf96fcf9bd828ce438f7b950 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:05:16 -0500 Subject: [PATCH 02/47] Remove compatibility import. --- toolz/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/toolz/__init__.py b/toolz/__init__.py index 7fa86ab4..43594067 100644 --- a/toolz/__init__.py +++ b/toolz/__init__.py @@ -6,8 +6,6 @@ from .recipes import * -from .compatibility import map, filter - from functools import partial, reduce sorted = sorted From cf9979a438372d4ab87c9b7426c168c1a32ce60c Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:05:57 -0500 Subject: [PATCH 03/47] Drop python 2 support. --- toolz/_signatures.py | 164 +++++++++++++++---------------------------- 1 file changed, 56 insertions(+), 108 deletions(-) diff --git a/toolz/_signatures.py b/toolz/_signatures.py index c55a778b..f32b4c85 100644 --- a/toolz/_signatures.py +++ b/toolz/_signatures.py @@ -18,14 +18,10 @@ import operator from importlib import import_module -from .compatibility import PY3 from .functoolz import (is_partial_args, is_arity, has_varargs, has_keywords, num_required_args) -if PY3: # pragma: py2 no cover - import builtins -else: # pragma: py3 no cover - import __builtin__ as builtins +import builtins # We mock builtin callables using lists of tuples with lambda functions. # @@ -235,54 +231,33 @@ lambda source, globals: None, lambda source, globals, locals: None] -if PY3: # pragma: py2 no cover - module_info[builtins].update( - breakpoint=[ - lambda *args, **kws: None], - bytes=[ - lambda: None, - lambda int: None, - lambda string, encoding='utf8', errors='strict': None], - compile=[ - (0, lambda source, filename, mode, flags=0, - dont_inherit=False, optimize=-1: None)], - max=[ - (1, lambda iterable: None, ('default', 'key',)), - (1, lambda arg1, arg2, *args: None, ('key',))], - min=[ - (1, lambda iterable: None, ('default', 'key',)), - (1, lambda arg1, arg2, *args: None, ('key',))], - open=[ - (0, lambda file, mode='r', buffering=-1, encoding=None, - errors=None, newline=None, closefd=True, opener=None: None)], - sorted=[ - (1, lambda iterable: None, ('key', 'reverse'))], - str=[ - lambda object='', encoding='utf', errors='strict': None], - ) - module_info[builtins]['print'] = [ - (0, lambda *args: None, ('sep', 'end', 'file', 'flush',))] - -else: # pragma: py3 no cover - module_info[builtins].update( - bytes=[ - lambda object='': None], - compile=[ - (0, lambda source, filename, mode, flags=0, - dont_inherit=False: None)], - max=[ - (1, lambda iterable, *args: None, ('key',))], - min=[ - (1, lambda iterable, *args: None, ('key',))], - open=[ - (0, lambda file, mode='r', buffering=-1: None)], - sorted=[ - lambda iterable, cmp=None, key=None, reverse=False: None], - str=[ - lambda object='': None], - ) - module_info[builtins]['print'] = [ - (0, lambda *args: None, ('sep', 'end', 'file',))] +module_info[builtins].update( + breakpoint=[ + lambda *args, **kws: None], + bytes=[ + lambda: None, + lambda int: None, + lambda string, encoding='utf8', errors='strict': None], + compile=[ + (0, lambda source, filename, mode, flags=0, + dont_inherit=False, optimize=-1: None)], + max=[ + (1, lambda iterable: None, ('default', 'key',)), + (1, lambda arg1, arg2, *args: None, ('key',))], + min=[ + (1, lambda iterable: None, ('default', 'key',)), + (1, lambda arg1, arg2, *args: None, ('key',))], + open=[ + (0, lambda file, mode='r', buffering=-1, encoding=None, + errors=None, newline=None, closefd=True, opener=None: None)], + sorted=[ + (1, lambda iterable: None, ('key', 'reverse'))], + str=[ + lambda object='', encoding='utf', errors='strict': None], +) +module_info[builtins]['print'] = [ + (0, lambda *args: None, ('sep', 'end', 'file', 'flush',))] + module_info[functools] = dict( cmp_to_key=[ @@ -346,16 +321,11 @@ (0, lambda *iterables: None, ('fillvalue',))], ) -if PY3: # pragma: py2 no cover - module_info[itertools].update( - product=[ - (0, lambda *iterables: None, ('repeat',))], - ) -else: # pragma: py3 no cover - module_info[itertools].update( - product=[ - lambda *iterables: None], - ) +module_info[itertools].update( + product=[ + (0, lambda *iterables: None, ('repeat',))], +) + module_info[operator] = dict( __abs__=[ @@ -622,51 +592,29 @@ classval=None: None)], ) -if PY3: # pragma: py2 no cover - def num_pos_args(sigspec): - """ Return the number of positional arguments. ``f(x, y=1)`` has 1""" - return sum(1 for x in sigspec.parameters.values() - if x.kind == x.POSITIONAL_OR_KEYWORD - and x.default is x.empty) - - def get_exclude_keywords(num_pos_only, sigspec): - """ Return the names of position-only arguments if func has **kwargs""" - if num_pos_only == 0: - return () - has_kwargs = any(x.kind == x.VAR_KEYWORD - for x in sigspec.parameters.values()) - if not has_kwargs: - return () - pos_args = list(sigspec.parameters.values())[:num_pos_only] - return tuple(x.name for x in pos_args) - - def signature_or_spec(func): - try: - return inspect.signature(func) - except (ValueError, TypeError): - return None - -else: # pragma: py3 no cover - def num_pos_args(sigspec): - """ Return the number of positional arguments. ``f(x, y=1)`` has 1""" - if sigspec.defaults: - return len(sigspec.args) - len(sigspec.defaults) - return len(sigspec.args) - - def get_exclude_keywords(num_pos_only, sigspec): - """ Return the names of position-only arguments if func has **kwargs""" - if num_pos_only == 0: - return () - has_kwargs = sigspec.keywords is not None - if not has_kwargs: - return () - return tuple(sigspec.args[:num_pos_only]) - - def signature_or_spec(func): - try: - return inspect.getargspec(func) - except TypeError: - return None + +def num_pos_args(sigspec): + """ Return the number of positional arguments. ``f(x, y=1)`` has 1""" + return sum(1 for x in sigspec.parameters.values() + if x.kind == x.POSITIONAL_OR_KEYWORD + and x.default is x.empty) + +def get_exclude_keywords(num_pos_only, sigspec): + """ Return the names of position-only arguments if func has **kwargs""" + if num_pos_only == 0: + return () + has_kwargs = any(x.kind == x.VAR_KEYWORD + for x in sigspec.parameters.values()) + if not has_kwargs: + return () + pos_args = list(sigspec.parameters.values())[:num_pos_only] + return tuple(x.name for x in pos_args) + +def signature_or_spec(func): + try: + return inspect.signature(func) + except (ValueError, TypeError): + return None def expand_sig(sig): From c12d2d71686e705e94f3a3c5dddfc5ef8d0e0ea9 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:09:56 -0500 Subject: [PATCH 04/47] Drop Python 2 compatibility. --- toolz/dicttoolz.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/toolz/dicttoolz.py b/toolz/dicttoolz.py index 91bff23c..55281e3a 100644 --- a/toolz/dicttoolz.py +++ b/toolz/dicttoolz.py @@ -1,6 +1,4 @@ import operator -from toolz.compatibility import (map, zip, iteritems, iterkeys, itervalues, - reduce) __all__ = ('merge', 'merge_with', 'valmap', 'keymap', 'itemmap', 'valfilter', 'keyfilter', 'itemfilter', @@ -60,7 +58,7 @@ def merge_with(func, *dicts, **kwargs): result = factory() for d in dicts: - for k, v in iteritems(d): + for k, v in d.items(): if k not in result: result[k] = [v] else: @@ -80,7 +78,7 @@ def valmap(func, d, factory=dict): itemmap """ rv = factory() - rv.update(zip(iterkeys(d), map(func, itervalues(d)))) + rv.update(zip(d.keys(), map(func, d.values()))) return rv @@ -96,7 +94,7 @@ def keymap(func, d, factory=dict): itemmap """ rv = factory() - rv.update(zip(map(func, iterkeys(d)), itervalues(d))) + rv.update(zip(map(func, d.keys()), d.values())) return rv @@ -112,7 +110,7 @@ def itemmap(func, d, factory=dict): valmap """ rv = factory() - rv.update(map(func, iteritems(d))) + rv.update(map(func, d.items())) return rv @@ -130,7 +128,7 @@ def valfilter(predicate, d, factory=dict): valmap """ rv = factory() - for k, v in iteritems(d): + for k, v in d.items(): if predicate(v): rv[k] = v return rv @@ -150,7 +148,7 @@ def keyfilter(predicate, d, factory=dict): keymap """ rv = factory() - for k, v in iteritems(d): + for k, v in d.items(): if predicate(k): rv[k] = v return rv @@ -173,7 +171,7 @@ def itemfilter(predicate, d, factory=dict): itemmap """ rv = factory() - for item in iteritems(d): + for item in d.items(): if predicate(item): k, v = item rv[k] = v From 5f5ca40faa79ba38cbb75b2bb60a863ea7e124aa Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:28:13 -0500 Subject: [PATCH 05/47] Remove Python 2 compatibility. --- toolz/functoolz.py | 329 +++++++++++++++------------------------------ 1 file changed, 108 insertions(+), 221 deletions(-) diff --git a/toolz/functoolz.py b/toolz/functoolz.py index 01d3857a..7db8bda3 100644 --- a/toolz/functoolz.py +++ b/toolz/functoolz.py @@ -6,7 +6,7 @@ from textwrap import dedent from types import MethodType -from .compatibility import PY3, PY34, PYPY +from .compatibility import PYPY from .utils import no_default @@ -229,44 +229,43 @@ def __init__(self, *args, **kwargs): def func(self): return self._partial.func - if PY3: # pragma: py2 no cover - @instanceproperty - def __signature__(self): - sig = inspect.signature(self.func) - args = self.args or () - keywords = self.keywords or {} - if is_partial_args(self.func, args, keywords, sig) is False: - raise TypeError('curry object has incorrect arguments') - - params = list(sig.parameters.values()) - skip = 0 - for param in params[:len(args)]: - if param.kind == param.VAR_POSITIONAL: - break - skip += 1 - - kwonly = False - newparams = [] - for param in params[skip:]: - kind = param.kind - default = param.default - if kind == param.VAR_KEYWORD: - pass - elif kind == param.VAR_POSITIONAL: - if kwonly: - continue - elif param.name in keywords: - default = keywords[param.name] + @instanceproperty + def __signature__(self): + sig = inspect.signature(self.func) + args = self.args or () + keywords = self.keywords or {} + if is_partial_args(self.func, args, keywords, sig) is False: + raise TypeError('curry object has incorrect arguments') + + params = list(sig.parameters.values()) + skip = 0 + for param in params[:len(args)]: + if param.kind == param.VAR_POSITIONAL: + break + skip += 1 + + kwonly = False + newparams = [] + for param in params[skip:]: + kind = param.kind + default = param.default + if kind == param.VAR_KEYWORD: + pass + elif kind == param.VAR_POSITIONAL: + if kwonly: + continue + elif param.name in keywords: + default = keywords[param.name] + kind = param.KEYWORD_ONLY + kwonly = True + else: + if kwonly: kind = param.KEYWORD_ONLY - kwonly = True - else: - if kwonly: - kind = param.KEYWORD_ONLY - if default is param.empty: - default = no_default - newparams.append(param.replace(default=default, kind=kind)) + if default is param.empty: + default = no_default + newparams.append(param.replace(default=default, kind=kind)) - return sig.replace(parameters=newparams) + return sig.replace(parameters=newparams) @instanceproperty def args(self): @@ -541,24 +540,17 @@ def __hash__(self): # 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) + # 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) # 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) + @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')) @@ -825,43 +817,28 @@ def __name__(self): return 'excepting' -if PY3: # pragma: py2 no cover - def _check_sigspec(sigspec, func, builtin_func, *builtin_args): - if sigspec is None: - try: - sigspec = inspect.signature(func) - except (ValueError, TypeError) as e: - sigspec = e - if isinstance(sigspec, ValueError): - return None, builtin_func(*builtin_args) - elif not isinstance(sigspec, inspect.Signature): - if ( - func in _sigs.signatures - and (( - hasattr(func, '__signature__') - and hasattr(func.__signature__, '__get__') - )) - ): # pragma: no cover (not covered in Python 3.4) - val = builtin_func(*builtin_args) - return None, val - return None, False - return sigspec, None - -else: # pragma: py3 no cover - def _check_sigspec(sigspec, func, builtin_func, *builtin_args): - if sigspec is None: - try: - sigspec = inspect.getargspec(func) - except TypeError as e: - sigspec = e - if isinstance(sigspec, TypeError): - if not callable(func): - return None, False - return None, builtin_func(*builtin_args) - return sigspec, None - - -if PY34 or PYPY: # pragma: no cover +def _check_sigspec(sigspec, func, builtin_func, *builtin_args): + if sigspec is None: + try: + sigspec = inspect.signature(func) + except (ValueError, TypeError) as e: + sigspec = e + if isinstance(sigspec, ValueError): + return None, builtin_func(*builtin_args) + elif not isinstance(sigspec, inspect.Signature): + if ( + func in _sigs.signatures + and (( + hasattr(func, '__signature__') + and hasattr(func.__signature__, '__get__') + )) + ): # pragma: no cover (not covered in Python 3.4) + val = builtin_func(*builtin_args) + return None, val + return None, False + return sigspec, None + +if PYPY: # pragma: no cover _check_sigspec_orig = _check_sigspec def _check_sigspec(sigspec, func, builtin_func, *builtin_args): @@ -879,142 +856,52 @@ def _check_sigspec(sigspec, func, builtin_func, *builtin_args): """ if PY3: # pragma: py2 no cover - def num_required_args(func, sigspec=None): - sigspec, rv = _check_sigspec(sigspec, func, _sigs._num_required_args, - func) - if sigspec is None: - return rv - return sum(1 for p in sigspec.parameters.values() - if p.default is p.empty - and p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY)) - - def has_varargs(func, sigspec=None): - sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_varargs, func) - if sigspec is None: - return rv - return any(p.kind == p.VAR_POSITIONAL - for p in sigspec.parameters.values()) - - def has_keywords(func, sigspec=None): - sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_keywords, func) - if sigspec is None: - return rv - return any(p.default is not p.empty - or p.kind in (p.KEYWORD_ONLY, p.VAR_KEYWORD) - for p in sigspec.parameters.values()) - - def is_valid_args(func, args, kwargs, sigspec=None): - sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_valid_args, - func, args, kwargs) - if sigspec is None: - return rv - try: - sigspec.bind(*args, **kwargs) - except TypeError: - return False - return True - - def is_partial_args(func, args, kwargs, sigspec=None): - sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_partial_args, - func, args, kwargs) - if sigspec is None: - return rv - try: - sigspec.bind_partial(*args, **kwargs) - except TypeError: - return False - return True - -else: # pragma: py3 no cover - def num_required_args(func, sigspec=None): - sigspec, rv = _check_sigspec(sigspec, func, _sigs._num_required_args, - func) - if sigspec is None: - return rv - num_defaults = len(sigspec.defaults) if sigspec.defaults else 0 - return len(sigspec.args) - num_defaults - - def has_varargs(func, sigspec=None): - sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_varargs, func) - if sigspec is None: - return rv - return sigspec.varargs is not None - - def has_keywords(func, sigspec=None): - sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_keywords, func) - if sigspec is None: - return rv - return sigspec.defaults is not None or sigspec.keywords is not None - - def is_valid_args(func, args, kwargs, sigspec=None): - sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_valid_args, - func, args, kwargs) - if sigspec is None: - return rv - spec = sigspec - defaults = spec.defaults or () - num_pos = len(spec.args) - len(defaults) - missing_pos = spec.args[len(args):num_pos] - if any(arg not in kwargs for arg in missing_pos): - return False - - if spec.varargs is None: - num_extra_pos = max(0, len(args) - num_pos) - else: - num_extra_pos = 0 - - kwargs = dict(kwargs) - - # Add missing keyword arguments (unless already included in `args`) - missing_kwargs = spec.args[num_pos + num_extra_pos:] - kwargs.update(zip(missing_kwargs, defaults[num_extra_pos:])) - - # Convert call to use positional arguments - args = args + tuple(kwargs.pop(key) for key in spec.args[len(args):]) - - if ( - not spec.keywords and kwargs - or not spec.varargs and len(args) > len(spec.args) - or set(spec.args[:len(args)]) & set(kwargs) - ): - return False - else: - return True - - def is_partial_args(func, args, kwargs, sigspec=None): - sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_partial_args, - func, args, kwargs) - if sigspec is None: - return rv - spec = sigspec - defaults = spec.defaults or () - num_pos = len(spec.args) - len(defaults) - if spec.varargs is None: - num_extra_pos = max(0, len(args) - num_pos) - else: - num_extra_pos = 0 +def num_required_args(func, sigspec=None): + sigspec, rv = _check_sigspec(sigspec, func, _sigs._num_required_args, + func) + if sigspec is None: + return rv + return sum(1 for p in sigspec.parameters.values() + if p.default is p.empty + and p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY)) - kwargs = dict(kwargs) +def has_varargs(func, sigspec=None): + sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_varargs, func) + if sigspec is None: + return rv + return any(p.kind == p.VAR_POSITIONAL + for p in sigspec.parameters.values()) - # Add missing keyword arguments (unless already included in `args`) - missing_kwargs = spec.args[num_pos + num_extra_pos:] - kwargs.update(zip(missing_kwargs, defaults[num_extra_pos:])) +def has_keywords(func, sigspec=None): + sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_keywords, func) + if sigspec is None: + return rv + return any(p.default is not p.empty + or p.kind in (p.KEYWORD_ONLY, p.VAR_KEYWORD) + for p in sigspec.parameters.values()) - # Add missing position arguments as keywords (may already be in kwargs) - missing_args = spec.args[len(args):num_pos + num_extra_pos] - kwargs.update((x, None) for x in missing_args) +def is_valid_args(func, args, kwargs, sigspec=None): + sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_valid_args, + func, args, kwargs) + if sigspec is None: + return rv + try: + sigspec.bind(*args, **kwargs) + except TypeError: + return False + return True - # Convert call to use positional arguments - args = args + tuple(kwargs.pop(key) for key in spec.args[len(args):]) +def is_partial_args(func, args, kwargs, sigspec=None): + sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_partial_args, + func, args, kwargs) + if sigspec is None: + return rv + try: + sigspec.bind_partial(*args, **kwargs) + except TypeError: + return False + return True - if ( - not spec.keywords and kwargs - or not spec.varargs and len(args) > len(spec.args) - or set(spec.args[:len(args)]) & set(kwargs) - ): - return False - else: - return True def is_arity(n, func, sigspec=None): From bd3f3feec3ea0d068e6f1e6e7ba49591977de47d Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:31:40 -0500 Subject: [PATCH 06/47] Remove Python 2 compatibility. --- toolz/itertoolz.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/toolz/itertoolz.py b/toolz/itertoolz.py index e71f1eee..71c9b158 100644 --- a/toolz/itertoolz.py +++ b/toolz/itertoolz.py @@ -4,8 +4,7 @@ import operator from functools import partial from random import Random -from toolz.compatibility import (map, filterfalse, zip, zip_longest, iteritems, - filter, Sequence) +from collections.abc import Sequence from toolz.utils import no_default @@ -25,7 +24,7 @@ def remove(predicate, seq): >>> list(remove(iseven, [1, 2, 3, 4])) [1, 3] """ - return filterfalse(predicate, seq) + return itertools.filterfalse(predicate, seq) def accumulate(binop, seq, initial=no_default): @@ -100,7 +99,7 @@ def groupby(key, seq): for item in seq: d[key(item)](item) rv = {} - for k, v in iteritems(d): + for k, v in d.items(): rv[k] = v.__self__ return rv @@ -703,7 +702,7 @@ def partition(n, seq, pad=no_pad): if pad is no_pad: return zip(*args) else: - return zip_longest(*args, fillvalue=pad) + return itertools.zip_longest(*args, fillvalue=pad) def partition_all(n, seq): @@ -721,7 +720,7 @@ def partition_all(n, seq): partition """ args = [iter(seq)] * n - it = zip_longest(*args, fillvalue=no_pad) + it = itertools.zip_longest(*args, fillvalue=no_pad) try: prev = next(it) except StopIteration: @@ -949,7 +948,7 @@ def diff(*seqs, **kwargs): if default == no_default: iters = zip(*seqs) else: - iters = zip_longest(*seqs, fillvalue=default) + iters = itertools.zip_longest(*seqs, fillvalue=default) key = kwargs.get('key', None) if key is None: for items in iters: From 3560b2b18affd616a26123c100636307de664946 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:32:09 -0500 Subject: [PATCH 07/47] Remove Python 2 compatibility. --- toolz/recipes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/toolz/recipes.py b/toolz/recipes.py index 08c6c8c1..89de88db 100644 --- a/toolz/recipes.py +++ b/toolz/recipes.py @@ -1,6 +1,5 @@ import itertools from .itertoolz import frequencies, pluck, getter -from .compatibility import map __all__ = ('countby', 'partitionby') From d56766514ef81ffc7387a85a6c94e93ff35fa87d Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:40:07 -0500 Subject: [PATCH 08/47] Remove errant if statement. --- toolz/functoolz.py | 1 - 1 file changed, 1 deletion(-) diff --git a/toolz/functoolz.py b/toolz/functoolz.py index 7db8bda3..7f9e7d21 100644 --- a/toolz/functoolz.py +++ b/toolz/functoolz.py @@ -855,7 +855,6 @@ def _check_sigspec(sigspec, func, builtin_func, *builtin_args): the signature registry in toolz._signatures is used. """ -if PY3: # pragma: py2 no cover def num_required_args(func, sigspec=None): sigspec, rv = _check_sigspec(sigspec, func, _sigs._num_required_args, func) From 85326bd35bd9bdda25287fffc3f0dff3fea30ab4 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:40:28 -0500 Subject: [PATCH 09/47] Remove Python 2 compatibility. --- toolz/curried/__init__.py | 4 ++-- toolz/sandbox/parallel.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/toolz/curried/__init__.py b/toolz/curried/__init__.py index 356eddbd..1f4ed7e1 100644 --- a/toolz/curried/__init__.py +++ b/toolz/curried/__init__.py @@ -64,7 +64,7 @@ do = toolz.curry(toolz.do) drop = toolz.curry(toolz.drop) excepts = toolz.curry(toolz.excepts) -filter = toolz.curry(toolz.filter) +filter = toolz.curry(filter) get = toolz.curry(toolz.get) get_in = toolz.curry(toolz.get_in) groupby = toolz.curry(toolz.groupby) @@ -75,7 +75,7 @@ join = toolz.curry(toolz.join) keyfilter = toolz.curry(toolz.keyfilter) keymap = toolz.curry(toolz.keymap) -map = toolz.curry(toolz.map) +map = toolz.curry(map) mapcat = toolz.curry(toolz.mapcat) nth = toolz.curry(toolz.nth) partial = toolz.curry(toolz.partial) diff --git a/toolz/sandbox/parallel.py b/toolz/sandbox/parallel.py index ef8ed39d..114077d2 100644 --- a/toolz/sandbox/parallel.py +++ b/toolz/sandbox/parallel.py @@ -1,6 +1,5 @@ import functools from toolz.itertoolz import partition_all -from toolz.compatibility import reduce, map from toolz.utils import no_default From 80ae19783d10852619d435253cfc2e052be7a73c Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:55:48 -0500 Subject: [PATCH 10/47] Fix missed items call. --- toolz/itertoolz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolz/itertoolz.py b/toolz/itertoolz.py index 71c9b158..05679eed 100644 --- a/toolz/itertoolz.py +++ b/toolz/itertoolz.py @@ -915,7 +915,7 @@ def join(leftkey, leftseq, rightkey, rightseq, else: yield (left_default, item) - for key, matches in iteritems(d): + for key, matches in d.items(): if key not in seen_keys: for match in matches: yield (match, right_default) From 50287b57df2bd9b9d62cbafef6158993d6266deb Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:56:27 -0500 Subject: [PATCH 11/47] Update tests. --- toolz/tests/test_compatibility.py | 14 ++++---- toolz/tests/test_dicttoolz.py | 12 ------- toolz/tests/test_functoolz.py | 56 +++++++++++++++---------------- toolz/tests/test_inspect_args.py | 38 +++++++-------------- toolz/tests/test_itertoolz.py | 1 - toolz/tests/test_serialization.py | 3 -- toolz/tests/test_signatures.py | 6 ++-- 7 files changed, 48 insertions(+), 82 deletions(-) diff --git a/toolz/tests/test_compatibility.py b/toolz/tests/test_compatibility.py index 6071606d..b0072970 100644 --- a/toolz/tests/test_compatibility.py +++ b/toolz/tests/test_compatibility.py @@ -1,5 +1,3 @@ -from toolz.compatibility import map, filter, iteritems, iterkeys, itervalues - def test_map_filter_are_lazy(): def bad(x): @@ -10,9 +8,9 @@ def bad(x): def test_dict_iteration(): d = {'a': 1, 'b': 2, 'c': 3} - assert not isinstance(iteritems(d), list) - assert not isinstance(iterkeys(d), list) - assert not isinstance(itervalues(d), list) - assert set(iteritems(d)) == set(d.items()) - assert set(iterkeys(d)) == set(d.keys()) - assert set(itervalues(d)) == set(d.values()) + assert not isinstance(d.items(), list) + assert not isinstance(d.keys(), list) + assert not isinstance(d.values(), list) + assert set(d.items()) == set(d.items()) + assert set(d.keys()) == set(d.keys()) + assert set(d.values()) == set(d.values()) diff --git a/toolz/tests/test_dicttoolz.py b/toolz/tests/test_dicttoolz.py index d092ab2c..050aaccf 100644 --- a/toolz/tests/test_dicttoolz.py +++ b/toolz/tests/test_dicttoolz.py @@ -5,7 +5,6 @@ itemfilter, assoc_in) from toolz.functoolz import identity from toolz.utils import raises -from toolz.compatibility import PY3 def inc(x): @@ -215,17 +214,6 @@ def items(self): def update(self, *args, **kwargs): self._d.update(*args, **kwargs) - # Should we require these to be defined for Python 2? - if not PY3: - def iterkeys(self): - return self._d.iterkeys() - - def itervalues(self): - return self._d.itervalues() - - def iteritems(self): - return self._d.iteritems() - # Unused methods that are part of the MutableMapping protocol #def get(self, key, *args): # return self._d.get(key, *args) diff --git a/toolz/tests/test_functoolz.py b/toolz/tests/test_functoolz.py index 4938df8a..555cf48d 100644 --- a/toolz/tests/test_functoolz.py +++ b/toolz/tests/test_functoolz.py @@ -3,7 +3,6 @@ from toolz.functoolz import (thread_first, thread_last, memoize, curry, 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 @@ -632,34 +631,33 @@ def __int__(self): 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 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(): diff --git a/toolz/tests/test_inspect_args.py b/toolz/tests/test_inspect_args.py index 7381612f..da985044 100644 --- a/toolz/tests/test_inspect_args.py +++ b/toolz/tests/test_inspect_args.py @@ -7,7 +7,6 @@ num_required_args, has_varargs, has_keywords) from toolz._signatures import builtins import toolz._signatures as _sigs -from toolz.compatibility import PY3 from toolz.utils import raises @@ -115,8 +114,6 @@ def test_is_valid(check_valid=is_valid_args, incomplete=False): def test_is_valid_py3(check_valid=is_valid_args, incomplete=False): - if not PY3: - return orig_check_valid = check_valid check_valid = lambda func, *args, **kwargs: orig_check_valid(func, args, kwargs) @@ -255,20 +252,19 @@ def test_has_unknown_args(): assert has_varargs(make_func('x, y, z=1')) is False assert has_varargs(make_func('x, y, z=1, **kwargs')) is False - if PY3: - f = make_func('*args') - f.__signature__ = 34 - assert has_varargs(f) is False + f = make_func('*args') + f.__signature__ = 34 + assert has_varargs(f) is False - class RaisesValueError(object): - def __call__(self): - pass - @property - def __signature__(self): - raise ValueError('Testing Python 3.4') + class RaisesValueError(object): + def __call__(self): + pass + @property + def __signature__(self): + raise ValueError('Testing Python 3.4') - f = RaisesValueError() - assert has_varargs(f) is None + f = RaisesValueError() + assert has_varargs(f) is None def test_num_required_args(): @@ -298,8 +294,7 @@ def test_has_varargs(): assert has_varargs(lambda *args: None) assert has_varargs(lambda **kwargs: None) is False assert has_varargs(map) - if PY3: - assert has_varargs(max) is None + assert has_varargs(max) is None def test_is_arity(): @@ -315,8 +310,6 @@ def test_is_arity(): def test_introspect_curry_valid_py3(check_valid=is_valid_args, incomplete=False): - if not PY3: - return orig_check_valid = check_valid check_valid = lambda _func, *args, **kwargs: orig_check_valid(_func, args, kwargs) @@ -361,8 +354,6 @@ def test_introspect_curry_partial_py3(): def test_introspect_curry_py3(): - if not PY3: - return f = toolz.curry(make_func('')) assert num_required_args(f) == 0 assert is_arity(0, f) @@ -443,8 +434,6 @@ def is_missing(modname, name, func): def test_inspect_signature_property(): - if not PY3: - return # By adding AddX to our signature registry, we can inspect the class # itself and objects of the class. `inspect.signature` doesn't like @@ -491,8 +480,7 @@ def __wrapped__(self): func = lambda x: x wrapped = Wrapped(func) - if PY3: - assert inspect.signature(func) == inspect.signature(wrapped) + assert inspect.signature(func) == inspect.signature(wrapped) assert num_required_args(Wrapped) is None _sigs.signatures[Wrapped] = (_sigs.expand_sig((0, lambda func: None)),) diff --git a/toolz/tests/test_itertoolz.py b/toolz/tests/test_itertoolz.py index 3444e4b1..61618725 100644 --- a/toolz/tests/test_itertoolz.py +++ b/toolz/tests/test_itertoolz.py @@ -14,7 +14,6 @@ sliding_window, count, partition, partition_all, take_nth, pluck, join, diff, topk, peek, peekn, random_sample) -from toolz.compatibility import range, filter from operator import add, mul diff --git a/toolz/tests/test_serialization.py b/toolz/tests/test_serialization.py index 92b07f05..5f432ae5 100644 --- a/toolz/tests/test_serialization.py +++ b/toolz/tests/test_serialization.py @@ -2,7 +2,6 @@ import toolz import toolz.curried import pickle -from toolz.compatibility import PY3 from toolz.utils import raises @@ -114,8 +113,6 @@ def g3(self): def test_curried_qualname(): - if not PY3: - return def preserves_identity(obj): return pickle.loads(pickle.dumps(obj)) is obj diff --git a/toolz/tests/test_signatures.py b/toolz/tests/test_signatures.py index b5bb12e9..e03d333c 100644 --- a/toolz/tests/test_signatures.py +++ b/toolz/tests/test_signatures.py @@ -1,7 +1,6 @@ import functools import toolz._signatures as _sigs from toolz._signatures import builtins, _is_valid_args, _is_partial_args -from toolz.compatibility import PY3 def test_is_valid(check_valid=_is_valid_args, incomplete=False): @@ -53,8 +52,8 @@ def test_is_valid(check_valid=_is_valid_args, incomplete=False): assert check_valid(f, 1, key=None) assert check_valid(f, 1, 2, key=None) assert check_valid(f, 1, 2, 3, key=None) - assert check_valid(f, key=None, default=None) is (PY3 and incomplete) - assert check_valid(f, 1, key=None, default=None) is PY3 + assert check_valid(f, key=None, default=None) #is (PY3 and incomplete) + assert check_valid(f, 1, key=None, default=None) #is PY3 assert check_valid(f, 1, 2, key=None, default=None) is False assert check_valid(f, 1, 2, 3, key=None, default=None) is False @@ -84,4 +83,3 @@ def test_for_coverage(): # :) assert _sigs._has_varargs(None) is None assert _sigs._has_keywords(None) is None assert _sigs._num_required_args(None) is None - From 22ba48f09317bd5db5ebd186c7bddf27a8e846ca Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 15:02:31 -0500 Subject: [PATCH 12/47] Fix Python specific tests. --- toolz/tests/test_signatures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolz/tests/test_signatures.py b/toolz/tests/test_signatures.py index e03d333c..03b9293c 100644 --- a/toolz/tests/test_signatures.py +++ b/toolz/tests/test_signatures.py @@ -52,8 +52,8 @@ def test_is_valid(check_valid=_is_valid_args, incomplete=False): assert check_valid(f, 1, key=None) assert check_valid(f, 1, 2, key=None) assert check_valid(f, 1, 2, 3, key=None) - assert check_valid(f, key=None, default=None) #is (PY3 and incomplete) - assert check_valid(f, 1, key=None, default=None) #is PY3 + assert check_valid(f, key=None, default=None) is incomplete + assert check_valid(f, 1, key=None, default=None) assert check_valid(f, 1, 2, key=None, default=None) is False assert check_valid(f, 1, 2, 3, key=None, default=None) is False From 2ddfd0eb1d6673bfc94ff40bf506337d5bb89ab1 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 15:07:21 -0500 Subject: [PATCH 13/47] Add map and filter to toolz namespace. --- toolz/__init__.py | 4 ++++ toolz/curried/__init__.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/toolz/__init__.py b/toolz/__init__.py index 43594067..8e3b0b9a 100644 --- a/toolz/__init__.py +++ b/toolz/__init__.py @@ -10,6 +10,10 @@ sorted = sorted +map = map + +filter = filter + # Aliases comp = compose diff --git a/toolz/curried/__init__.py b/toolz/curried/__init__.py index 1f4ed7e1..356eddbd 100644 --- a/toolz/curried/__init__.py +++ b/toolz/curried/__init__.py @@ -64,7 +64,7 @@ do = toolz.curry(toolz.do) drop = toolz.curry(toolz.drop) excepts = toolz.curry(toolz.excepts) -filter = toolz.curry(filter) +filter = toolz.curry(toolz.filter) get = toolz.curry(toolz.get) get_in = toolz.curry(toolz.get_in) groupby = toolz.curry(toolz.groupby) @@ -75,7 +75,7 @@ join = toolz.curry(toolz.join) keyfilter = toolz.curry(toolz.keyfilter) keymap = toolz.curry(toolz.keymap) -map = toolz.curry(map) +map = toolz.curry(toolz.map) mapcat = toolz.curry(toolz.mapcat) nth = toolz.curry(toolz.nth) partial = toolz.curry(toolz.partial) From eab69e6478b7d437e8f4e225f6521dfda7027a47 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 15:11:00 -0500 Subject: [PATCH 14/47] Remove Python 2.7 from test travis.yml. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6d8404eb..6be84529 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ sudo: false language: python python: - - "2.7" - "3.4" - "3.5" - "3.6" From 9f4ab2fa425809c1071394baccc9f4e4a43cbd36 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 15:15:33 -0500 Subject: [PATCH 15/47] Fix sandbox tests. --- toolz/sandbox/tests/test_core.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/toolz/sandbox/tests/test_core.py b/toolz/sandbox/tests/test_core.py index 14e3847f..ca7cc68b 100644 --- a/toolz/sandbox/tests/test_core.py +++ b/toolz/sandbox/tests/test_core.py @@ -1,8 +1,6 @@ from toolz import curry, unique, first, take from toolz.sandbox.core import EqualityHashKey, unzip from itertools import count, repeat -from toolz.compatibility import map, zip - def test_EqualityHashKey_default_key(): EqualityHashDefault = curry(EqualityHashKey, None) From b281ddeb1c30476fc1205d5218753d402083eece Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 15:17:04 -0500 Subject: [PATCH 16/47] Add missing reduce import. --- toolz/dicttoolz.py | 1 + 1 file changed, 1 insertion(+) diff --git a/toolz/dicttoolz.py b/toolz/dicttoolz.py index 55281e3a..46e0abfd 100644 --- a/toolz/dicttoolz.py +++ b/toolz/dicttoolz.py @@ -1,4 +1,5 @@ import operator +from functools import reduce __all__ = ('merge', 'merge_with', 'valmap', 'keymap', 'itemmap', 'valfilter', 'keyfilter', 'itemfilter', From aa6d33edc2c465b0a6b2bcdbedf98ce9d4568427 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 15:40:10 -0500 Subject: [PATCH 17/47] Remove Python 3.4 support. Also removed 3.7-dev --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6be84529..80bc4ddc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,8 @@ sudo: false language: python python: - - "3.4" - "3.5" - "3.6" - - "3.7-dev" - "pypy" # Enable 3.7 without globally enabling sudo and dist: xenial for other build jobs From e4416e05c27cf47d239261134f31f3637a4e2474 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 15:41:34 -0500 Subject: [PATCH 18/47] "Hardcode" Python 3 support. --- toolz/compatibility.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/toolz/compatibility.py b/toolz/compatibility.py index 5e8756fd..29f0ebd4 100644 --- a/toolz/compatibility.py +++ b/toolz/compatibility.py @@ -1,5 +1,6 @@ +__all__ = ('PYPY', 'PY3') + import sys +PY3 = True PYPY = hasattr(sys, 'pypy_version_info') - -__all__ = ('PYPY',) From 03becaa7abd0c1a571b99a41c72fec27abb533fb Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 16:03:20 -0500 Subject: [PATCH 19/47] Add PyPy3. --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 80bc4ddc..324a70f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: python python: - "3.5" - "3.6" - - "pypy" # Enable 3.7 without globally enabling sudo and dist: xenial for other build jobs matrix: @@ -11,6 +10,9 @@ matrix: - python: 3.7 dist: xenial sudo: true + - python: "pypy3.5-7.0" + dist: xenial + sudo: true env: - PEP8_IGNORE="E731,W503,E402" From 72cf9755de264527ffcc492f1ded6bc413fe11f8 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 16:13:44 -0500 Subject: [PATCH 20/47] Make pycodestyle happy. --- toolz/_signatures.py | 8 +++++--- toolz/functoolz.py | 25 +++++++++++++++---------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/toolz/_signatures.py b/toolz/_signatures.py index f32b4c85..30fa06c9 100644 --- a/toolz/_signatures.py +++ b/toolz/_signatures.py @@ -596,20 +596,22 @@ def num_pos_args(sigspec): """ Return the number of positional arguments. ``f(x, y=1)`` has 1""" return sum(1 for x in sigspec.parameters.values() - if x.kind == x.POSITIONAL_OR_KEYWORD - and x.default is x.empty) + if x.kind == x.POSITIONAL_OR_KEYWORD + and x.default is x.empty) + def get_exclude_keywords(num_pos_only, sigspec): """ Return the names of position-only arguments if func has **kwargs""" if num_pos_only == 0: return () has_kwargs = any(x.kind == x.VAR_KEYWORD - for x in sigspec.parameters.values()) + for x in sigspec.parameters.values()) if not has_kwargs: return () pos_args = list(sigspec.parameters.values())[:num_pos_only] return tuple(x.name for x in pos_args) + def signature_or_spec(func): try: return inspect.signature(func) diff --git a/toolz/functoolz.py b/toolz/functoolz.py index 7f9e7d21..e80e422f 100644 --- a/toolz/functoolz.py +++ b/toolz/functoolz.py @@ -6,7 +6,7 @@ from textwrap import dedent from types import MethodType -from .compatibility import PYPY +from .compatibility import PYPY from .utils import no_default @@ -838,6 +838,7 @@ def _check_sigspec(sigspec, func, builtin_func, *builtin_args): return None, False return sigspec, None + if PYPY: # pragma: no cover _check_sigspec_orig = _check_sigspec @@ -848,6 +849,7 @@ def _check_sigspec(sigspec, func, builtin_func, *builtin_args): return None, val return _check_sigspec_orig(sigspec, func, builtin_func, *builtin_args) + _check_sigspec.__doc__ = """ \ Private function to aid in introspection compatibly across Python versions. @@ -857,31 +859,34 @@ def _check_sigspec(sigspec, func, builtin_func, *builtin_args): def num_required_args(func, sigspec=None): sigspec, rv = _check_sigspec(sigspec, func, _sigs._num_required_args, - func) + func) if sigspec is None: return rv return sum(1 for p in sigspec.parameters.values() - if p.default is p.empty - and p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY)) + if p.default is p.empty + and p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY)) + def has_varargs(func, sigspec=None): sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_varargs, func) if sigspec is None: return rv return any(p.kind == p.VAR_POSITIONAL - for p in sigspec.parameters.values()) + for p in sigspec.parameters.values()) + def has_keywords(func, sigspec=None): sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_keywords, func) if sigspec is None: return rv return any(p.default is not p.empty - or p.kind in (p.KEYWORD_ONLY, p.VAR_KEYWORD) - for p in sigspec.parameters.values()) + or p.kind in (p.KEYWORD_ONLY, p.VAR_KEYWORD) + for p in sigspec.parameters.values()) + def is_valid_args(func, args, kwargs, sigspec=None): sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_valid_args, - func, args, kwargs) + func, args, kwargs) if sigspec is None: return rv try: @@ -890,9 +895,10 @@ def is_valid_args(func, args, kwargs, sigspec=None): return False return True + def is_partial_args(func, args, kwargs, sigspec=None): sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_partial_args, - func, args, kwargs) + func, args, kwargs) if sigspec is None: return rv try: @@ -902,7 +908,6 @@ def is_partial_args(func, args, kwargs, sigspec=None): return True - def is_arity(n, func, sigspec=None): """ Does a function have only n positional arguments? From 373f620beaf0b979a846f5ff1d013de747862f86 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 16:16:04 -0500 Subject: [PATCH 21/47] Address another pycodestyle complaint. --- toolz/functoolz.py | 1 + 1 file changed, 1 insertion(+) diff --git a/toolz/functoolz.py b/toolz/functoolz.py index e80e422f..46f18bc9 100644 --- a/toolz/functoolz.py +++ b/toolz/functoolz.py @@ -857,6 +857,7 @@ def _check_sigspec(sigspec, func, builtin_func, *builtin_args): the signature registry in toolz._signatures is used. """ + def num_required_args(func, sigspec=None): sigspec, rv = _check_sigspec(sigspec, func, _sigs._num_required_args, func) From 91edafa520d7beffe277b2956b9da15b7649fd86 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Thu, 17 Oct 2019 17:29:23 -0500 Subject: [PATCH 22/47] Remove ending line. --- toolz/compatibility.py | 1 - 1 file changed, 1 deletion(-) diff --git a/toolz/compatibility.py b/toolz/compatibility.py index f5f933c9..29f0ebd4 100644 --- a/toolz/compatibility.py +++ b/toolz/compatibility.py @@ -4,4 +4,3 @@ PY3 = True PYPY = hasattr(sys, 'pypy_version_info') - From 66527cfe2193085fe0f83144a4d4a9589bff163d Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Thu, 17 Oct 2019 17:38:34 -0500 Subject: [PATCH 23/47] Update documentation. --- README.rst | 2 +- doc/source/install.rst | 2 +- doc/source/references.rst | 4 ++-- setup.py | 4 +--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 099c3ff8..04e99690 100644 --- a/README.rst +++ b/README.rst @@ -73,7 +73,7 @@ This builds a standard wordcount function from pieces within ``toolz``: Dependencies ------------ -``toolz`` supports Python 2.7 and Python 3.4+ with a common codebase. +``toolz`` supports Python 3.4+ with a common codebase. It is pure Python and requires no dependencies beyond the standard library. diff --git a/doc/source/install.rst b/doc/source/install.rst index cfba80aa..3a999259 100644 --- a/doc/source/install.rst +++ b/doc/source/install.rst @@ -11,4 +11,4 @@ three ways: 1. Toolz is pure Python 2. Toolz relies only on the standard library -3. Toolz simultaneously supports Python versions 2.7, 3.4+, PyPy +3. Toolz simultaneously supports Python versions 3.4+ and PyPy diff --git a/doc/source/references.rst b/doc/source/references.rst index 3cdb2db9..3da61a9a 100644 --- a/doc/source/references.rst +++ b/doc/source/references.rst @@ -7,9 +7,9 @@ References similar library for Ruby - `Clojure `__: A functional language whose standard library has several counterparts in ``toolz`` -- `itertools `__: The +- `itertools `__: The Python standard library for iterator tools -- `functools `__: The +- `functools `__: The Python standard library for function tools - `Functional Programming HOWTO `__: The description of functional programming features from the official diff --git a/setup.py b/setup.py index 9577b7fe..1b20b41e 100755 --- a/setup.py +++ b/setup.py @@ -21,13 +21,11 @@ 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.*", + python_requires=">=3.4", classifiers=[ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: BSD License", "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", From 8c64cca8f62b4a52215027b16a980c5446ac0d14 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Thu, 17 Oct 2019 17:46:54 -0500 Subject: [PATCH 24/47] Add pypy 7.2 to TravisCI. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 324a70f8..86d9b6dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,9 @@ matrix: - python: "pypy3.5-7.0" dist: xenial sudo: true + - python: "pypy3.6-7.2" + dist: xenial + sudo: true env: - PEP8_IGNORE="E731,W503,E402" From 1f52206d70b4e8b031784d28d61a8102988094b8 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Thu, 17 Oct 2019 17:51:02 -0500 Subject: [PATCH 25/47] Pypy3.6 try 2. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 86d9b6dc..61d6484e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ matrix: - python: "pypy3.5-7.0" dist: xenial sudo: true - - python: "pypy3.6-7.2" + - python: "pypy3.6-7.1.1" dist: xenial sudo: true From b1aad6825549f8015cd76aa0b70d96d377d73e0a Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Thu, 17 Oct 2019 17:57:13 -0500 Subject: [PATCH 26/47] Add Python 3.8 to TravisCI. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 61d6484e..dcea35b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,9 @@ matrix: - python: 3.7 dist: xenial sudo: true + - python: 3.8 + dist: xenial + sudo: true - python: "pypy3.5-7.0" dist: xenial sudo: true From 6bfbe7b8698dd54db32de3d94af0e06f6a764399 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Thu, 17 Oct 2019 17:59:48 -0500 Subject: [PATCH 27/47] Add Python 3.8 try 2. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dcea35b9..17b21e6a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ matrix: - python: 3.7 dist: xenial sudo: true - - python: 3.8 + - python: "3.8-dev" dist: xenial sudo: true - python: "pypy3.5-7.0" From 62a2470605b866d6622142ea75185d8928b1e41e Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Thu, 17 Oct 2019 18:01:04 -0500 Subject: [PATCH 28/47] Revert "Add Python 3.8 try 2." This reverts commit 6bfbe7b8698dd54db32de3d94af0e06f6a764399. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 17b21e6a..dcea35b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ matrix: - python: 3.7 dist: xenial sudo: true - - python: "3.8-dev" + - python: 3.8 dist: xenial sudo: true - python: "pypy3.5-7.0" From 30b5e8e317ffd3f0de549b18c86f2fb20ac97729 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 6 Jan 2020 13:06:00 -0600 Subject: [PATCH 29/47] Fix mapping import for Python 3. --- toolz/tests/test_dicttoolz.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/toolz/tests/test_dicttoolz.py b/toolz/tests/test_dicttoolz.py index 8a3d5d7c..2693899b 100644 --- a/toolz/tests/test_dicttoolz.py +++ b/toolz/tests/test_dicttoolz.py @@ -1,12 +1,11 @@ from collections import defaultdict as _defaultdict +from collections import Mapping 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, Mapping - def inc(x): From 3b5afe90edd671fcb28c66afba691dd4bb846a2a Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Sat, 13 Jul 2019 14:04:53 -0500 Subject: [PATCH 30/47] Revert "Remove Python 2/3 specific code." This reverts commit 0684a4d91ba140a22a64aef85d4a7148b7f881a7. # Conflicts: # toolz/compatibility.py --- toolz/compatibility.py | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/toolz/compatibility.py b/toolz/compatibility.py index 29f0ebd4..51e3673f 100644 --- a/toolz/compatibility.py +++ b/toolz/compatibility.py @@ -1,6 +1,34 @@ -__all__ = ('PYPY', 'PY3') - +import operator import sys - -PY3 = True +PY3 = sys.version_info[0] > 2 +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') + +if PY3: + map = map + filter = filter + range = range + zip = zip + from functools import reduce + from itertools import zip_longest + from itertools import filterfalse + iteritems = operator.methodcaller('items') + iterkeys = operator.methodcaller('keys') + itervalues = operator.methodcaller('values') + from collections.abc import Sequence +else: + range = xrange + reduce = reduce + from itertools import imap as map + from itertools import ifilter as filter + from itertools import ifilterfalse as filterfalse + from itertools import izip as zip + from itertools import izip_longest as zip_longest + iteritems = operator.methodcaller('iteritems') + iterkeys = operator.methodcaller('iterkeys') + itervalues = operator.methodcaller('itervalues') + from collections import Sequence From 4dea63857cbde7f9601d768324722d764a1b4daf Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 12:19:04 -0500 Subject: [PATCH 31/47] Fix DeprecationWarning. --- toolz/tests/test_dicttoolz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolz/tests/test_dicttoolz.py b/toolz/tests/test_dicttoolz.py index 2693899b..d45cd6cf 100644 --- a/toolz/tests/test_dicttoolz.py +++ b/toolz/tests/test_dicttoolz.py @@ -1,5 +1,5 @@ from collections import defaultdict as _defaultdict -from collections import Mapping +from collections.abc import Mapping import os from toolz.dicttoolz import (merge, merge_with, valmap, keymap, update_in, assoc, dissoc, keyfilter, valfilter, itemmap, From dce0edf5240c8d1af9e6f27292e0bad63a3eece7 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 15:06:51 -0500 Subject: [PATCH 32/47] Add deprecation warning to compatibility module. We also check that PYPY is a version that supports Python 3. --- toolz/compatibility.py | 45 +++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/toolz/compatibility.py b/toolz/compatibility.py index 51e3673f..b42be099 100644 --- a/toolz/compatibility.py +++ b/toolz/compatibility.py @@ -1,34 +1,29 @@ +import warnings +warnings.warn("The toolz.compatibility module is no longer " + "needed in Python 3.x and has been deprecated. Please " + "import these utilities directly from the standard library." + "This module will be removed in a future release.", DeprecationWarning) + import operator import sys + PY3 = sys.version_info[0] > 2 PY34 = sys.version_info[0] == 3 and sys.version_info[1] == 4 -PYPY = hasattr(sys, 'pypy_version_info') +PYPY = hasattr(sys, 'pypy_version_info') and PY3 __all__ = ('map', 'filter', 'range', 'zip', 'reduce', 'zip_longest', 'iteritems', 'iterkeys', 'itervalues', 'filterfalse', 'PY3', 'PY34', 'PYPY') -if PY3: - map = map - filter = filter - range = range - zip = zip - from functools import reduce - from itertools import zip_longest - from itertools import filterfalse - iteritems = operator.methodcaller('items') - iterkeys = operator.methodcaller('keys') - itervalues = operator.methodcaller('values') - from collections.abc import Sequence -else: - range = xrange - reduce = reduce - from itertools import imap as map - from itertools import ifilter as filter - from itertools import ifilterfalse as filterfalse - from itertools import izip as zip - from itertools import izip_longest as zip_longest - iteritems = operator.methodcaller('iteritems') - iterkeys = operator.methodcaller('iterkeys') - itervalues = operator.methodcaller('itervalues') - from collections import Sequence + +map = map +filter = filter +range = range +zip = zip +from functools import reduce +from itertools import zip_longest +from itertools import filterfalse +iteritems = operator.methodcaller('items') +iterkeys = operator.methodcaller('keys') +itervalues = operator.methodcaller('values') +from collections.abc import Sequence \ No newline at end of file From 15175067348ac6c531a0343d710eac44f356f362 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 15:08:45 -0500 Subject: [PATCH 33/47] Remove compatibility tests These tests are testing features of Python 3. We test that the import works. --- toolz/tests/test_compatibility.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/toolz/tests/test_compatibility.py b/toolz/tests/test_compatibility.py index b0072970..db304027 100644 --- a/toolz/tests/test_compatibility.py +++ b/toolz/tests/test_compatibility.py @@ -1,16 +1 @@ - -def test_map_filter_are_lazy(): - def bad(x): - raise Exception() - map(bad, [1, 2, 3]) - filter(bad, [1, 2, 3]) - - -def test_dict_iteration(): - d = {'a': 1, 'b': 2, 'c': 3} - assert not isinstance(d.items(), list) - assert not isinstance(d.keys(), list) - assert not isinstance(d.values(), list) - assert set(d.items()) == set(d.items()) - assert set(d.keys()) == set(d.keys()) - assert set(d.values()) == set(d.values()) +import toolz.compatibility \ No newline at end of file From cb345f75b35f3704aedcee01b7b279c263a53c88 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 15:12:40 -0500 Subject: [PATCH 34/47] Support Python >=3.5 Update setup.py to reflect current Python support. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 1b20b41e..1adc0927 100755 --- a/setup.py +++ b/setup.py @@ -21,15 +21,15 @@ long_description=(open('README.rst').read() if exists('README.rst') else ''), zip_safe=False, - python_requires=">=3.4", + python_requires=">=3.5", classifiers=[ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy"]) From b05665dba01b7d76c60b40af5928fbeacace9650 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 15:16:50 -0500 Subject: [PATCH 35/47] Pycodestyle changes. --- toolz/compatibility.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/toolz/compatibility.py b/toolz/compatibility.py index b42be099..b44795c4 100644 --- a/toolz/compatibility.py +++ b/toolz/compatibility.py @@ -1,8 +1,9 @@ import warnings warnings.warn("The toolz.compatibility module is no longer " - "needed in Python 3.x and has been deprecated. Please " - "import these utilities directly from the standard library." - "This module will be removed in a future release.", DeprecationWarning) + "needed in Python 3.x and has been deprecated. Please " + "import these utilities directly from the standard library." + "This module will be removed in a future release.", + category=DeprecationWarning) import operator import sys @@ -26,4 +27,4 @@ iteritems = operator.methodcaller('items') iterkeys = operator.methodcaller('keys') itervalues = operator.methodcaller('values') -from collections.abc import Sequence \ No newline at end of file +from collections.abc import Sequence From cbfc621193c709fd1177cc913ae367338be668ef Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 15:24:03 -0500 Subject: [PATCH 36/47] Fix formatting of deprecation warning. --- toolz/compatibility.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolz/compatibility.py b/toolz/compatibility.py index b44795c4..9a1242c1 100644 --- a/toolz/compatibility.py +++ b/toolz/compatibility.py @@ -1,7 +1,7 @@ import warnings warnings.warn("The toolz.compatibility module is no longer " - "needed in Python 3.x and has been deprecated. Please " - "import these utilities directly from the standard library." + "needed in Python 3 and has been deprecated. Please " + "import these utilities directly from the standard library. " "This module will be removed in a future release.", category=DeprecationWarning) From bcddd8bd418a1420722bc886c67acb91d3cbd452 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 15:33:47 -0500 Subject: [PATCH 37/47] Update travis.yml --- .travis.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index dcea35b9..51f90e58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,16 +3,12 @@ language: python python: - "3.5" - "3.6" + - "3.7" + - "3.8" # Enable 3.7 without globally enabling sudo and dist: xenial for other build jobs matrix: include: - - python: 3.7 - dist: xenial - sudo: true - - python: 3.8 - dist: xenial - sudo: true - python: "pypy3.5-7.0" dist: xenial sudo: true From 1e1842de98843142aa850c9b2d7820ebccd69f1c Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 15:37:43 -0500 Subject: [PATCH 38/47] Update travis ci (pypy) --- .travis.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 51f90e58..f8ee519d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,16 +5,9 @@ python: - "3.6" - "3.7" - "3.8" - -# Enable 3.7 without globally enabling sudo and dist: xenial for other build jobs -matrix: - include: - - python: "pypy3.5-7.0" - dist: xenial - sudo: true - - python: "pypy3.6-7.1.1" - dist: xenial - sudo: true + - "pypy3.5-7.0" + - "pypy3.6-7.1.1" + - "pypy3.6-7.3.0" env: - PEP8_IGNORE="E731,W503,E402" From aa658ed0b8ea167c55795d7f88401d152997e9f5 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 15:42:25 -0500 Subject: [PATCH 39/47] Add Python 3.9-dev --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f8ee519d..24fc8ad2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,9 @@ python: - "3.6" - "3.7" - "3.8" + - "3.9-dev" - "pypy3.5-7.0" - "pypy3.6-7.1.1" - - "pypy3.6-7.3.0" env: - PEP8_IGNORE="E731,W503,E402" From 81aafd64ad0700d7101914f13caa198222bc7f35 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 15:44:16 -0500 Subject: [PATCH 40/47] Test with latest versions of pypy for Python 3.5/3.6 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 24fc8ad2..287bdd45 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ python: - "3.7" - "3.8" - "3.9-dev" - - "pypy3.5-7.0" - - "pypy3.6-7.1.1" + - "pypy3.5" + - "pypy3.6" env: - PEP8_IGNORE="E731,W503,E402" From f0aa08249f6f1c900e2bc9cbe1e34b5e550036bc Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 15:48:18 -0500 Subject: [PATCH 41/47] Revert "Test with latest versions of pypy for Python 3.5/3.6" This reverts commit 81aafd64ad0700d7101914f13caa198222bc7f35. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 287bdd45..24fc8ad2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ python: - "3.7" - "3.8" - "3.9-dev" - - "pypy3.5" - - "pypy3.6" + - "pypy3.5-7.0" + - "pypy3.6-7.1.1" env: - PEP8_IGNORE="E731,W503,E402" From f44567dd32ef48205366fcd1a52013fbb640e8da Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 16:58:36 -0500 Subject: [PATCH 42/47] Update readme for Python support. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 04e99690..00ecc856 100644 --- a/README.rst +++ b/README.rst @@ -73,7 +73,7 @@ This builds a standard wordcount function from pieces within ``toolz``: Dependencies ------------ -``toolz`` supports Python 3.4+ with a common codebase. +``toolz`` supports Python 3.5+ with a common codebase. It is pure Python and requires no dependencies beyond the standard library. From fa0887e30eef0f74f3aa776354b9cfed7437f512 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 17:04:46 -0500 Subject: [PATCH 43/47] Remove python version specific no cover pragma. --- .coveragerc | 1 - toolz/functoolz.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.coveragerc b/.coveragerc index f9b82ac7..5098075d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -7,4 +7,3 @@ omit = [report] exclude_lines = pragma: no cover - pragma: py$MAJOR_PYTHON_VERSION no cover diff --git a/toolz/functoolz.py b/toolz/functoolz.py index 46f18bc9..6794a903 100644 --- a/toolz/functoolz.py +++ b/toolz/functoolz.py @@ -832,7 +832,7 @@ def _check_sigspec(sigspec, func, builtin_func, *builtin_args): hasattr(func, '__signature__') and hasattr(func.__signature__, '__get__') )) - ): # pragma: no cover (not covered in Python 3.4) + ): val = builtin_func(*builtin_args) return None, val return None, False @@ -843,7 +843,7 @@ def _check_sigspec(sigspec, func, builtin_func, *builtin_args): _check_sigspec_orig = _check_sigspec def _check_sigspec(sigspec, func, builtin_func, *builtin_args): - # Python 3.4 and PyPy may lie, so use our registry for builtins instead + # PyPy may lie, so use our registry for builtins instead if func in _sigs.signatures: val = builtin_func(*builtin_args) return None, val From 85579a3903f1440cece7c4d709b66675f20f6cda Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 17:10:29 -0500 Subject: [PATCH 44/47] Import filterfalse and zip_longest into global namespace. --- toolz/itertoolz.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/toolz/itertoolz.py b/toolz/itertoolz.py index 05679eed..72d41896 100644 --- a/toolz/itertoolz.py +++ b/toolz/itertoolz.py @@ -3,6 +3,7 @@ import collections import operator from functools import partial +from itertools import filterfalse, zip_longest from random import Random from collections.abc import Sequence from toolz.utils import no_default @@ -24,7 +25,7 @@ def remove(predicate, seq): >>> list(remove(iseven, [1, 2, 3, 4])) [1, 3] """ - return itertools.filterfalse(predicate, seq) + return filterfalse(predicate, seq) def accumulate(binop, seq, initial=no_default): @@ -702,7 +703,7 @@ def partition(n, seq, pad=no_pad): if pad is no_pad: return zip(*args) else: - return itertools.zip_longest(*args, fillvalue=pad) + return zip_longest(*args, fillvalue=pad) def partition_all(n, seq): @@ -720,7 +721,7 @@ def partition_all(n, seq): partition """ args = [iter(seq)] * n - it = itertools.zip_longest(*args, fillvalue=no_pad) + it = zip_longest(*args, fillvalue=no_pad) try: prev = next(it) except StopIteration: @@ -948,7 +949,7 @@ def diff(*seqs, **kwargs): if default == no_default: iters = zip(*seqs) else: - iters = itertools.zip_longest(*seqs, fillvalue=default) + iters = zip_longest(*seqs, fillvalue=default) key = kwargs.get('key', None) if key is None: for items in iters: From b58439510381ef1a2fdee7fee84d6466a20c00a2 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 17:15:47 -0500 Subject: [PATCH 45/47] Clean up __init__ --- toolz/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/toolz/__init__.py b/toolz/__init__.py index 8e3b0b9a..d94c5bb5 100644 --- a/toolz/__init__.py +++ b/toolz/__init__.py @@ -8,12 +8,6 @@ from functools import partial, reduce -sorted = sorted - -map = map - -filter = filter - # Aliases comp = compose From 996ec0c86276df236c5bdb3737dcac5b4e0592ff Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 17:18:34 -0500 Subject: [PATCH 46/47] Revert "Clean up __init__" This reverts commit b58439510381ef1a2fdee7fee84d6466a20c00a2. --- toolz/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/toolz/__init__.py b/toolz/__init__.py index d94c5bb5..8e3b0b9a 100644 --- a/toolz/__init__.py +++ b/toolz/__init__.py @@ -8,6 +8,12 @@ from functools import partial, reduce +sorted = sorted + +map = map + +filter = filter + # Aliases comp = compose From 918918a92bc67cb2282d7f83e00811a6f879e322 Mon Sep 17 00:00:00 2001 From: Ryan Grout Date: Mon, 16 Mar 2020 17:39:25 -0500 Subject: [PATCH 47/47] Fix pragma checks. --- toolz/_signatures.py | 2 +- toolz/functoolz.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/toolz/_signatures.py b/toolz/_signatures.py index 30fa06c9..328cea91 100644 --- a/toolz/_signatures.py +++ b/toolz/_signatures.py @@ -742,7 +742,7 @@ def _has_varargs(func): checks = [check_varargs(sig) for sig in sigs] if all(checks): return True - elif any(checks): # pragma: py2 no cover + elif any(checks): return None return False diff --git a/toolz/functoolz.py b/toolz/functoolz.py index 6794a903..21a7d3be 100644 --- a/toolz/functoolz.py +++ b/toolz/functoolz.py @@ -346,14 +346,14 @@ def __reduce__(self): func = self.func modname = getattr(func, '__module__', None) qualname = getattr(func, '__qualname__', None) - if qualname is None: # pragma: py3 no cover + if qualname is None: # pragma: no cover qualname = getattr(func, '__name__', None) is_decorated = None if modname and qualname: attrs = [] obj = import_module(modname) for attr in qualname.split('.'): - if isinstance(obj, curry): # pragma: py2 no cover + if isinstance(obj, curry): attrs.append('func') obj = obj.func obj = getattr(obj, attr, None)