Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-funcy for openSUSE:Factory 
checked in at 2021-09-06 15:58:09
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-funcy (Old)
 and      /work/SRC/openSUSE:Factory/.python-funcy.new.1899 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-funcy"

Mon Sep  6 15:58:09 2021 rev:8 rq:917116 version:1.16

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-funcy/python-funcy.changes        
2020-08-18 15:05:25.579901082 +0200
+++ /work/SRC/openSUSE:Factory/.python-funcy.new.1899/python-funcy.changes      
2021-09-06 15:58:25.905289643 +0200
@@ -1,0 +2,19 @@
+Tue Aug 31 10:08:03 UTC 2021 - John Paul Adrian Glaubitz 
<adrian.glaub...@suse.com>
+
+- Update to 1.16
+  * support Python 3.9 officially
+  * unify @memoize() and @cache(): both have 
.skip/.memory/.invalidate/.invalidate_all now
+  * support dynamic resulting exception in @reraise() (Laurens Duijvesteijn)
+  * made () optional for @decorator-made decorators with kw-only args
+  * added @throttle()
+  * added has_path() (Denys Zorinets)
+  * fixed autocurry kwargs handling
+- from version 1.15
+  * made rpartial accept keyworded arguments (Ruan Comelli)
+  * made `@cache.invalidate()` idempotent (Dmitry Vasilyanov)
+  * made raiser() accept a string as a shortcut
+  * fixed cheatsheat description for 'distinct' helper (tsouvarev)
+  * fixed some seqs docstrings
+  * fixed some typos (Tim Gates)
+
+-------------------------------------------------------------------

Old:
----
  funcy-1.14.tar.gz

New:
----
  funcy-1.16.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-funcy.spec ++++++
--- /var/tmp/diff_new_pack.Sy7DxL/_old  2021-09-06 15:58:26.321289546 +0200
+++ /var/tmp/diff_new_pack.Sy7DxL/_new  2021-09-06 15:58:26.321289546 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-funcy
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2021 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-funcy
-Version:        1.14
+Version:        1.16
 Release:        0
 Summary:        Functional tools for Python
 License:        BSD-3-Clause

++++++ funcy-1.14.tar.gz -> funcy-1.16.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/CHANGELOG new/funcy-1.16/CHANGELOG
--- old/funcy-1.14/CHANGELOG    2019-11-28 20:08:47.000000000 +0100
+++ new/funcy-1.16/CHANGELOG    2021-05-10 07:38:06.000000000 +0200
@@ -1,3 +1,20 @@
+1.16
+- support Python 3.9 officially
+- unify @memoize() and @cache(): both have 
.skip/.memory/.invalidate/.invalidate_all now
+- support dynamic resulting exception in @reraise() (Laurens Duijvesteijn)
+- made () optional for @decorator-made decorators with kw-only args
+- added @throttle()
+- added has_path() (Denys Zorinets)
+- fixed autocurry kwargs handling
+
+1.15
+- made rpartial accept keyworded arguments (Ruan Comelli)
+- made `@cache.invalidate()` idempotent (Dmitry Vasilyanov)
+- made raiser() accept a string as a shortcut
+- fixed cheatsheat description for 'distinct' helper (tsouvarev)
+- fixed some seqs docstrings
+- fixed some typos (Tim Gates)
+
 1.14
 - stated Python 3.7 and 3.8 support
 - dropped Python 2.6
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/LICENSE new/funcy-1.16/LICENSE
--- old/funcy-1.14/LICENSE      2018-02-08 14:01:12.000000000 +0100
+++ new/funcy-1.16/LICENSE      2020-07-09 17:41:29.000000000 +0200
@@ -1,4 +1,4 @@
-Copyright (c) 2012-2018, Alexander Schepanovski.
+Copyright (c) 2012-2020, Alexander Schepanovski.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without 
modification,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/PKG-INFO new/funcy-1.16/PKG-INFO
--- old/funcy-1.14/PKG-INFO     2019-11-28 20:41:42.000000000 +0100
+++ new/funcy-1.16/PKG-INFO     2021-05-10 09:17:36.870961700 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: funcy
-Version: 1.14
+Version: 1.16
 Summary: A fancy and practical functional tools
 Home-page: http://github.com/Suor/funcy
 Author: Alexander Schepanovski
@@ -219,6 +219,7 @@
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/VERSION new/funcy-1.16/VERSION
--- old/funcy-1.14/VERSION      2019-11-28 20:15:30.000000000 +0100
+++ new/funcy-1.16/VERSION      2021-05-10 07:38:09.000000000 +0200
@@ -1 +1 @@
-1.14
+1.16
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/funcy/_inspect.py 
new/funcy-1.16/funcy/_inspect.py
--- old/funcy-1.14/funcy/_inspect.py    2018-10-03 14:49:11.000000000 +0200
+++ new/funcy-1.16/funcy/_inspect.py    2021-05-08 11:10:10.000000000 +0200
@@ -1,5 +1,6 @@
 from __future__ import absolute_import
-from inspect import CO_VARARGS
+from inspect import CO_VARARGS, CO_VARKEYWORDS
+from collections import namedtuple
 import types
 import re
 
@@ -94,6 +95,7 @@
 
 
 type_classes = (type, types.ClassType) if hasattr(types, 'ClassType') else type
+Spec = namedtuple("Spec", "max_n names req_n req_names kw")
 
 
 def get_spec(func, _cache={}):
