[Subversion] / Contextual / Contextual.txt  

Diff of /Contextual/Contextual.txt

Parent Directory | Revision Log

version 2278, Wed Jan 24 03:06:24 2007 UTC version 2279, Sat Feb 24 05:37:44 2007 UTC
Line 6 
Line 6 
 Developer's Guide  Developer's Guide
 -----------------  -----------------
   
       >>> from peak import context
   
   
 Overview and Concepts  Overview and Concepts
 =====================  =====================
   
   The ``Contextual`` library provides a safe replacement for the use of global
   (or thread-local) variables, making it easy to create non-invasive frameworks
   that are still highly testable and configurable.  Code based on ``Contextual``
   is also safe for use in event-driven, coroutine, or "co-operative" multitasking
   systems such as Twisted and ``peak.events``, because an entire thread's context
   can be easily saved and restored when switching between different logical
   threads, tasks, or coroutines.
   
   The basic idea of the ``Contextual`` library is that it allows you to have
   "current" instances or values, that are specific to the current call stack in
   the current thread.  These "current" values can be temporarily changed
   using either a Python 2.5 ``with:`` statement, or, if backward compatibility is
   desired, a special decorator that simulates the ``with:`` statement in older
   versions of Python.
   
   There are three major kinds of "current" values that ``Contextual`` supports:
   
   Services
       Services are like singletons, but have a separate instance for each thread
       and can easily be replaced as needed for e.g. testing.  Services are
       created by subclassing ``context.Service``, and the resulting class's
       ``get()`` classmethod will return the current instance.  Alternative
       implementations of a service can be defined and used to replace the default
       implementation.
   
   Configuration Settings
       The ``context.Config`` service provides thread-safe access to stateless
       configuration values.  It is essentially a mapping whose contents cannot be
       changed once they have been read.  The idea is that a configuration's
       settings may be changed more than once while the configuration is being
       set up, but once any code actually *uses* a setting, it should be
       impossible for other code to see a different value for the same setting,
       within the scope of that configuration.  (Of course, you can set up nested
       and/or inheriting configurations with different setting values, if you need
       to.)
   
   Transaction Resources
       The ``context.Action`` service manages access to object that may need to
       be recycled, saved, or reverted at the completion of an action such as
       a transaction, web request, command, or other atomic operation.  Requesting
       a resource such as a database connection causes it to become registered
       with the current action, so that it will be notified of the success or
       failure of the current ``Action``.  This then allows the resource to commit
       or rollback as appropriate, return connections to connection pools, etc.
   
       The boundaries of an action are indicated by the scope of a ``with:``
       statement (or substitute).  E.g.::
   
           with context.Action():
               # any resources used here will be notified of the
               # success or failure of this block of code
   
   
   Creating and Using Services
   ===========================
   
   Here's a simple "counter" service implementation::
   
       >>> class Counter(context.Service):
       ...     value = 0
       ...     def inc(self):
       ...         self.value += 1
   
   You can get the current instance of the service using its ``get()``
   classmethod::
   
       >>> count = Counter.get()   # get the current instance
       >>> count.value
       0
       >>> count.inc()
       >>> count.value
       1
   
   By default, each thread will have its own unique instance, that's created upon
   first use, and is cached thereafter::
   
       >>> count is Counter.get()  # still using the same one
       True
   
       >>> def run_in_another_thread():
       ...     c2 = Counter.get()
       ...     print c2 is count           # this will be different
       ...     print c2 is Counter.get()   # but the same as long as thread runs
   
       >>> import threading
       >>> t = threading.Thread(target=run_in_another_thread)
   
       >>> t.start(); t.join() # run and wait for it to finish
       False
       True
   
   But you can replace the current instance with another instance, by using a
   ``with:`` statement::
   
       >>> with Counter():     # the easy way to say it
       ...     print count is Counter.get()
       False
   
       >>> c2 = Counter()  # prove that this is the instance used in the block
       >>> with c2:
       ...     print c2 is Counter.get()
       True
   
       >>> count is Counter.get()  # old value is restored
       True
   
   You can also create alternative implementations for a service, and substitute
   them for the original service.  They do not have to be subclasses of the
   original service for this to work, but they should of course be a suitable
   replacement::
   
       >>> class DoubleCounter(context.Service):
       ...     context.replaces(Counter)
       ...     value = 0
       ...     def inc(self):
       ...         self.value += 2
   
       >>> with DoubleCounter():
       ...     print Counter.get().value
       ...     Counter.get().inc()
       ...     print Counter.get().value
       0
       2
   
   
   
   
Line 59 
Line 186 
   
   
   
 PEP 343 Support  Support for Python 2.3/2.4
 ===============  ==========================
   
     'call_with', 'with_', 'manager', 'gen_exc_info',    # PEP 343 impl.      'call_with', 'with_', 'manager', 'gen_exc_info',    # PEP 343 impl.
   


Generate output suitable for use with a patch program
Legend:
Removed from v.2278  
changed lines
  Added in v.2279

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help