Script 'mail_helper' called by obssrc
Hello community,

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

Package is "python-dogpile.cache"

Mon Sep  6 15:57:59 2021 rev:32 rq:916952 version:1.1.4

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-dogpile.cache/python-dogpile.cache.changes    
    2021-06-25 15:02:13.016215909 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-dogpile.cache.new.1899/python-dogpile.cache.changes
      2021-09-06 15:58:12.589292715 +0200
@@ -1,0 +2,19 @@
+Mon Sep  6 04:29:32 UTC 2021 - Steve Kowalik <steven.kowa...@suse.com>
+
+- Update to 1.1.4:
+  * [usecase] [memcached] Added support for pymemcache socket keepalive
+    and retrying client.
+  * [bug] [general] Fixed Python 3.10 deprecation warning involving
+    threading. Pull request
+  * [bug] [regression] [tests] Repaired the test suite to work with the
+    5.x series of the decorator module, which now appears to make use of
+    the __signature__ attribute.
+  * [bug] [regression] Fixed regression where ProxyBackend was missing
+    several methods that were added as part of the 1.1 release.
+  * [feature] [region] Added new region method CacheRegion.key_is_locked().
+    Returns True if the given key is subject to the dogpile lock, which
+    would indicate that the generator function is running at that time.
+  * [feature] [memcached] Added support for the pymemcache backend, using
+    the "dogpile.cache.pymemcache" backend identifier.
+
+-------------------------------------------------------------------

Old:
----
  dogpile.cache-1.1.3.tar.gz

New:
----
  dogpile.cache-1.1.4.tar.gz

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

Other differences:
------------------
++++++ python-dogpile.cache.spec ++++++
--- /var/tmp/diff_new_pack.9poagK/_old  2021-09-06 15:58:13.121292592 +0200
+++ /var/tmp/diff_new_pack.9poagK/_new  2021-09-06 15:58:13.125292592 +0200
@@ -19,13 +19,12 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %global pythons python3
 Name:           python-dogpile.cache
-Version:        1.1.3
+Version:        1.1.4
 Release:        0
 %define modname dogpile.cache
-%define modver  1_1_1
+%define modver  1_1_4
 Summary:        A caching front-end based on the Dogpile lock
 License:        BSD-3-Clause
-Group:          Development/Languages/Python
 URL:            https://github.com/sqlalchemy/dogpile.cache
 Source:         
https://github.com/sqlalchemy/%{modname}/archive/rel_%{modver}.tar.gz#/%{modname}-%{version}.tar.gz
 BuildRequires:  %{python_module Mako}
@@ -67,6 +66,6 @@
 %license LICENSE
 %doc README.rst
 %{python_sitelib}/dogpile
-%{python_sitelib}/dogpile.cache-1.1.1-py%{python_version}.egg-info
+%{python_sitelib}/dogpile.cache-%{version}-py%{python_version}.egg-info
 
 %changelog

++++++ dogpile.cache-1.1.3.tar.gz -> dogpile.cache-1.1.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/.pre-commit-config.yaml 
new/dogpile.cache-rel_1_1_4/.pre-commit-config.yaml
--- old/dogpile.cache-rel_1_1_1/.pre-commit-config.yaml 2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/.pre-commit-config.yaml 2021-09-02 
16:42:13.000000000 +0200
@@ -2,24 +2,26 @@
 # See https://pre-commit.com/hooks.html for more hooks
 repos:
 -   repo: https://github.com/python/black
-    rev: 20.8b1
+    rev: 21.5b1
     hooks:
     -   id: black
 
 -   repo: https://github.com/sqlalchemyorg/zimports
-    rev: master
+    rev: v0.4.1
     hooks:
     -   id: zimports
+        args:
+            - --keep-unused-type-checking
 
 -   repo: https://github.com/pycqa/flake8
-    rev: master
+    rev: 3.9.2
     hooks:
     -   id: flake8
         additional_dependencies:
           - flake8-import-order
           - flake8-builtins
-          - flake8-docstrings
+          - flake8-docstrings>=1.3.1
           - flake8-rst-docstrings
-          - pydocstyle<4.0.0
+          - pydocstyle
           - pygments
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/LICENSE 
new/dogpile.cache-rel_1_1_4/LICENSE
--- old/dogpile.cache-rel_1_1_1/LICENSE 2020-11-23 14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/LICENSE 2021-09-02 16:42:13.000000000 +0200
@@ -1,4 +1,4 @@
-Copyright 2005-2020 Michael Bayer.
+Copyright 2005-2021 Michael Bayer.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/docs/build/changelog.rst 
new/dogpile.cache-rel_1_1_4/docs/build/changelog.rst
--- old/dogpile.cache-rel_1_1_1/docs/build/changelog.rst        2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/docs/build/changelog.rst        2021-09-02 
16:42:13.000000000 +0200
@@ -3,6 +3,70 @@
 =========
 
 .. changelog::