@@ -107,35 +109,40 @@
     if mod in STD_MODULES or mod in ARGS and func.__name__ in ARGS[mod]:
         _spec = ARGS[mod].get(func.__name__, '*')
         required, _, optional = _spec.partition('-')
-        required_names = re.findall(r'\w+|\*', required)
-        spec = set(required_names), len(required_names), len(required_names) + 
len(optional)
+        req_names = re.findall(r'\w+|\*', required)  # a list with dups of *
+        max_n = len(req_names) + len(optional)
+        req_n = len(req_names)
+        spec = Spec(max_n=max_n, names=set(), req_n=req_n, 
req_names=set(req_names), kw=False)
         _cache[func] = spec
         return spec
     elif isinstance(func, type_classes):
         # Old style classes without base
         if not hasattr(func, '__init__'):
-            return set(), 0, 0
+            return Spec(max_n=0, names=set(), req_n=0, req_names=set(), 
kw=False)
         # __init__ inherited from builtin classes
         objclass = getattr(func.__init__, '__objclass__', None)
         if objclass and objclass is not func:
             return get_spec(objclass)
         # Introspect constructor and remove self
-        _required_names, _required_n, _max_n = get_spec(func.__init__)
-        self_name = func.__init__.__code__.co_varnames[0]
-        return _required_names - set([self_name]), _required_n - 1, _max_n - 1
+        spec = get_spec(func.__init__)
+        self_set = set([func.__init__.__code__.co_varnames[0]])
+        return spec._replace(max_n=spec.max_n - 1, names=spec.names - self_set,
+                             req_n=spec.req_n - 1, req_names=spec.req_names - 
self_set)
     else:
         try:
             defaults_n = len(func.__defaults__)
         except (AttributeError, TypeError):
             defaults_n = 0
         try:
-            names = func.__code__.co_varnames
+            varnames = func.__code__.co_varnames
             n = func.__code__.co_argcount
-            required_n = n - defaults_n
-            required_names = set(names[:required_n])
+            names = set(varnames[:n])
+            req_n = n - defaults_n
+            req_names = set(varnames[:req_n])
+            kw = bool(func.__code__.co_flags & CO_VARKEYWORDS)
             # If there are varargs they could be required, but all keywords 
args can't be
-            max_n = required_n + 1 if func.__code__.co_flags & CO_VARARGS else 
n
-            return required_names, required_n, max_n
+            max_n = req_n + 1 if func.__code__.co_flags & CO_VARARGS else n
+            return Spec(max_n=max_n, names=names, req_n=req_n, 
req_names=req_names, kw=kw)
         except AttributeError:
             raise ValueError('Unable to introspect %s() arguments'
                                 % getattr(func, '__name__', func))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/funcy/calc.py new/funcy-1.16/funcy/calc.py
--- old/funcy-1.14/funcy/calc.py        2019-07-03 16:30:38.000000000 +0200
+++ new/funcy-1.16/funcy/calc.py        2021-05-09 05:12:04.000000000 +0200
@@ -1,5 +1,8 @@
-from datetime import datetime, timedelta
+from datetime import timedelta
+import time
 import inspect
+from collections import deque
+from bisect import bisect
 
 from .decorators import wraps
 from .compat import PY2
@@ -8,9 +11,11 @@
 
 
 
-class SkipMemoization(Exception):
+class SkipMemory(Exception):
     pass
 
