Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add context manager for unpinning #57

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions multidb/pinning.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


__all__ = ['this_thread_is_pinned', 'pin_this_thread', 'unpin_this_thread',
'use_primary_db', 'use_master', 'db_write']
'use_primary_db', 'use_secondary_db', 'use_master', 'db_write']


_locals = threading.local()
Expand All @@ -33,8 +33,8 @@ def unpin_this_thread():
_locals.pinned = False


class UsePrimaryDB(object):
"""A contextmanager/decorator to use the master database."""
class _UseDB(object):
"""A contextmanager/decorator to use the specified database."""
def __call__(self, func):
@wraps(func)
def decorator(*args, **kw):
Expand All @@ -43,14 +43,31 @@ def decorator(*args, **kw):
return decorator

def __enter__(self):
_locals.old = this_thread_is_pinned()
pin_this_thread()
_locals.old = getattr(_locals, 'old', [])
_locals.old.append(this_thread_is_pinned())

def __exit__(self, type, value, tb):
if not _locals.old:
previous_state = _locals.old.pop()
if previous_state:
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ This introduces a subtle behaviour change. Previously, manual use of unpin_this_thread() would leak out of the context block, for example when:

  1. a thread is pinned
  2. enters the context block (_locals.old = True)
  3. is manually unpinned (with unpin_this_thread())
  4. exits the context block (not _locals.old == False so no action is taken)
  5. the thread will be unpinned when it reaches here

This new code will always restore the previous pinning state when exiting a context block. In my view this is more predictable, but I'm open to your opinion on this.

pin_this_thread()
else:
unpin_this_thread()


class UsePrimaryDB(_UseDB):
"""A contextmanager/decorator to use the primary database."""
def __enter__(self):
super(UsePrimaryDB, self).__enter__()
pin_this_thread()


class UseSecondaryDB(_UseDB):
"""A contextmanager/decorator to use the secondary database."""
def __enter__(self):
super(UseSecondaryDB, self).__enter__()
unpin_this_thread()


class DeprecatedUseMaster(UsePrimaryDB):
def __enter__(self):
warnings.warn(
Expand All @@ -62,6 +79,7 @@ def __enter__(self):


use_primary_db = UsePrimaryDB()
use_secondary_db = UseSecondaryDB()
use_master = DeprecatedUseMaster()


Expand Down
Loading