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 .