+SkipMemoization = SkipMemory  # Old name
+
 # TODO: use real kwonly once in Python 3 only
 def memoize(*args, **kwargs):
     """@memoize(key_func=None). Makes decorated function memoize its results.
@@ -28,13 +33,26 @@
     if kwargs:
         raise TypeError('memoize() got unexpected keyword arguments: %s', ', 
'.join(kwargs))
 
-    def decorator(func):
-        memory = {}
+    return _memory_decorator({}, key_func)
+
+memoize.skip = SkipMemory
+
+
+def cache(timeout, key_func=None):
+    """Caches a function results for timeout seconds."""
+    if isinstance(timeout, timedelta):
+        timeout = timeout.total_seconds()
+
+    return _memory_decorator(CacheMemory(timeout), key_func)
+
+cache.skip = SkipMemory
+
 
+def _memory_decorator(memory, key_func):
+    def decorator(func):
         @wraps(func)
         def wrapper(*args, **kwargs):
-            # NOTE: we inline this here but not in @cache,
-            #       since @memoize also targets microoptimizations.
+            # We inline this here since @memoize also targets 
microoptimizations
             key = key_func(*args, **kwargs) if key_func else \
                   args + tuple(sorted(kwargs.items())) if kwargs else args
             try:
@@ -46,11 +64,48 @@
                 except SkipMemoization as e:
                     return e.args[0] if e.args else None
 
+        def invalidate(*args, **kwargs):
+            key = key_func(*args, **kwargs) if key_func else \
+                  args + tuple(sorted(kwargs.items())) if kwargs else args
+            memory.pop(key, None)
+        wrapper.invalidate = invalidate
+
+        def invalidate_all():
+            memory.clear()
+        wrapper.invalidate_all = invalidate_all
+
         wrapper.memory = memory
         return wrapper
     return decorator
 
-memoize.skip = SkipMemoization
+class CacheMemory(dict):
+    def __init__(self, timeout):
+        self.timeout = timeout
+        self.clear()
+
+    def __setitem__(self, key, value):
+        expires_at = time.time() + self.timeout
+        dict.__setitem__(self, key, (value, expires_at))
+        self._keys.append(key)
+        self._expires.append(expires_at)
+
+    def __getitem__(self, key):
+        value, expires_at = dict.__getitem__(self, key)
+        if expires_at <= time.time():
+            self.expire()
+            raise KeyError(key)
+        return value
+
+    def expire(self):
+        i = bisect(self._expires, time.time())
+        for _ in range(i):
+            self._expires.popleft()
+            self.pop(self._keys.popleft(), None)
+
+    def clear(self):
+        dict.clear(self)
+        self._keys = deque()
+        self._expires = deque()
 
 
 def _make_lookuper(silent):
@@ -97,43 +152,6 @@
 silent_lookuper.__name__ = 'silent_lookuper'
 
 
-def cache(timeout, key_func=None):
-    """Caches a function results for timeout seconds."""
-    if isinstance(timeout, int):
-        timeout = timedelta(seconds=timeout)
-
-    if key_func is None:
-        key_func = lambda *a, **kw: a + tuple(sorted(kw.items())) if kw else a
-
-    def decorator(func):
-        cache = {}
-
-        @wraps(func)
-        def wrapper(*args, **kwargs):
-            key = key_func(*args, **kwargs)
-            if key in cache:
-                result, timestamp = cache[key]
-                if datetime.now() - timestamp < timeout:
-                    return result
-                else:
-                    del cache[key]
-
-            result = func(*args, **kwargs)
-            cache[key] = result, datetime.now()
-            return result
-
-        def invalidate(*args, **kwargs):
-            cache.pop(key_func(*args, **kwargs))
-        wrapper.invalidate = invalidate
-
-        def invalidate_all():
-            cache.clear()
-        wrapper.invalidate_all = invalidate_all
-
-        return wrapper
-    return decorator
-
-
 if PY2:
     def has_arg_types(func):
         spec = inspect.getargspec(func)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/funcy/colls.py 
new/funcy-1.16/funcy/colls.py
--- old/funcy-1.14/funcy/colls.py       2019-05-21 10:13:31.000000000 +0200
+++ new/funcy-1.16/funcy/colls.py       2021-03-21 06:53:51.000000000 +0100
@@ -19,7 +19,7 @@
            'is_distinct', 'all', 'any', 'none', 'one', 'some',
            'zipdict', 'flip', 'project', 'omit', 'zip_values', 'zip_dicts',
            'where', 'pluck', 'pluck_attr', 'invoke', 'lwhere', 'lpluck', 
'lpluck_attr', 'linvoke',
-           'get_in', 'set_in', 'update_in']
+           'get_in', 'set_in', 'update_in', 'has_path']
 
 
 ### Generic ops
@@ -32,7 +32,7 @@
 
 def _factory(coll, mapper=None):
     coll_type = type(coll)
-    # Hack for defaultdicts overriden constructor
+    # Hack for defaultdicts overridden constructor
     if isinstance(coll, defaultdict):
         item_factory = compose(mapper, coll.default_factory) if mapper and 
coll.default_factory \
                        else coll.default_factory
@@ -265,7 +265,6 @@
     for key in keys:
         yield key, tuple(d[key] for d in dicts)
 
-
 def get_in(coll, path, default=None):
     """Returns a value at path in the given nested collection."""
     for key in path:
@@ -294,6 +293,14 @@
         copy[path[0]] = update_in(copy.get(path[0], current_default), 
path[1:], update, default)
         return copy
 
+def has_path(coll, path):
+    """Checks if path exists in the given nested collection."""
+    for p in path:
+        try:
+            coll = coll[p]
+        except (KeyError, IndexError):
+            return False
+    return True
 
 def lwhere(mappings, **cond):
     """Selects mappings containing all pairs in cond."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/funcy/decorators.py 
