Skip to content

Commit

Permalink
Merge pull request #12 from thorgate/chore/update-django-lts-support
Browse files Browse the repository at this point in the history
Update Django version to support latest LTS
  • Loading branch information
jorgenader authored May 13, 2019
2 parents 81e404e + 441186e commit e32789b
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 17 deletions.
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ env:
- DJANGO=1.11
- DJANGO=2.0
- DJANGO=2.1
- DJANGO=2.2

matrix:
exclude:
- python: "3.4"
env: DJANGO=2.1
- python: "3.4"
env: DJANGO=2.2
- python: "3.7"
env: DJANGO=1.8
- python: "3.7"
Expand Down Expand Up @@ -45,7 +48,7 @@ deploy:
repo: thorgate/tg-utils
tags: true
python: "3.6"
condition: "$DJANGO = 2.0"
condition: "$DJANGO = 2.2"

notifications:
email: false
1 change: 1 addition & 0 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ pytest==2.8.5
pytest-django==2.9.1
django_compressor==2.0
hashids==1.1.0
python-redis-lock>=3.2.0,<4.0.0
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@
},
include_package_data=True,
install_requires=[
'django>=1.8,!=2.1.0,!=2.1.1,<2.2',
'django>=1.8,!=2.1.0,!=2.1.1,<3.0',
],
extras_require={
'lock': [
'redis>=2.10.0',
'python-redis-lock>=3.2.0,<4.0.0',
],
'health_check': [
Expand Down
2 changes: 1 addition & 1 deletion tg_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

__author__ = 'Thorgate'
__email__ = '[email protected]'
__version__ = '0.6.1'
__version__ = '0.7.0'
38 changes: 24 additions & 14 deletions tg_utils/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,37 @@


REDIS_LOCK_URL = getattr(settings, 'REDIS_LOCK_URL', False)
if not REDIS_LOCK_URL:
raise ImproperlyConfigured("To use locking, set REDIS_LOCK_URL in your settings.py")
_redis_conn = StrictRedis.from_url(REDIS_LOCK_URL)

DEFAULT_PREFIX = getattr(settings, 'REDIS_LOCK_DEFAULT_PREFIX', 'acquires_lock_')
DEFAULT_PREFIX = getattr(settings, 'REDIS_LOCK_DEFAULT_PREFIX', 'acquires_lock')


logger = logging.getLogger('tg-utils.lock')
logger.setLevel(logging.DEBUG if settings.DEBUG else logging.INFO)


def get_redis_connection():
if not REDIS_LOCK_URL:
raise ImproperlyConfigured("To use locking, set REDIS_LOCK_URL in your settings.py")

return StrictRedis.from_url(REDIS_LOCK_URL)


def get_lock(resource, expires):
# Seconds from now on
if isinstance(expires, timedelta):
expires = expires.total_seconds()

return redis_lock.Lock(_redis_conn, resource, expire=expires)
return redis_lock.Lock(get_redis_connection(), resource, expire=expires)


def acquires_lock(expires, should_fail=True, should_wait=False, resource=None, prefix=DEFAULT_PREFIX):
def acquires_lock(expires, should_fail=True, should_wait=False, resource=None, prefix=DEFAULT_PREFIX, create_id=None):
"""
Decorator to ensure function only runs when it is unique holder of the resource.
Any invocations of the functions before the first is done
will raise RuntimeError.
Locks are stored in redis with prefix: `lock:acquires_lock`
Locks are stored in redis with default prefix: `lock:acquires_lock`
Arguments:
expires(timedelta|int): Expiry time of lock, way more than expected time to run.
Expand All @@ -49,6 +53,7 @@ def acquires_lock(expires, should_fail=True, should_wait=False, resource=None, p
should_wait(bool): Should this task wait for lock to be released.
resource(str): Resource identifier, by default taken from function name.
prefix(str): Change prefix added to redis key (the 'lock:' part will always be added)
create_id(function): Change suffix added to redis key to lock only specific function call based on arguments.
Example:
Expand All @@ -60,11 +65,6 @@ def acquires_lock(expires, should_fail=True, should_wait=False, resource=None, p
def foo():
...
"""

# Seconds from now on
if isinstance(expires, timedelta):
expires = expires.total_seconds()

# This is just a tiny wrapper around redis_lock
# 1) acquire lock or fail
# 2) run function
Expand All @@ -76,12 +76,20 @@ def decorator(f):
if resource is None:
resource = f.__name__

resource = '%s%s' % (prefix, resource)
resource = '%s:%s' % (prefix, resource)

@wraps(f)
def wrapper(*args, **kwargs):
lock_suffix = None

if create_id:
lock_suffix = create_id(*args, **kwargs)

# The context manager is annoying and always blocking...
lock = redis_lock.Lock(_redis_conn, resource, expire=expires)
lock = get_lock(
resource='%s:%s' % (resource, lock_suffix) if lock_suffix else resource,
expires=expires,
)
lock_acquired = False

# Get default lock blocking mode
Expand All @@ -104,9 +112,11 @@ def wrapper(*args, **kwargs):
if not lock.acquire(blocking=is_blocking):
if should_fail:
raise RuntimeError("Failed to acquire lock: %s" % resource)

logger.warning('Failed to acquire lock: %s', resource)
if not should_execute_if_lock_fails:
return False

else:
lock_acquired = True

Expand Down

0 comments on commit e32789b

Please sign in to comment.