py-sherlock / sherlock

Easy distributed locks for Python with a choice of backends.
MIT License
374 stars 35 forks source link

Setup sherlock with only redis backend. #29

Closed Cediddi closed 1 year ago

Cediddi commented 6 years ago

I want to install sherlock but I'll not use memcache as my backend, I'll use redis exclusively. I wish there was a way to select our backends like in celery. pylibmc depends on memcached.h file.

pip install celery[redis]
vaidik commented 5 years ago

Ah. That's a good suggestion. I can work on this. Give me sometime to look into this. Watch out this thread for updates.

Unfortunately I have not been able to respond actively but I am going to try to do now. Apologies for the delay.

isidentical commented 5 years ago

Eh, i dont think it is possible unless pypa resolves pypa/setuptools#1139 .

isidentical commented 5 years ago

BTW you can just clone this fork and pip install -e '.[redis]' but until pypa resolves the issue i mentioned i can't PR to this repo's master.

This commit allows to optional dependicies.

Cediddi commented 5 years ago

I think the commit @isidentical mentioned is a good start. Celery does not come with a backend or broker driver out of the box (except ), you are supposed to install drivers either manually or by extras.

pip install celery only comes with rabbitmq support (which is not celery's dependency, but kombu needs it). Documentation says, you should do pip install celery[redis,rabbitmq]

I think this is the way to go and docs should reflect that the drivers are optional.

isidentical commented 4 years ago

I am cleaning up my github, the link is now broken but i kept diff,

diff --git a/setup.py b/setup.py
index a101a73..daa1eec 100644
--- a/setup.py
+++ b/setup.py
@@ -24,11 +24,6 @@ setup(
     platforms=('Any',),
     packages=find_packages(exclude=['tests']),
     include_package_data=True,
-    install_requires=[
-        'redis',
-        'python-etcd',
-        'pylibmc',
-    ],
     zip_safe = False,
     classifiers = [
         'Development Status :: 2 - Pre-Alpha',
@@ -41,5 +36,10 @@ setup(
     tests_require=[
         'nose',
         'mock',
-    ]
+    ],
+    extras_require={
+        'redis': ['redis'],
+        'etcd': ['python-etcd'],
+        'memcached': ['pylibmc']
+    }
 )
diff --git a/sherlock/__init__.py b/sherlock/__init__.py
index c88ca14..393caf6 100644
--- a/sherlock/__init__.py
+++ b/sherlock/__init__.py
@@ -227,50 +227,75 @@ Distributed Locking in Other Languages
 * NodeJS - https://github.com/thedeveloper/warlock
 '''

-import etcd
-import pylibmc
-import redis
-
+try:
+    from contextlib import suppress
+except ImportError:
+    from contextlib import contextmanager
+    @contextmanager
+    def suppress(exc):
+        try:
+            yield
+        except exc:
+            pass

 class _Backends(object):
     '''
     A simple object that provides a list of available backends.
     '''

-    REDIS = {
-        'name': 'REDIS',
-        'library': 'redis',
-        'client_class': redis.StrictRedis,
-        'lock_class': 'RedisLock',
-        'default_args': (),
-        'default_kwargs': {},
-    }
-    ETCD = {
-        'name': 'ETCD',
-        'library': 'etcd',
-        'client_class': etcd.Client,
-        'lock_class': 'EtcdLock',
-        'default_args': (),
-        'default_kwargs': {},
-    }
-    MEMCACHED = {
-        'name': 'MEMCACHED',
-        'library': 'pylibmc',
-        'client_class': pylibmc.Client,
-        'lock_class': 'MCLock',
-        'default_args': (
-            ['localhost'],
-        ),
-        'default_kwargs': {
-            'binary': True,
-        },
-    }
-
-    _valid_backends = (
-        REDIS,
-        ETCD,
-        MEMCACHED,
-    )
+    _valid_backends = []
+    _valid_backend_modules = {}
+
+    with suppress(ImportError):
+        import redis
+
+        REDIS = {
+            'name': 'REDIS',
+            'library': 'redis',
+            'client_class': redis.StrictRedis,
+            'lock_class': 'RedisLock',
+            'default_args': (),
+            'default_kwargs': {},
+        }
+
+        _valid_backends.append(REDIS)
+        _valid_backend_modules['redis'] = redis
+
+    with suppress(ImportError):
+        import etcd
+
+        ETCD = {
+            'name': 'ETCD',
+            'library': 'etcd',
+            'client_class': etcd.Client,
+            'lock_class': 'EtcdLock',
+            'default_args': (),
+            'default_kwargs': {},
+        }
+
+        _valid_backends.append(ETCD)
+        _valid_backend_modules['etcd'] = etcd
+
+    with suppress(ImportError):
+        import pylibmc
+
+        MEMCACHED = {
+            'name': 'MEMCACHED',
+            'library': 'pylibmc',
+            'client_class': pylibmc.Client,
+            'lock_class': 'MCLock',
+            'default_args': (
+                ['localhost'],
+            ),
+            'default_kwargs': {
+                'binary': True,
+            },
+        }
+
+        _valid_backends.append(MEMCACHED)
+        _valid_backend_modules['pylibmc'] = pylibmc
+
+    _valid_backends = tuple(_valid_backends)

     def register(self, name, lock_class, library, client_class,
                  default_args=(), default_kwargs={}):
diff --git a/sherlock/lock.py b/sherlock/lock.py
index a92ee02..9e2b2f3 100644
--- a/sherlock/lock.py
+++ b/sherlock/lock.py
@@ -9,14 +9,8 @@ __all__ = [
     'LockException',
     'LockTimeoutException',
     'Lock',
-    'RedisLock',
-    'EtcdLock',
-    'MCLock'
 ]

-import etcd
-import pylibmc
-import redis
 import time
 import uuid

@@ -24,6 +18,18 @@ from . import backends
 from . import _configuration

+def depends(module):
+    if module in backends._valid_backend_modules:
+        globals()[module] = backends._valid_backend_modules[module]
+
+        def wrapper(cls):
+            __all__.append(cls.__name__)
+            return cls
+
+        return wrapper
+    else:
+        return (lambda cls: None)
+
 class LockException(Exception):
     '''
     Generic exception for Locks.
@@ -320,7 +326,7 @@ class Lock(BaseLock):
                                 'Configure lock backend first.')
         return self._lock_proxy.locked()

-
+@depends('redis')
 class RedisLock(BaseLock):
     '''
     Implementation of lock with Redis as the backend for synchronization.
@@ -447,7 +453,7 @@ class RedisLock(BaseLock):
             return False
         return True

-
+@depends('etcd')
 class EtcdLock(BaseLock):
     '''
     Implementation of lock with Etcd as the backend for synchronization.
@@ -562,7 +568,7 @@ class EtcdLock(BaseLock):
         except etcd.EtcdKeyNotFound:
             return False

-
+@depends('pylibmc')
 class MCLock(BaseLock):
     '''
     Implementation of lock with Memcached as the backend for synchronization.
judahrand commented 1 year ago

This is now implemented.

Cediddi commented 1 year ago

This is great. Thanks for the effort. Myself from 5 years ago would be thrilled. I may give sherlock another go in a different project now :)