new/funcy-1.16/funcy/decorators.py
--- old/funcy-1.14/funcy/decorators.py  2019-04-17 17:29:31.000000000 +0200
+++ new/funcy-1.16/funcy/decorators.py  2021-05-08 07:56:40.000000000 +0200
@@ -23,12 +23,18 @@
     """
     if has_single_arg(deco):
         return make_decorator(deco)
-    else:
+    elif has_1pos_and_kwonly(deco):
         # Any arguments after first become decorator arguments
         # And a decorator with arguments is essentialy a decorator fab
+        def decorator_fab(_func=None, **dkwargs):  # TODO: make _func pos only 
in Python 3
+            if _func is not None:
+                return make_decorator(deco, (), dkwargs)(_func)
+            return make_decorator(deco, (), dkwargs)
+    else:
         def decorator_fab(*dargs, **dkwargs):
             return make_decorator(deco, dargs, dkwargs)
-        return wraps(deco)(decorator_fab)
+
+    return wraps(deco)(decorator_fab)
 
 
 def make_decorator(deco, dargs=(), dkwargs={}):
@@ -73,14 +79,27 @@
     def has_single_arg(func):
         spec = inspect.getargspec(func)
         return len(spec.args) == 1 and not spec.varargs and not spec.keywords
+
+    def has_1pos_and_kwonly(func):
+        spec = inspect.getargspec(func)
+        return len(spec.args) == 1 and not spec.varargs
 else:
+    from collections import Counter
+    from inspect import Parameter as P
+
     def has_single_arg(func):
-        spec = inspect.signature(func)
-        if len(spec.parameters) != 1:
+        sig = inspect.signature(func)
+        if len(sig.parameters) != 1:
             return False
-        arg = next(iter(spec.parameters.values()))
+        arg = next(iter(sig.parameters.values()))
         return arg.kind not in (arg.VAR_POSITIONAL, arg.VAR_KEYWORD)
 
+    def has_1pos_and_kwonly(func):
+        sig = inspect.signature(func)
+        kinds = Counter(p.kind for p in sig.parameters.values())
+        return kinds[P.POSITIONAL_ONLY] + kinds[P.POSITIONAL_OR_KEYWORD] == 1 \
+            and kinds[P.VAR_POSITIONAL] == 0
+
 
 def get_argnames(func):
     func = getattr(func, '__original__', None) or unwrap(func)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/funcy/flow.py new/funcy-1.16/funcy/flow.py
--- old/funcy-1.14/funcy/flow.py        2019-11-28 19:29:12.000000000 +0100
+++ new/funcy-1.16/funcy/flow.py        2021-05-09 04:59:09.000000000 +0200
@@ -7,7 +7,7 @@
 
 
 __all__ = ['raiser', 'ignore', 'silent', 'suppress', 'nullcontext', 'reraise', 
'retry', 'fallback',
-           'limit_error_rate', 'ErrorRateExceeded',
+           'limit_error_rate', 'ErrorRateExceeded', 'throttle',
            'post_processing', 'collecting', 'joining',
            'once', 'once_per', 'once_per_args',
            'wrap_with']
@@ -18,6 +18,9 @@
 def raiser(exception_or_class=Exception, *args, **kwargs):
     """Constructs function that raises the given exception
        with given arguments on any invocation."""
+    if isinstance(exception_or_class, str):
+        exception_or_class = Exception(exception_or_class)
+
     def _raiser(*a, **kw):
         if args or kwargs:
             raise exception_or_class(*args, **kwargs)
@@ -108,6 +111,8 @@
     try:
         yield
     except errors as e:
+        if callable(into) and not _is_exception_type(into):
+            into = into(e)
         raise_from(into, e)
 
 
@@ -147,8 +152,11 @@
 def _ensure_exceptable(errors):
     """Ensures that errors are passable to except clause.
        I.e. should be BaseException subclass or a tuple."""
-    is_exception = isinstance(errors, type) and issubclass(errors, 
BaseException)
-    return errors if is_exception else tuple(errors)
+    return errors if _is_exception_type(errors) else tuple(errors)
+
+
+def _is_exception_type(value):
+    return isinstance(value, type) and issubclass(value, BaseException)
 
 
 class ErrorRateExceeded(Exception):
@@ -186,6 +194,28 @@
     return decorator
 
 
+def throttle(period):
+    """Allows only one run in a period, the rest is skipped"""
+    if isinstance(period, timedelta):
+        period = period.total_seconds()
+
+    def decorator(func):
+        func.blocked_until = None
+
+        @wraps(func)
+        def wrapper(*args, **kwargs):
+            now = time.time()
+            if func.blocked_until and func.blocked_until > now:
+                return
+            func.blocked_until = now + period
+
+            return func(*args, **kwargs)
+
+        return wrapper
+
+    return decorator
+
+
 ### Post processing decorators
 
 @decorator
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/funcy/funcs.py 
new/funcy-1.16/funcy/funcs.py
--- old/funcy-1.14/funcy/funcs.py       2018-10-03 14:49:11.000000000 +0200
+++ new/funcy-1.16/funcy/funcs.py       2021-05-08 11:08:25.000000000 +0200
@@ -2,7 +2,7 @@
 from functools import partial, reduce
 
 from .compat import map
-from ._inspect import get_spec
+from ._inspect import get_spec, Spec
 from .primitives import EMPTY
 from .funcmakers import make_func, make_pred
 
@@ -31,15 +31,16 @@
        Can be used to construct methods."""
     return lambda *a, **kw: func(*(args + a), **dict(kwargs, **kw))
 
-def rpartial(func, *args):
-    """Partially applies last arguments."""
-    return lambda *a: func(*(a + args))
+def rpartial(func, *args, **kwargs):
+    """Partially applies last arguments.
+       New keyworded arguments extend and override kwargs."""
+    return lambda *a, **kw: func(*(a + args), **dict(kwargs, **kw))
 
 
 def curry(func, n=EMPTY):
     """Curries func into a chain of one argument functions."""
     if n is EMPTY:
-        _, n, _ = get_spec(func)
+        n = get_spec(func).max_n
 
     if n <= 1:
         return func
@@ -53,7 +54,7 @@
     """Curries func into a chain of one argument functions.
        Arguments are passed from right to left."""
     if n is EMPTY:
-        _, n, _ = get_spec(func)
+        n = get_spec(func).max_n
 
     if n <= 1:
         return func
@@ -67,17 +68,18 @@
 def autocurry(func, n=EMPTY, _spec=None, _args=(), _kwargs={}):
     """Creates a version of func returning its partial applications
        until sufficient arguments are passed."""
-    spec = _spec or (get_spec(func) if n is EMPTY else (set(), n, n))
-    required_names, required_n, max_n = spec
+    spec = _spec or (get_spec(func) if n is EMPTY else Spec(n, set(), n, 
set(), False))
 
     def autocurried(*a, **kw):
         args = _args + a
         kwargs = _kwargs.copy()
         kwargs.update(kw)
 