+    :version: 1.1.4
+    :released: Thu Sep 2 2021
+
+    .. change::
+        :tags: bug, general
+        :tickets: 203
+
+        Fixed Python 3.10 deprecation warning involving threading. Pull request
+        courtesy Karthikeyan Singaravelan.
+
+    .. change::
+        :tags: usecase, memcached
+
+        Added support for pymemcache socket keepalive and retrying client.
+
+        .. seealso::
+
+            :paramref:`.PyMemcacheBackend.socket_keepalive`
+
+            :paramref:`.PyMemcacheBackend.enable_retry_client`
+
+.. changelog::
+    :version: 1.1.3
+    :released: Thu May 20 2021
+
+    .. change::
+        :tags: bug, regression, tests
+
+        Repaired the test suite to work with the 5.x series of the 
``decorator``
+        module, which now appears to make use of the ``__signature__`` 
attribute.
+
+    .. change::
+        :tags: bug, regression
+        :tickets: 202
+
+        Fixed regression where :class:`.ProxyBackend` was missing several 
methods
+        that were added as part of the 1.1 release.
+
+.. changelog::
+    :version: 1.1.2
+    :released: Tue Jan 26 2021
+
+    .. change::
+        :tags: feature, region
+        :tickets: 101
+
+        Added new region method :meth:`.CacheRegion.key_is_locked`. Returns 
True if
+        the given key is subject to the dogpile lock, which would indicate 
that the
+        generator function is running at that time. Pull request courtesy 
Bastien
+        Gerard.
+
+    .. change::
+        :tags: feature, memcached
+        :tickets: 134
+
+        Added support for the pymemcache backend, using the
+        ``"dogpile.cache.pymemcache"`` backend identifier. Pull request 
courtesy
+        Mois??s Guimar??es de Medeiros.
+
+        .. seealso::
+
+          :class:`.PyMemcacheBackend`
+
+.. changelog::
     :version: 1.1.1
     :released: Mon Nov 23 2020
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/docs/build/conf.py 
new/dogpile.cache-rel_1_1_4/docs/build/conf.py
--- old/dogpile.cache-rel_1_1_1/docs/build/conf.py      2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/docs/build/conf.py      2021-09-02 
16:42:13.000000000 +0200
@@ -39,7 +39,7 @@
     "sphinx_paramlinks",
 ]
 
