| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
""" |
|---|
| 8 |
Dynamic pseudo-scoping for Python. |
|---|
| 9 |
|
|---|
| 10 |
Call functions with context.call({key: value}, func); func and |
|---|
| 11 |
functions that it calls will be able to use 'context.get(key)' to |
|---|
| 12 |
retrieve 'value'. |
|---|
| 13 |
|
|---|
| 14 |
This is thread-safe. |
|---|
| 15 |
""" |
|---|
| 16 |
|
|---|
| 17 |
try: |
|---|
| 18 |
from threading import local |
|---|
| 19 |
except ImportError: |
|---|
| 20 |
local = None |
|---|
| 21 |
|
|---|
| 22 |
from twisted.python import threadable |
|---|
| 23 |
|
|---|
| 24 |
defaultContextDict = {} |
|---|
| 25 |
|
|---|
| 26 |
setDefault = defaultContextDict.__setitem__ |
|---|
| 27 |
|
|---|
| 28 |
class ContextTracker: |
|---|
| 29 |
def __init__(self): |
|---|
| 30 |
self.contexts = [defaultContextDict] |
|---|
| 31 |
|
|---|
| 32 |
def callWithContext(self, ctx, func, *args, **kw): |
|---|
| 33 |
newContext = self.contexts[-1].copy() |
|---|
| 34 |
newContext.update(ctx) |
|---|
| 35 |
self.contexts.append(newContext) |
|---|
| 36 |
try: |
|---|
| 37 |
return func(*args,**kw) |
|---|
| 38 |
finally: |
|---|
| 39 |
self.contexts.pop() |
|---|
| 40 |
|
|---|
| 41 |
def getContext(self, key, default=None): |
|---|
| 42 |
return self.contexts[-1].get(key, default) |
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
class _ThreadedContextTracker: |
|---|
| 46 |
def __init__(self): |
|---|
| 47 |
self.threadId = threadable.getThreadID |
|---|
| 48 |
self.contextPerThread = {} |
|---|
| 49 |
|
|---|
| 50 |
def currentContext(self): |
|---|
| 51 |
tkey = self.threadId() |
|---|
| 52 |
try: |
|---|
| 53 |
return self.contextPerThread[tkey] |
|---|
| 54 |
except KeyError: |
|---|
| 55 |
ct = self.contextPerThread[tkey] = ContextTracker() |
|---|
| 56 |
return ct |
|---|
| 57 |
|
|---|
| 58 |
def callWithContext(self, ctx, func, *args, **kw): |
|---|
| 59 |
return self.currentContext().callWithContext(ctx, func, *args, **kw) |
|---|
| 60 |
|
|---|
| 61 |
def getContext(self, key, default=None): |
|---|
| 62 |
return self.currentContext().getContext(key, default) |
|---|
| 63 |
|
|---|
| 64 |
|
|---|
| 65 |
class _TLSContextTracker(_ThreadedContextTracker): |
|---|
| 66 |
def __init__(self): |
|---|
| 67 |
self.storage = local() |
|---|
| 68 |
|
|---|
| 69 |
def currentContext(self): |
|---|
| 70 |
try: |
|---|
| 71 |
return self.storage.ct |
|---|
| 72 |
except AttributeError: |
|---|
| 73 |
ct = self.storage.ct = ContextTracker() |
|---|
| 74 |
return ct |
|---|
| 75 |
|
|---|
| 76 |
if local is None: |
|---|
| 77 |
ThreadedContextTracker = _ThreadedContextTracker |
|---|
| 78 |
else: |
|---|
| 79 |
ThreadedContextTracker = _TLSContextTracker |
|---|
| 80 |
|
|---|
| 81 |
def installContextTracker(ctr): |
|---|
| 82 |
global theContextTracker |
|---|
| 83 |
global call |
|---|
| 84 |
global get |
|---|
| 85 |
|
|---|
| 86 |
theContextTracker = ctr |
|---|
| 87 |
call = theContextTracker.callWithContext |
|---|
| 88 |
get = theContextTracker.getContext |
|---|
| 89 |
|
|---|
| 90 |
installContextTracker(ThreadedContextTracker()) |
|---|