-        if len(args) + len(kwargs) >= max_n:
+        if not spec.kw and len(args) + len(kwargs) >= spec.max_n:
             return func(*args, **kwargs)
-        elif len(args) + len(set(kwargs) & required_names) >= required_n:
+        elif len(args) + len(set(kwargs) & spec.names) >= spec.max_n:
+            return func(*args, **kwargs)
+        elif len(args) + len(set(kwargs) & spec.req_names) >= spec.req_n:
             try:
                 return func(*args, **kwargs)
             except TypeError:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/funcy/objects.py 
new/funcy-1.16/funcy/objects.py
--- old/funcy-1.14/funcy/objects.py     2019-11-28 14:14:52.000000000 +0100
+++ new/funcy-1.16/funcy/objects.py     2020-01-23 11:23:37.000000000 +0100
@@ -100,7 +100,7 @@
 class LazyObject(object):
     """
     A simplistic lazy init object.
-    Rewrites itself when any attribute is accesssed.
+    Rewrites itself when any attribute is accessed.
     """
     # NOTE: we can add lots of magic methods here to intercept on more events,
     #       this is postponed. As well as metaclass to support isinstance() 
check.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/funcy/seqs.py new/funcy-1.16/funcy/seqs.py
--- old/funcy-1.14/funcy/seqs.py        2018-10-03 14:49:11.000000000 +0200
+++ new/funcy-1.16/funcy/seqs.py        2020-03-06 05:42:42.000000000 +0100
@@ -109,22 +109,22 @@
 # TODO: tree-seq equivalent
 
 def lmap(f, *seqs):
-    """An extended version of builtin map().
+    """An extended version of builtin map() returning a list.
        Derives a mapper from string, int, slice, dict or set."""
     return _lmap(make_func(f, builtin=PY2), *seqs)
 
 def lfilter(pred, seq):
-    """An extended version of builtin filter().
+    """An extended version of builtin filter() returning a list.
        Derives a predicate from string, int, slice, dict or set."""
     return _lfilter(make_pred(pred, builtin=PY2), seq)
 
 def map(f, *seqs):
-    """An extended version of builtin imap().
+    """An extended version of builtin map().
        Derives a mapper from string, int, slice, dict or set."""
     return _map(make_func(f, builtin=PY2), *seqs)
 
 def filter(pred, seq):
-    """An extended version of builtin ifilter().
+    """An extended version of builtin filter().
        Derives a predicate from string, int, slice, dict or set."""
     return _filter(make_pred(pred, builtin=PY2), seq)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/funcy.egg-info/PKG-INFO 
new/funcy-1.16/funcy.egg-info/PKG-INFO
--- old/funcy-1.14/funcy.egg-info/PKG-INFO      2019-11-28 20:41:41.000000000 
+0100
+++ new/funcy-1.16/funcy.egg-info/PKG-INFO      2021-05-10 09:17:36.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: funcy
-Version: 1.14
+Version: 1.16
 Summary: A fancy and practical functional tools
 Home-page: http://github.com/Suor/funcy
 Author: Alexander Schepanovski
@@ -219,6 +219,7 @@
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
 Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/funcy.egg-info/SOURCES.txt 
new/funcy-1.16/funcy.egg-info/SOURCES.txt
--- old/funcy-1.14/funcy.egg-info/SOURCES.txt   2019-11-28 20:41:41.000000000 
+0100
+++ new/funcy-1.16/funcy.egg-info/SOURCES.txt   2021-05-10 09:17:36.000000000 
+0200
@@ -56,6 +56,7 @@
 tests/__pycache__/test_calc.cpython-37-PYTEST.pyc
 tests/__pycache__/test_calc.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_calc.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_calc.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_calc.pypy-41-PYTEST.pyc
 tests/__pycache__/test_colls.cpython-27-PYTEST.pyc
 tests/__pycache__/test_colls.cpython-35-PYTEST.pyc
@@ -64,6 +65,7 @@
 tests/__pycache__/test_colls.cpython-37-PYTEST.pyc
 tests/__pycache__/test_colls.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_colls.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_colls.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_colls.pypy-41-PYTEST.pyc
 tests/__pycache__/test_debug.cpython-27-PYTEST.pyc
 tests/__pycache__/test_debug.cpython-35-PYTEST.pyc
@@ -72,6 +74,7 @@
 tests/__pycache__/test_debug.cpython-37-PYTEST.pyc
 tests/__pycache__/test_debug.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_debug.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_debug.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_debug.pypy-41-PYTEST.pyc
 tests/__pycache__/test_decorators.cpython-27-PYTEST.pyc
 tests/__pycache__/test_decorators.cpython-35-PYTEST.pyc
@@ -80,6 +83,7 @@
 tests/__pycache__/test_decorators.cpython-37-PYTEST.pyc
 tests/__pycache__/test_decorators.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_decorators.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_decorators.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_decorators.pypy-41-PYTEST.pyc
 tests/__pycache__/test_flow.cpython-27-PYTEST.pyc
 tests/__pycache__/test_flow.cpython-35-PYTEST.pyc
