Hi, Many web frameworks and ORM tools have the need to propagate data depending on some or other context within which a request is dealt with. Passing it all via parameters to every nook of your code is cumbersome.
A lot of the frameworks use a thread local context to solve this problem. I'm assuming these are based on threading.local. (See, for example: http://www.sqlalchemy.org/docs/05/session.html#unitofwork_contextual ) Such usage assumes that one request is served per thread. This is not necessarily the case. (Twisted would perhaps be an example, but I have not checked how the twisted people deal with the issue.) The bottom line for me is that if you build a WSGI app, you'd not want to restrict it to being able to run in a one request-per-thread setup. So I've been playing with the idea to use something that creates a context local to the current call stack instead. I.e. a context (dict) which is inserted into the call stack at some point, and can be accessed by any method/function deeper in the stack. The normal use case for this is to propagate a database connection. But it can also be used to propagate other things, such as information about the user who is currently logged in, etc. Since this is one way of creating objects that are global to a context (the call stack), I'm sure it is in some ways evil and can be abused. But that criticism can be levelled against the thread-local solution too... I attach some code to illustrate - and would appreciate some feedback on the idea and its implementation. -i
# --------------------------------------------- CC # import inspect class CC(object): """A class used to create and maintain a call-local context. A call-local context is a way to have data that is accessable to any code from a certain point in the call stack onwards, without the need to explicitly pass parameters each time. A call-local context does for code beyond it in the call stack, what thread-local does for code in a certain thread. Such a context is a dict, and it is a singleton per call stack. To create the context, do: >>> cc = CC(somename='somevalue') Code can use the context by means of a class method which will return the correct singleton per call stack: >>> def foo(): >>> assert CC.get_context()['somename'] == 'somevalue' Code that will use the context then needs to be invoked via the context: >>> cc(foo) Another example: >>> def bar(someparam): >>> print someparam >>> foo() >>> >>> cc(bar, 'value for some param') """ def __init__(self, **kwargs): self.context = None self.context = self.__class__.get_context() or {} self.context.update(kwargs) def __call__(self, callable, *args, **kwargs): """This is used to call a callable within the constructed call context""" return callable(*args, **kwargs) @classmethod def get_context_hash(cls): """Returns a unique, hashable identifier identifying the current call context""" return id(cls.get_context()) @classmethod def get_context(cls): """Returns the current call context, or None if there is none""" context = None f = inspect.currentframe() while context is None and f: candidate = f.f_locals.get('self', None) if isinstance(candidate, cls): context = candidate.context to_delete = f f = f.f_back del to_delete return context # ------------------------------------ Use case in SQLAlchemy # # Note the dict itself is not really used to store a session, # since SQLAlchemy's scoped session already takes care of # storing sessions. # from sqlalchemy.orm import scoped_session, sessionmaker Session = scoped_session(sessionmaker(), scopefunc=CC.get_context_hash) def checkit(): sess = Session() sess2 = Session() assert sess is sess2 CC()(checkit) def checkit2(session): sess = Session() assert sess is not session session = Session() CC()(checkit2, session) # ---------------------------------------- To show/ test what CC does # class A(object): def foo(self): print CC.get_context() # Here's how you'd get the dict class B(object): a = A() def bar(self, arg): print arg print CC.get_context() CC.get_context()[5] = '5' # It can be modified self.a.foo() b = B() cc = CC(one=2) # Create a call context cc(b.bar, 'some argument for bar()') # Call methods that will use it class C(object): b = B() def goo(self): CC(two=2)(b.bar, 'some argument for bar() sent in from goo()') # Here, a call context is created, then we call a method (goo) # which will create another. The point of the excercise is to # show that the context is a singleton c = C() CC(a='A')(c.goo)
_______________________________________________ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com