-changelog_sections = ["feature", "bug"]
+changelog_sections = ["feature", "usecase", "bug"]
 
 changelog_render_ticket = (
     "https://github.com/sqlalchemy/dogpile.cache/issues/%s";
@@ -65,7 +65,7 @@
 
 # General information about the project.
 project = "dogpile.cache"
-copyright = "2011-2020 Mike Bayer"
+copyright = "2011-2021 Mike Bayer"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -74,7 +74,7 @@
 # The short X.Y version.
 version = dogpile.__version__
 # The full version, including alpha/beta/rc tags.
-release = "1.1.1"
+release = "1.1.4"
 
 
 # The language for content autogenerated by Sphinx. Refer to documentation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/dogpile/__init__.py 
new/dogpile.cache-rel_1_1_4/dogpile/__init__.py
--- old/dogpile.cache-rel_1_1_1/dogpile/__init__.py     2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/dogpile/__init__.py     2021-09-02 
16:42:13.000000000 +0200
@@ -1,4 +1,4 @@
-__version__ = "1.1.1"
+__version__ = "1.1.4"
 
 from .lock import Lock  # noqa
 from .lock import NeedRegenerationException  # noqa
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/dogpile/cache/api.py 
new/dogpile.cache-rel_1_1_4/dogpile/cache/api.py
--- old/dogpile.cache-rel_1_1_1/dogpile/cache/api.py    2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/dogpile/cache/api.py    2021-09-02 
16:42:13.000000000 +0200
@@ -85,6 +85,17 @@
 
         raise NotImplementedError()
 
+    @abc.abstractmethod
+    def locked(self) -> bool:
+        """Check if the mutex was acquired.
+
+        :return: true if the lock is acquired.
+
+        .. versionadded:: 1.1.2
+
+        """
+        raise NotImplementedError()
+
     @classmethod
     def __subclasshook__(cls, C):
         return hasattr(C, "acquire") and hasattr(C, "release")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/dogpile.cache-rel_1_1_1/dogpile/cache/backends/__init__.py 
new/dogpile.cache-rel_1_1_4/dogpile/cache/backends/__init__.py
--- old/dogpile.cache-rel_1_1_1/dogpile/cache/backends/__init__.py      
2020-11-23 14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/dogpile/cache/backends/__init__.py      
2021-09-02 16:42:13.000000000 +0200
@@ -25,6 +25,11 @@
     "MemcachedBackend",
 )
 register_backend(
+    "dogpile.cache.pymemcache",
+    "dogpile.cache.backends.memcached",
+    "PyMemcacheBackend",
+)
+register_backend(
     "dogpile.cache.memory", "dogpile.cache.backends.memory", "MemoryBackend"
 )
 register_backend(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/dogpile.cache-rel_1_1_1/dogpile/cache/backends/memcached.py 
new/dogpile.cache-rel_1_1_4/dogpile/cache/backends/memcached.py
--- old/dogpile.cache-rel_1_1_1/dogpile/cache/backends/memcached.py     
2020-11-23 14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/dogpile/cache/backends/memcached.py     
2021-09-02 16:42:13.000000000 +0200
@@ -12,25 +12,30 @@
 import typing
 from typing import Any
 from typing import Mapping
+import warnings
 
 from ..api import CacheBackend
 from ..api import NO_VALUE
 from ... import util
 
+
 if typing.TYPE_CHECKING:
+    import bmemcached
     import memcache
     import pylibmc
-    import bmemcached
+    import pymemcache
 else:
     # delayed import
-    memcache = None
-    pylibmc = None
-    bmemcached = None
+    bmemcached = None  # noqa F811
+    memcache = None  # noqa F811
+    pylibmc = None  # noqa F811
+    pymemcache = None  # noqa F811
 
 __all__ = (
     "GenericMemcachedBackend",
     "MemcachedBackend",
     "PylibmcBackend",
+    "PyMemcacheBackend",
     "BMemcachedBackend",
     "MemcachedLock",
 )
@@ -58,6 +63,10 @@
             if i < 15:
                 i += 1
 
+    def locked(self):
+        client = self.client_fn()
+        return client.get(self.key) is not None
+
     def release(self):
         client = self.client_fn()
         client.delete(self.key)
@@ -188,7 +197,11 @@
 
     def get_multi(self, keys):
         values = self.client.get_multi(keys)
-        return [NO_VALUE if key not in values else values[key] for key in keys]
+
+        return [
+            NO_VALUE if val is None else val
+            for val in [values.get(key, NO_VALUE) for key in keys]
+        ]
 
     def set(self, key, value):
         self.client.set(key, value, **self.set_arguments)
@@ -223,9 +236,6 @@
         super(MemcacheArgs, self).__init__(arguments)
 
 
-pylibmc = None
-
-
 class PylibmcBackend(MemcacheArgs, GenericMemcachedBackend):
     """A backend for the
     `pylibmc <http://sendapatch.se/projects/pylibmc/index.html>`_
@@ -275,9 +285,6 @@
         )
 
 
-memcache = None
-
-
 class MemcachedBackend(MemcacheArgs, GenericMemcachedBackend):
     """A backend using the standard
     `Python-memcached <http://www.tummy.com/Community/software/\
@@ -306,9 +313,6 @@
         return memcache.Client(self.url)
 
 
-bmemcached = None
-
-
 class BMemcachedBackend(GenericMemcachedBackend):
     """A backend for the
     `python-binary-memcached <https://github.com/jaysonsantos/\
@@ -321,16 +325,6 @@
     SASL is a standard for adding authentication mechanisms
     to protocols in a way that is protocol independent.
 
-    SSL/TLS is a security layer on end-to-end communication.
-    It provides following benefits:
-
-    * Encryption: Data is encrypted on the wire between
-      Memcached client and server.
-    * Authentication: Optionally, both server and client
-      authenticate each other.
-    * Integrity: Data is not tampered or altered when
-      transmitted between client and server
-
     A typical configuration using username/password::
 
         from dogpile.cache import make_region
@@ -417,3 +411,179 @@
         """python-binary-memcached api does not implements delete_multi"""
         for key in keys:
             self.delete(key)
+
+
+class PyMemcacheBackend(GenericMemcachedBackend):
+    """A backend for the
+    `pymemcache <https://github.com/pinterest/pymemcache>`_
+    memcached client.
+
+    A comprehensive, fast, pure Python memcached client
+
+    .. versionadded:: 1.1.2
+
+    pymemcache supports the following features:
+
+    * Complete implementation of the memcached text protocol.
+    * Configurable timeouts for socket connect and send/recv calls.
+    * Access to the "noreply" flag, which can significantly increase
+      the speed of writes.
+    * Flexible, simple approach to serialization and deserialization.
+    * The (optional) ability to treat network and memcached errors as
+      cache misses.
+
+    dogpile.cache uses the ``HashClient`` from pymemcache in order to reduce
+    API differences when compared to other memcached client drivers.
+    This allows the user to provide a single server or a list of memcached
+    servers.
+
+    Arguments which can be passed to the ``arguments``
+    dictionary include:
+
+    :param tls_context: optional TLS context, will be used for
+     TLS connections.
+
+     A typical configuration using tls_context::
+
+        import ssl
+        from dogpile.cache import make_region
+
+        ctx = ssl.create_default_context(cafile="/path/to/my-ca.pem")
+
+        region = make_region().configure(
+            'dogpile.cache.pymemcache',
+            expiration_time = 3600,
+            arguments = {
+                'url':["127.0.0.1"],
+                'tls_context':ctx,
+            }
+        )
+
+     .. seealso::
+
+        `<https://docs.python.org/3/library/ssl.html>`_ - additional TLS
+        documentation.
+
+    :param serde: optional "serde". Defaults to
+     ``pymemcache.serde.pickle_serde``.
+
+    :param default_noreply:  defaults to False.  When set to True this flag
+     enables the pymemcache "noreply" feature.  See the pymemcache
+     documentation for further details.
+
+    :param socket_keepalive: optional socket keepalive, will be used for
+     TCP keepalive configuration.  Use of this parameter requires pymemcache
+     3.5.0 or greater.  This parameter
+     accepts a
+     `pymemcache.client.base.KeepAliveOpts
+     
<https://pymemcache.readthedocs.io/en/latest/apidoc/pymemcache.client.base.html#pymemcache.client.base.KeepaliveOpts>`_
+     object.
+
+     A typical configuration using ``socket_keepalive``::
+
+        from pymemcache import KeepaliveOpts
+        from dogpile.cache import make_region
+
+        # Using the default keepalive configuration
+        socket_keepalive = KeepaliveOpts()
+
+        region = make_region().configure(
+            'dogpile.cache.pymemcache',
+            expiration_time = 3600,
+            arguments = {
+                'url':["127.0.0.1"],
+                'socket_keepalive': socket_keepalive
+            }
+        )
+
+     .. versionadded:: 1.1.4 - added support for ``socket_keepalive``.
+
+    :param enable_retry_client: optional flag to enable retry client
+     mechanisms to handle failure.  Defaults to False.  When set to ``True``,
+     the :paramref:`.PyMemcacheBackend.retry_attempts` parameter must also
+     be set, along with optional parameters
+     :paramref:`.PyMemcacheBackend.retry_delay`.
+     :paramref:`.PyMemcacheBackend.retry_for`,
+     :paramref:`.PyMemcacheBackend.do_not_retry_for`.
+
+     .. seealso::
+
+        
`<https://pymemcache.readthedocs.io/en/latest/getting_started.html#using-the-built-in-retrying-mechanism>`_
 -
+        in the pymemcache documentation
+
+     .. versionadded:: 1.1.4
+
+    :param retry_attempts: how many times to attempt an action before
+     failing. Must be 1 or above. Defaults to None.
+
+     .. versionadded:: 1.1.4
+
+    :param retry_delay: optional int|float, how many seconds to sleep between
+     each attempt. Defaults to None.
+
+     .. versionadded:: 1.1.4
+
+    :param retry_for: optional None|tuple|set|list, what exceptions to
+     allow retries for. Will allow retries for all exceptions if None.
+     Example: ``(MemcacheClientError, MemcacheUnexpectedCloseError)``
+     Accepts any class that is a subclass of Exception.  Defaults to None.
+
+     .. versionadded:: 1.1.4
+
+    :param do_not_retry_for: optional None|tuple|set|list, what
+     exceptions should be retried. Will not block retries for any Exception if
+     None. Example: ``(IOError, MemcacheIllegalInputError)``
+     Accepts any class that is a subclass of Exception. Defaults to None.
+
+     .. versionadded:: 1.1.4
+
+    """  # noqa E501
+
+    def __init__(self, arguments):
+        super().__init__(arguments)
+
+        self.serde = arguments.get("serde", pymemcache.serde.pickle_serde)
+        self.default_noreply = arguments.get("default_noreply", False)
+        self.tls_context = arguments.get("tls_context", None)
+        self.socket_keepalive = arguments.get("socket_keepalive", None)
+        self.enable_retry_client = arguments.get("enable_retry_client", False)
+        self.retry_attempts = arguments.get("retry_attempts", None)
+        self.retry_delay = arguments.get("retry_delay", None)
+        self.retry_for = arguments.get("retry_for", None)
+        self.do_not_retry_for = arguments.get("do_not_retry_for", None)
+        if (
+            self.retry_delay is not None
+            or self.retry_attempts is not None
+            or self.retry_for is not None
+            or self.do_not_retry_for is not None
+        ) and not self.enable_retry_client:
+            warnings.warn(
+                "enable_retry_client is not set; retry options "
+                "will be ignored"
+            )
+
+    def _imports(self):
+        global pymemcache
+        import pymemcache
+
+    def _create_client(self):
+        _kwargs = {
+            "serde": self.serde,
+            "default_noreply": self.default_noreply,
+            "tls_context": self.tls_context,
+        }
+        if self.socket_keepalive is not None:
+            _kwargs.update({"socket_keepalive": self.socket_keepalive})
+
+        client = pymemcache.client.hash.HashClient(self.url, **_kwargs)
+
+        if self.enable_retry_client:
+            return pymemcache.client.retrying.RetryingClient(
+                client,
+                attempts=self.retry_attempts,
+                retry_delay=self.retry_delay,
+                retry_for=self.retry_for,
+                do_not_retry_for=self.do_not_retry_for,
+            )
+
+        return client
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/dogpile.cache-rel_1_1_1/dogpile/cache/backends/null.py 
new/dogpile.cache-rel_1_1_4/dogpile/cache/backends/null.py
--- old/dogpile.cache-rel_1_1_1/dogpile/cache/backends/null.py  2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/dogpile/cache/backends/null.py  2021-09-02 
16:42:13.000000000 +0200
@@ -24,6 +24,9 @@
     def release(self):
         pass
 
+    def locked(self):
+        return False
+
 
 class NullBackend(CacheBackend):
     """A "null" backend that effectively disables all cache operations.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/dogpile.cache-rel_1_1_1/dogpile/cache/backends/redis.py 
new/dogpile.cache-rel_1_1_4/dogpile/cache/backends/redis.py
--- old/dogpile.cache-rel_1_1_1/dogpile/cache/backends/redis.py 2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/dogpile/cache/backends/redis.py 2021-09-02 
16:42:13.000000000 +0200
@@ -18,7 +18,7 @@
     import redis
 else:
     # delayed import
-    redis = None
+    redis = None  # noqa F811
 
 __all__ = ("RedisBackend", "RedisSentinelBackend")
 
@@ -173,11 +173,7 @@
 
     def set_serialized(self, key, value):
         if self.redis_expiration_time:
-            self.writer_client.setex(
-                key,
-                self.redis_expiration_time,
-                value,
-            )
+            self.writer_client.setex(key, self.redis_expiration_time, value)
         else:
             self.writer_client.set(key, value)
 
@@ -283,7 +279,7 @@
                 "distributed_lock": True,
                 "thread_local_lock": False,
                 **arguments,
-            },
+            }
         )
 
     def _imports(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/dogpile/cache/proxy.py 
new/dogpile.cache-rel_1_1_4/dogpile/cache/proxy.py
--- old/dogpile.cache-rel_1_1_1/dogpile/cache/proxy.py  2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/dogpile/cache/proxy.py  2021-09-02 
16:42:13.000000000 +0200
@@ -19,6 +19,7 @@
 from .api import CacheBackend
 from .api import CacheMutex
 from .api import KeyType
+from .api import SerializedReturnType
 
 
 class ProxyBackend(CacheBackend):
@@ -101,3 +102,17 @@
 
     def get_mutex(self, key: KeyType) -> Optional[CacheMutex]:
         return self.proxied.get_mutex(key)
+
+    def get_serialized(self, key: KeyType) -> SerializedReturnType:
+        return self.proxied.get_serialized(key)
+
+    def get_serialized_multi(
+        self, keys: Sequence[KeyType]
+    ) -> Sequence[SerializedReturnType]:
+        return self.proxied.get_serialized_multi(keys)
+
+    def set_serialized(self, key: KeyType, value: bytes) -> None:
+        self.proxied.set_serialized(key, value)
+
+    def set_serialized_multi(self, mapping: Mapping[KeyType, bytes]) -> None:
+        self.proxied.set_serialized_multi(mapping)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/dogpile/cache/region.py 
new/dogpile.cache-rel_1_1_4/dogpile/cache/region.py
--- old/dogpile.cache-rel_1_1_1/dogpile/cache/region.py 2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/dogpile/cache/region.py 2021-09-02 
16:42:13.000000000 +0200
@@ -554,6 +554,9 @@
         def release(self):
             self.lock.release()
 
+        def locked(self):
+            return self.lock.locked()
+
     def _create_mutex(self, key):
         mutex = self.backend.get_mutex(key)
         if mutex is not None:
@@ -865,6 +868,17 @@
 
         return True
 
+    def key_is_locked(self, key: KeyType) -> bool:
+        """Return True if a particular cache key is currently being generated
+        within the dogpile lock.
+
+        .. versionadded:: 1.1.2
+
+        """
+        mutex = self._mutex(key)
+        locked: bool = mutex.locked()
+        return locked
+
     def get_or_create(
         self,
         key: KeyType,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/dogpile/util/compat.py 
new/dogpile.cache-rel_1_1_4/dogpile/util/compat.py
--- old/dogpile.cache-rel_1_1_1/dogpile/util/compat.py  2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/dogpile/util/compat.py  2021-09-02 
16:42:13.000000000 +0200
@@ -28,6 +28,11 @@
 
     """
 
+    # if a Signature is already present, as is the case with newer
+    # "decorator" package, defer back to built in
+    if hasattr(func, "__signature__"):
+        return inspect.getfullargspec(func)
+
     if inspect.ismethod(func):
         func = func.__func__
     if not inspect.isfunction(func):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/dogpile/util/langhelpers.py 
new/dogpile.cache-rel_1_1_4/dogpile/util/langhelpers.py
--- old/dogpile.cache-rel_1_1_1/dogpile/util/langhelpers.py     2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/dogpile/util/langhelpers.py     2021-09-02 
16:42:13.000000000 +0200
@@ -149,3 +149,10 @@
             # the thread ident and unlock.
             del self.keys[current_thread]
             self.mutex.release()
+
+    def locked(self):
+        current_thread = threading.get_ident()
+        keys = self.keys.get(current_thread)
+        if keys is None:
+            return False
+        return self.key in keys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/dogpile.cache-rel_1_1_1/dogpile/util/readwrite_lock.py 
new/dogpile.cache-rel_1_1_4/dogpile/util/readwrite_lock.py
--- old/dogpile.cache-rel_1_1_1/dogpile/util/readwrite_lock.py  2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/dogpile/util/readwrite_lock.py  2021-09-02 
16:42:13.000000000 +0200
@@ -62,10 +62,10 @@
             # check if we are the last asynchronous reader thread
             # out the door.
             if self.async_ == 0:
-                # yes. so if a sync operation is waiting, notifyAll to wake
+                # yes. so if a sync operation is waiting, notify_all to wake
                 # it up
                 if self.current_sync_operation is not None:
-                    self.condition.notifyAll()
+                    self.condition.notify_all()
             elif self.async_ < 0:
                 raise LockError(
                     "Synchronizer error - too many "
@@ -95,7 +95,7 @@
             # establish ourselves as the current sync
             # this indicates to other read/write operations
             # that they should wait until this is None again
-            self.current_sync_operation = threading.currentThread()
+            self.current_sync_operation = threading.current_thread()
 
             # now wait again for asyncs to finish
             if self.async_ > 0:
@@ -117,7 +117,7 @@
         """Release the 'write' lock."""
         self.condition.acquire()
         try:
-            if self.current_sync_operation is not threading.currentThread():
+            if self.current_sync_operation is not threading.current_thread():
                 raise LockError(
                     "Synchronizer error - current thread doesn't "
                     "have the write lock"
@@ -128,7 +128,7 @@
             self.current_sync_operation = None
 
             # tell everyone to get ready
-            self.condition.notifyAll()
+            self.condition.notify_all()
 
             log.debug("%s released write lock", self)
         finally:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/tests/cache/_fixtures.py 
new/dogpile.cache-rel_1_1_4/tests/cache/_fixtures.py
--- old/dogpile.cache-rel_1_1_1/tests/cache/_fixtures.py        2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/tests/cache/_fixtures.py        2021-09-02 
16:42:13.000000000 +0200
@@ -6,6 +6,7 @@
 from threading import Thread
 import time
 from unittest import TestCase
+import uuid
 
 import pytest
 
@@ -109,6 +110,21 @@
         backend.delete("some_key")
         eq_(backend.get_serialized("some_key"), NO_VALUE)
 
+    def test_region_is_key_locked(self):
+        reg = self._region()
+        random_key = str(uuid.uuid1())
+        assert not reg.get(random_key)
+        eq_(reg.key_is_locked(random_key), False)
+        # ensures that calling key_is_locked doesn't acquire the lock
+        eq_(reg.key_is_locked(random_key), False)
+
+        mutex = reg.backend.get_mutex(random_key)
+        if mutex:
+            mutex.acquire()
+            eq_(reg.key_is_locked(random_key), True)
+            mutex.release()
+            eq_(reg.key_is_locked(random_key), False)
+
     def test_region_set_get_value(self):
         reg = self._region()
         reg.set("some key", "some value")
@@ -229,7 +245,17 @@
 
     @pytest.mark.time_intensive
     def test_threaded_get_multi(self):
+        """This test is testing that when we get inside the "creator" for
+        a certain key, there are no other "creators" running at all for
+        that key.
+
+        With "distributed" locks, this is not 100% the case.
+
+        """
         reg = self._region(config_args={"expiration_time": 0.25})
+        backend_mutex = reg.backend.get_mutex("some_key")
+        is_custom_mutex = backend_mutex is not None
+
         locks = dict((str(i), Lock()) for i in range(11))
 
         canary = collections.defaultdict(list)
@@ -274,8 +300,12 @@
             t.join()
 
         assert sum([len(v) for v in canary.values()]) > 10
-        for l in canary.values():
-            assert False not in l
+
+        # for non-custom mutex, check that we never had two creators
+        # running at once
+        if not is_custom_mutex:
+            for l in canary.values():
+                assert False not in l
 
     def test_region_delete(self):
         reg = self._region()
@@ -293,19 +323,24 @@
         # with very slow processing missing a timeout, as is often the
         # case with this particular test
 
-        reg = self._region(config_args={"expiration_time": 0.75})
+        expire_time = 1.00
+
+        reg = self._region(config_args={"expiration_time": expire_time})
         counter = itertools.count(1)
 
         def creator():
             return "some value %d" % next(counter)
 
         eq_(reg.get_or_create("some key", creator), "some value 1")
-        time.sleep(0.85)
+        time.sleep(expire_time + (0.2 * expire_time))
         # expiration is definitely hit
-        eq_(reg.get("some key", ignore_expiration=True), "some value 1")
+        post_expiration = reg.get("some key", ignore_expiration=True)
+        if post_expiration is not NO_VALUE:
+            eq_(post_expiration, "some value 1")
+
         eq_(reg.get_or_create("some key", creator), "some value 2")
 
-        # this line needs to run less the .75 sec before the previous
+        # this line needs to run less the expire_time sec before the previous
         # two or it hits the expiration
         eq_(reg.get("some key"), "some value 2")
 
@@ -387,11 +422,15 @@
         backend = self._backend()
         mutex = backend.get_mutex("foo")
 
+        assert not mutex.locked()
         ac = mutex.acquire()
         assert ac
         ac2 = mutex.acquire(False)
+        assert mutex.locked()
         assert not ac2
         mutex.release()
+        assert not mutex.locked()
+
         ac3 = mutex.acquire()
         assert ac3
         mutex.release()
@@ -470,6 +509,9 @@
     def release(self):
         return
 
+    def locked(self):
+        return False
+
 
 class MockBackend(CacheBackend):
     def __init__(self, arguments):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/dogpile.cache-rel_1_1_1/tests/cache/test_dbm_backend.py 
new/dogpile.cache-rel_1_1_4/tests/cache/test_dbm_backend.py
--- old/dogpile.cache-rel_1_1_1/tests/cache/test_dbm_backend.py 2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/tests/cache/test_dbm_backend.py 2021-09-02 
16:42:13.000000000 +0200
@@ -2,6 +2,7 @@
 import sys
 
 from dogpile.cache.backends.file import AbstractFileLock
+from dogpile.cache.proxy import ProxyBackend
 from dogpile.util.readwrite_lock import ReadWriteMutex
 from . import assert_raises_message
 from ._fixtures import _GenericBackendTest
@@ -53,6 +54,15 @@
     }
 
 
+class DBMBackendProxyTest(_GenericBackendTest):
+    backend = "dogpile.cache.dbm"
+
+    config_args = {
+        "arguments": {"filename": test_fname, "lock_factory": MutexLock},
+        "wrap": [ProxyBackend],
+    }
+
+
 class DBMBackendSerializerTest(
     _GenericSerializerTest, DBMBackendConditionTest
 ):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/dogpile.cache-rel_1_1_1/tests/cache/test_memcached_backend.py 
new/dogpile.cache-rel_1_1_4/tests/cache/test_memcached_backend.py
--- old/dogpile.cache-rel_1_1_1/tests/cache/test_memcached_backend.py   
2020-11-23 14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/tests/cache/test_memcached_backend.py   
2021-09-02 16:42:13.000000000 +0200
@@ -2,11 +2,13 @@
 import ssl
 from threading import Thread
 import time
+from unittest import mock
 from unittest import TestCase
 import weakref
 
 import pytest
 
+from dogpile.cache import make_region
 from dogpile.cache.backends.memcached import GenericMemcachedBackend
 from dogpile.cache.backends.memcached import MemcachedBackend
 from dogpile.cache.backends.memcached import PylibmcBackend
@@ -141,9 +143,6 @@
 ):
     backend = "dogpile.cache.bmemcached"
 
-    def test_threaded_get_multi(self):
-        pytest.skip("failing on bmemcached right now")
-
 
 class BMemcachedTLSTest(_NonDistributedTLSMemcachedTest):
     backend = "dogpile.cache.bmemcached"
@@ -169,6 +168,65 @@
     backend = "dogpile.cache.bmemcached"
 
 
+class PyMemcacheTest(_NonDistributedMemcachedTest):
+    backend = "dogpile.cache.pymemcache"
+
+    def test_pymemcache_enable_retry_client_not_set(self):
+        with mock.patch("warnings.warn") as warn_mock:
+            _ = make_region().configure(
+                "dogpile.cache.pymemcache",
+                arguments={"url": "foo", "retry_attempts": 2},
+            )
+            eq_(
+                warn_mock.mock_calls[0],
+                mock.call(
+                    "enable_retry_client is not set; retry options "
+                    "will be ignored"
+                ),
+            )
+
+
+class PyMemcacheDistributedWithTimeoutTest(
+    _DistributedMemcachedWithTimeoutTest
+):
+    backend = "dogpile.cache.pymemcache"
+
+
+class PyMemcacheTLSTest(_NonDistributedTLSMemcachedTest):
+    backend = "dogpile.cache.pymemcache"
+
+
+class PyMemcacheDistributedTest(_DistributedMemcachedTest):
+    backend = "dogpile.cache.pymemcache"
+
+
+class PyMemcacheDistributedMutexTest(_DistributedMemcachedMutexTest):
+    backend = "dogpile.cache.pymemcache"
+
+
+class PyMemcacheDistributedMutexWithTimeoutTest(
+    _DistributedMemcachedMutexWithTimeoutTest
+):
+    backend = "dogpile.cache.pymemcache"
+
+
+class PyMemcacheSerializerTest(
+    _GenericSerializerTest, _NonDistributedMemcachedTest
+):
+    backend = "dogpile.cache.pymemcache"
+
+
+class PyMemcacheRetryTest(_NonDistributedMemcachedTest):
+    backend = "dogpile.cache.pymemcache"
+    config_args = {
+        "arguments": {
+            "url": MEMCACHED_URL,
+            "enable_retry_client": True,
+            "retry_attempts": 3,
+        }
+    }
+
+
 class MemcachedTest(_NonDistributedMemcachedTest):
     backend = "dogpile.cache.memcached"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/tests/cache/test_region.py 
new/dogpile.cache-rel_1_1_4/tests/cache/test_region.py
--- old/dogpile.cache-rel_1_1_1/tests/cache/test_region.py      2020-11-23 
14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/tests/cache/test_region.py      2021-09-02 
16:42:13.000000000 +0200
@@ -202,6 +202,11 @@
 
         eq_(reg.get_or_create("some key", creator), "some value")
 
+    def test_key_is_locked(self):
+        reg = self._region()
+
+        eq_(reg.key_is_locked("some key"), False)
+
     def test_multi_creator(self):
         reg = self._region()
 
@@ -752,7 +757,7 @@
 
     class UsedKeysProxy(ProxyBackend):
 
-        """ Keep a counter of hose often we set a particular key"""
+        """Keep a counter of hose often we set a particular key"""
 
         def __init__(self, *args, **kwargs):
             super(ProxyBackendTest.UsedKeysProxy, self).__init__(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dogpile.cache-rel_1_1_1/tox.ini 
new/dogpile.cache-rel_1_1_4/tox.ini
--- old/dogpile.cache-rel_1_1_1/tox.ini 2020-11-23 14:51:14.000000000 +0100
+++ new/dogpile.cache-rel_1_1_4/tox.ini 2021-09-02 16:42:13.000000000 +0200
@@ -38,6 +38,7 @@
     {memcached}: python-memcached
     {memcached}: python-binary-memcached>=0.29.0
     {memcached}: pifpaf>=2.5.0
+    {memcached}: pymemcache>=3.5.0
     {redis}: redis
     {redis}: pifpaf
     {redis_sentinel}: redis
@@ -54,9 +55,13 @@
 basepython = python3
 deps=
     mypy
+    types-decorator
+    types-redis
     redis
     Mako
     decorator
+    types-redis
+    types-decorator
 commands = mypy ./dogpile/
 
 # thanks to https://julien.danjou.info/the-best-flake8-extensions/
@@ -70,4 +75,7 @@
       flake8-rst-docstrings
       # used by flake8-rst-docstrings
       pygments
-commands = flake8 ./dogpile/ ./tests/ setup.py  {posargs}
+      black==21.5b1
+commands =
+    flake8 ./dogpile/ ./tests/ setup.py  {posargs}
+    black --check .

Reply via email to