@@ -88,6 +92,7 @@
 tests/__pycache__/test_flow.cpython-37-PYTEST.pyc
 tests/__pycache__/test_flow.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_flow.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_flow.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_flow.pypy-41-PYTEST.pyc
 tests/__pycache__/test_funcmakers.cpython-27-PYTEST.pyc
 tests/__pycache__/test_funcmakers.cpython-35-PYTEST.pyc
@@ -96,6 +101,7 @@
 tests/__pycache__/test_funcmakers.cpython-37-PYTEST.pyc
 tests/__pycache__/test_funcmakers.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_funcmakers.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_funcmakers.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_funcmakers.pypy-41-PYTEST.pyc
 tests/__pycache__/test_funcolls.cpython-27-PYTEST.pyc
 tests/__pycache__/test_funcolls.cpython-35-PYTEST.pyc
@@ -104,6 +110,7 @@
 tests/__pycache__/test_funcolls.cpython-37-PYTEST.pyc
 tests/__pycache__/test_funcolls.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_funcolls.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_funcolls.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_funcolls.pypy-41-PYTEST.pyc
 tests/__pycache__/test_funcs.cpython-27-PYTEST.pyc
 tests/__pycache__/test_funcs.cpython-35-PYTEST.pyc
@@ -112,6 +119,7 @@
 tests/__pycache__/test_funcs.cpython-37-PYTEST.pyc
 tests/__pycache__/test_funcs.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_funcs.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_funcs.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_funcs.pypy-41-PYTEST.pyc
 tests/__pycache__/test_interface.cpython-27-PYTEST.pyc
 tests/__pycache__/test_interface.cpython-35-PYTEST.pyc
@@ -120,6 +128,7 @@
 tests/__pycache__/test_interface.cpython-37-PYTEST.pyc
 tests/__pycache__/test_interface.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_interface.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_interface.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_interface.pypy-41-PYTEST.pyc
 tests/__pycache__/test_objects.cpython-27-PYTEST.pyc
 tests/__pycache__/test_objects.cpython-35-PYTEST.pyc
@@ -128,6 +137,7 @@
 tests/__pycache__/test_objects.cpython-37-PYTEST.pyc
 tests/__pycache__/test_objects.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_objects.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_objects.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_objects.pypy-41-PYTEST.pyc
 tests/__pycache__/test_seqs.cpython-27-PYTEST.pyc
 tests/__pycache__/test_seqs.cpython-35-PYTEST.pyc
@@ -136,6 +146,7 @@
 tests/__pycache__/test_seqs.cpython-37-PYTEST.pyc
 tests/__pycache__/test_seqs.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_seqs.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_seqs.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_seqs.pypy-41-PYTEST.pyc
 tests/__pycache__/test_strings.cpython-27-PYTEST.pyc
 tests/__pycache__/test_strings.cpython-35-PYTEST.pyc
@@ -144,6 +155,7 @@
 tests/__pycache__/test_strings.cpython-37-PYTEST.pyc
 tests/__pycache__/test_strings.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_strings.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_strings.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_strings.pypy-41-PYTEST.pyc
 tests/__pycache__/test_tree.cpython-27-PYTEST.pyc
 tests/__pycache__/test_tree.cpython-35-PYTEST.pyc
@@ -152,6 +164,7 @@
 tests/__pycache__/test_tree.cpython-37-PYTEST.pyc
 tests/__pycache__/test_tree.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_tree.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_tree.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_tree.pypy-41-PYTEST.pyc
 tests/__pycache__/test_types.cpython-27-PYTEST.pyc
 tests/__pycache__/test_types.cpython-35-PYTEST.pyc
@@ -160,4 +173,5 @@
 tests/__pycache__/test_types.cpython-37-PYTEST.pyc
 tests/__pycache__/test_types.cpython-37-pytest-5.0.1.pyc
 tests/__pycache__/test_types.cpython-38-pytest-5.3.1.pyc
+tests/__pycache__/test_types.cpython-38-pytest-5.4.3.pyc
 tests/__pycache__/test_types.pypy-41-PYTEST.pyc
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/setup.py new/funcy-1.16/setup.py
--- old/funcy-1.14/setup.py     2019-11-28 20:15:42.000000000 +0100
+++ new/funcy-1.16/setup.py     2021-05-07 13:22:59.000000000 +0200
@@ -34,6 +34,7 @@
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: Implementation :: CPython',
         'Programming Language :: Python :: Implementation :: PyPy',
 
Binary files old/funcy-1.14/tests/__pycache__/test_calc.cpython-27-PYTEST.pyc 
and new/funcy-1.16/tests/__pycache__/test_calc.cpython-27-PYTEST.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_calc.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_calc.cpython-38-pytest-5.4.3.pyc differ
Binary files old/funcy-1.14/tests/__pycache__/test_colls.cpython-27-PYTEST.pyc 
and new/funcy-1.16/tests/__pycache__/test_colls.cpython-27-PYTEST.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_colls.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_colls.cpython-38-pytest-5.4.3.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_debug.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_debug.cpython-38-pytest-5.4.3.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_decorators.cpython-27-PYTEST.pyc and 
new/funcy-1.16/tests/__pycache__/test_decorators.cpython-27-PYTEST.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_decorators.cpython-38-pytest-5.4.3.pyc 
and 
new/funcy-1.16/tests/__pycache__/test_decorators.cpython-38-pytest-5.4.3.pyc 
differ
Binary files old/funcy-1.14/tests/__pycache__/test_flow.cpython-27-PYTEST.pyc 
and new/funcy-1.16/tests/__pycache__/test_flow.cpython-27-PYTEST.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_flow.cpython-37-pytest-5.0.1.pyc and 
new/funcy-1.16/tests/__pycache__/test_flow.cpython-37-pytest-5.0.1.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_flow.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_flow.cpython-38-pytest-5.4.3.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_funcmakers.cpython-38-pytest-5.4.3.pyc 
and 
new/funcy-1.16/tests/__pycache__/test_funcmakers.cpython-38-pytest-5.4.3.pyc 
differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_funcolls.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_funcolls.cpython-38-pytest-5.4.3.pyc 
differ
Binary files old/funcy-1.14/tests/__pycache__/test_funcs.cpython-27-PYTEST.pyc 
and new/funcy-1.16/tests/__pycache__/test_funcs.cpython-27-PYTEST.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_funcs.cpython-37-pytest-5.0.1.pyc and 
new/funcy-1.16/tests/__pycache__/test_funcs.cpython-37-pytest-5.0.1.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_funcs.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_funcs.cpython-38-pytest-5.4.3.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_interface.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_interface.cpython-38-pytest-5.4.3.pyc 
differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_objects.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_objects.cpython-38-pytest-5.4.3.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_seqs.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_seqs.cpython-38-pytest-5.4.3.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_strings.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_strings.cpython-38-pytest-5.4.3.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_tree.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_tree.cpython-38-pytest-5.4.3.pyc differ
Binary files 
old/funcy-1.14/tests/__pycache__/test_types.cpython-38-pytest-5.4.3.pyc and 
new/funcy-1.16/tests/__pycache__/test_types.cpython-38-pytest-5.4.3.pyc differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/tests/test_calc.py 
new/funcy-1.16/tests/test_calc.py
--- old/funcy-1.14/tests/test_calc.py   2018-07-01 08:09:34.000000000 +0200
+++ new/funcy-1.16/tests/test_calc.py   2021-05-09 05:06:16.000000000 +0200
@@ -1,4 +1,5 @@
 from math import sin, cos
+from datetime import timedelta
 import pytest
 
 from funcy.calc import *
@@ -111,10 +112,12 @@
     assert function_table(cos)(-1) is None
 
 
-def test_cache():
+@pytest.mark.parametrize('typ',
+    [pytest.param(int, id='int'), pytest.param(lambda s: timedelta(seconds=s), 
id='timedelta')])
+def test_cache(typ):
     calls = []
 
-    @cache(timeout=60)
+    @cache(timeout=typ(60))
     def inc(x):
         calls.append(x)
         return x + 1
@@ -122,9 +125,7 @@
     assert inc(0) == 1
     assert inc(1) == 2
     assert inc(0) == 1
-    inc.invalidate(0)
-    assert inc(0) == 1
-    assert calls == [0, 1, 0]
+    assert calls == [0, 1]
 
 
 def test_cache_mixed_args():
@@ -144,5 +145,37 @@
         return x + 1
 
     assert inc(0) == 1
+    assert inc(1) == 2
     assert inc(0) == 1
-    assert calls == [0, 0]
+    assert calls == [0, 1, 0]
+    assert len(inc.memory) == 1  # Both call should be erased then one added
+
+
+def test_cache_invalidate():
+    calls = []
+
+    @cache(timeout=60)
+    def inc(x):
+        calls.append(x)
+        return x + 1
+
+    assert inc(0) == 1
+    assert inc(1) == 2
+    assert inc(0) == 1
+    assert calls == [0, 1]
+
+    inc.invalidate_all()
+    assert inc(0) == 1
+    assert inc(1) == 2
+    assert inc(0) == 1
+    assert calls == [0, 1, 0, 1]
+
+    inc.invalidate(1)
+    assert inc(0) == 1
+    assert inc(1) == 2
+    assert inc(0) == 1
+    assert calls == [0, 1, 0, 1, 1]
+
+    # ensure invalidate() is idempotent (doesn't raise KeyError on the 2nd 
call)
+    inc.invalidate(0)
+    inc.invalidate(0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/tests/test_colls.py 
new/funcy-1.16/tests/test_colls.py
--- old/funcy-1.14/tests/test_colls.py  2019-11-28 20:14:12.000000000 +0100
+++ new/funcy-1.16/tests/test_colls.py  2021-03-21 07:01:39.000000000 +0100
@@ -246,7 +246,6 @@
     assert get_in([1, 2], [3]) is None
     assert get_in({'x': [1, 2]}, ['x', 1]) == 2
 
-
 def test_set_in():
     d = {
         'a': {
@@ -281,6 +280,29 @@
     assert d2['a']['b'] == 1
     assert d2['c'] is d['c']
 
+def test_has_path():
+    d = {
+        "a": {
+            "b": "c",
+            "d": "e",
+            "f": {
+                "g": "h"
+            }
+        },
+        "i": "j"
+    }
+
+    assert has_path(d, [])
+    assert not has_path(d, ["m"])
+    assert not has_path(d, ["m", "n"])
+    assert has_path(d, ("i",))
+    assert has_path(d, ("a", "b"))
+    assert has_path(d, ["a", "f", "g"])
+
+def test_has_path_list():
+    assert has_path([1, 2], [0])
+    assert not has_path([1, 2], [3])
+    assert has_path({'x': [1, 2]}, ['x', 1])
 
 def test_where():
     data = [{'a': 1, 'b': 2}, {'a': 10, 'b': 2}]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/tests/test_decorators.py 
new/funcy-1.16/tests/test_decorators.py
--- old/funcy-1.14/tests/test_decorators.py     2019-03-17 05:36:21.000000000 
+0100
+++ new/funcy-1.16/tests/test_decorators.py     2021-05-08 07:58:29.000000000 
+0200
@@ -26,6 +26,20 @@
     assert ten() == 12
 
 
+def test_decorator_kw_only_args():
+    @decorator
+    def add(call, **kwargs):  # TODO: use real kw-only args in Python 3
+        return call() + kwargs.get("n", 1)
+
+    def ten():
+        return 10
+
+    # Should work with or without parentheses
+    assert add(n=2)(ten)() == 12
+    assert add()(ten)() == 11
+    assert add(ten)() == 11
+
+
 def test_decorator_access_arg():
     @decorator
     def multiply(call):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/tests/test_flow.py 
new/funcy-1.16/tests/test_flow.py
--- old/funcy-1.14/tests/test_flow.py   2019-11-28 19:56:27.000000000 +0100
+++ new/funcy-1.16/tests/test_flow.py   2021-05-09 05:03:27.000000000 +0200
@@ -1,3 +1,4 @@
+from datetime import timedelta
 import pytest
 from funcy.flow import *
 
@@ -29,11 +30,16 @@
     with pytest.raises(Exception) as e: raiser()()
     assert e.type is Exception
 
-    with pytest.raises(MyError): raiser(MyError)()
-    with pytest.raises(MyError) as e: raiser(MyError, 'some message')()
-    assert e.value.args == ('some message',)
+    with pytest.raises(Exception, match="text") as e: raiser("text")()
+    assert e.type is Exception
+
+    with pytest.raises(MyError):
+        raiser(MyError)()
+    with pytest.raises(MyError, match="some message"):
+        raiser(MyError('some message'))()
+    with pytest.raises(MyError, match="some message") as e:
+        raiser(MyError, 'some message')()
 
-    with pytest.raises(MyError): raiser(MyError('some message'))()
     with pytest.raises(MyError): raiser(MyError)('junk', keyword='junk')
 
 
@@ -67,6 +73,10 @@
         with reraise(ValueError, MyError):
             raise TypeError
 
+    with pytest.raises(MyError, match="heyhey"):
+        with reraise(ValueError, lambda e: MyError(str(e) * 2)):
+            raise ValueError("hey")
+
 
 def test_retry():
     with pytest.raises(MyError):
@@ -143,6 +153,25 @@
     assert calls == [1, 2]
 
 
+@pytest.mark.parametrize('typ',
+    [pytest.param(int, id='int'), pytest.param(lambda s: timedelta(seconds=s), 
id='timedelta')])
+def test_throttle(monkeypatch, typ):
+    timestamps = iter([0, 0.01, 1, 1.000025])
+    monkeypatch.setattr('time.time', lambda: next(timestamps))
+
+    calls = []
+
+    @throttle(typ(1))
+    def throttled(x):
+        calls.append(x)
+
+    throttled(1)
+    throttled(2)
+    throttled(3)
+    throttled(4)
+    assert calls == [1, 3]
+
+
 def test_post_processing():
     @post_processing(max)
     def my_max(l):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/funcy-1.14/tests/test_funcs.py 
new/funcy-1.16/tests/test_funcs.py
--- old/funcy-1.14/tests/test_funcs.py  2018-10-03 14:49:11.000000000 +0200
+++ new/funcy-1.16/tests/test_funcs.py  2021-05-08 10:33:58.000000000 +0200
@@ -32,6 +32,9 @@
     assert rpartial(__sub__, 10)(1) == -9
     assert rpartial(pow, 2, 85)(10) == 15
 
+    merge = lambda a, b, c='bra': a + b + c
+    assert rpartial(merge, a='abra')(b='cada') == 'abracadabra'
+    assert rpartial(merge, 'cada', c='fancy')('abra', c='funcy') == 
'abracadafuncy'
 
 def test_curry():
     assert curry(lambda: 42)() == 42
@@ -73,6 +76,22 @@
     assert at(1)(2, 3) == (1, 2, 3)
     assert at(a=1)(b=2) == (1, 2, 9)
     assert at(c=3)(1)(2) == (1, 2, 3)
+    assert at(c=3, a=1, b=2) == (1, 2, 3)
+
+    with pytest.raises(TypeError): at(b=2, c=9, d=42)(1)
+
+def test_autocurry_kwargs():
+    at = autocurry(lambda a, b, **kw: (a, b, kw))
+    assert at(1, 2) == (1, 2, {})
+    assert at(1)(c=9)(2) == (1, 2, {'c': 9})
+    assert at(c=9, d=5)(e=7)(1, 2) == (1, 2, {'c': 9, 'd': 5, 'e': 7})
+
+    at = autocurry(lambda a, b=2, c=3: (a, b, c))
+    assert at(1) == (1, 2, 3)
+    assert at(a=1) == (1, 2, 3)
+    assert at(c=9)(1) == (1, 2, 9)
+    assert at(b=3, c=9)(1) == (1, 3, 9)
+    with pytest.raises(TypeError): at(b=2, d=3, e=4)(a=1, c=1)
 
 def test_autocurry_builtin():
     assert autocurry(complex)(imag=1)(0) == 1j

Reply via email to