Package: zope.app.locking
This package provides a framework for object locking. The implementation
is intended to provide a simple general-purpose locking architecture upon
which other locking applications can be built (WebDAV locking, for example).Detailed DcoumentationObject LockingThis package provides a framework for object locking. The implementation
is intended to provide a simple general-purpose locking architecture upon
which other locking applications can be built (WebDAV locking, for example).The locking system is purelyadvisory- it provides a way to associate a
lock with an object, but it does not enforce locking in any way. It is up
to application-level code to ensure that locked objects are restricted in
a way appropriate to the application.The Zope 3 locking model defines interfaces and a default implementation
that:allows for a single lock on an object, owned by a specific principaldoes not necessarily impose inherent semantic meaning (exclusive
vs. non-exclusive, write vs. read) on locks, though it will
provide fields that higher-level application components can use
to implement and enforce such semanticscan potentially be used to build more ambitious locking
mechanisms (such as WebDAV locking equivalent to Zope 2)supports common use cases that have been uncovered in several years
of development of real-world applications (such as reporting all of
the objects locked by a given user)The Zope3 locking architecture defines anILockableinterface and
provides a default adapter implementation that requires only that an
object be adaptable toIKeyReference. All persistent objects can be
adapted to this interface by default in Zope 3, so in practice all
persistent objects are lockable.The defaultILockableadapter implementation provides support for:locking and unlocking an objectbreaking an existing lock on an objectobtaining the lock information for an objectLocking operations (lock, unlock, break lock) fire events that may be
handled by applications or other components to interact with the locking
system in a loosely-coupled way.Lock information is accessible through an object that supports theILockInfointerface. TheILockInfointerface impliesIAnnotatable,
so that other locking implementations (superseding or complementing the
default implementation) can store more information if needed to support
extended locking semantics.The locking architecture also supports an efficient method of lock tracking
that allows you to determine what locks are held on objects. The default
implementation provides anILockTrackerutility that can be used by
applications to quickly find all objects locked by a particular principal.Locking essentialsNormally, locking is provided by the default locking implementation. In
this example, we’ll create a simple content class. The content class
is persistent, which allows us to use the default locking adapters and
utilities.>>> import persistent>>> class Content(persistent.Persistent):
...     """A sample content object"""
...
...     def __init__(self, value):
...         self.value = value
...
...     def __call__(self):
...         return self
...
...     def __hash__(self):
...         return self.value
...
...     def __cmp__(self, other):
...         return cmp(self.value, other.value)Now we will create a few sample objects to work with:>>> item1 = Content("item1")
>>> item1.__name__ = "item1">>> item2 = Content("item2")
>>> item2.__name__ = "item2">>> item3 = Content("item3")
>>> item3.__name__ = "item3"It is possible to test whether an object supports locking by attempting
to adapt it to the ILockable interface:>>> from zope.app.locking.interfaces import ILockable
>>> from zope.app.locking.interfaces import ILockInfo>>> ILockable(item1, None)
<Locking adapter for...>>> ILockable(42, None)There must be an active interaction to use locking, to allow the framework
to determine the principal performing locking operations. This example sets
up some sample principals and a helper to switch principals for further
examples:>>> class FauxPrincipal:
...    def __init__(self, id):
...        self.id = id>>> britney = FauxPrincipal('britney')
>>> tim = FauxPrincipal('tim')>>> class FauxParticipation:
...     interaction = None
...     def __init__(self, principal):
...         self.principal = principal>>> import zope.security.management
>>> def set_principal(principal):
...     if zope.security.management.queryInteraction():
...         zope.security.management.endInteraction()
...     participation = FauxParticipation(principal)
...     zope.security.management.newInteraction(participation)>>> set_principal(britney)Now, let’s look at basic locking. To perform locking operations, we first
have to adapt an object toILockable:>>> obj = ILockable(item1)
>>> from zope.interface.verify import verifyObject
>>> verifyObject(ILockable, obj)
TrueWe can ask if the object is locked:>>> obj.locked()
FalseIf it were locked, we could get the id of the principal that owns the
lock. Since it is not locked, this will returnNone:>>> obj.locker()Now let’s lock the object. Note that the lock method return an instance
of an object that implementsILockInfoon success:>>> info = obj.lock()
>>> verifyObject(ILockInfo, info)
True>>> obj.locked()
True>>> obj.locker()
'britney'Methods are provided to check whether the current principal already has
the lock on an object and whether the lock is already owned by a different
principal:>>> obj.ownLock()
True>>> obj.isLockedOut()
FalseIf we switch principals, we see that the answers reflect the current
principal:>>> set_principal(tim)
>>> obj.ownLock()
False>>> obj.isLockedOut()
TrueA principal can only release his or her own locks:>>> obj.unlock()
Traceback (most recent call last):
  ...
LockingError: Principal is not lock ownerIf we switch back to the original principal, we see that the original
principal can unlock the object:>>> set_principal(britney)
>>> obj.unlock()There is a mechanism for breaking locks that does not take the current
principal into account. This will break any existing lock on an object:>>> obj.lock()
<...LockInfo...>>>> set_principal(tim)
>>> obj.locked()
True>>> obj.breaklock()
>>> obj.locked()
FalseLocks can be created with an optional timeout. If a timeout is provided,
it should be an integer number of seconds from the time the lock is
created.>>> # fake time function to avoid a time.sleep in tests!
>>> import time
>>> def faketime():
...    return time.time() + 3600.0>>> obj.lock(timeout=10)
<...LockInfo...>>>> obj.locked()
True>>> import zope.app.locking.storage
>>> zope.app.locking.storage.timefunc = faketime
>>> obj.locked()
False(Note that we undo our time hack in the tearDown of this module.)Finally, it is possible to explicitly get anILockInfoobject that
contains the lock information for the object. Note that locks that do
not have a timeout set have a timeout value ofNone.>>> obj = ILockable(item2)
>>> obj.lock()
<...LockInfo...>>>> info = obj.getLockInfo()
>>> info.principal_id
'tim'
>>> info.timeoutIt is possible to get the object associated with a lock directly from
anILockInfoinstance:>>> target = info.target
>>> target.__name__ == 'item2'
TrueTheILockInfointerface extends the IMapping interface, so application
code can store extra information on locks if necessary. It is recommended
that keys for extra data use qualified names following the convention that
is commonly used for annotations:>>> info['my.namespace.extra'] = 'spam'
>>> info['my.namespace.extra']
'spam'
>>> obj.unlock()
>>> obj.locked()
FalseLock trackingIt is often desirable to be able to report on the currently held locks in
a system (particularly on a per-user basis), without requiring an expensive
brute-force search. AnILockTrackerutility allows an application to get
the current locks for a principal, or all current locks:>>> set_principal(tim)
>>> obj = ILockable(item2)
>>> obj.lock()
<...LockInfo...>>>> set_principal(britney)
>>> obj = ILockable(item3)
>>> obj.lock()
<...LockInfo...>>>> from zope.app.locking.interfaces import ILockTracker
>>> from zope.component import getUtility
>>> util = getUtility(ILockTracker)
>>> verifyObject(ILockTracker, util)
True>>> items = util.getLocksForPrincipal('britney')
>>> len(items) == 1
True>>> items = util.getAllLocks()
>>> len(items) >= 2
TrueThese methods allow an application to create forms and other code that
performs unlocking or breaking of locks on sets of objects:>>> items = util.getAllLocks()
>>> for item in items:
...     obj = ILockable(item.target)
...     obj.breaklock()>>> items = util.getAllLocks()
>>> len(items)
0The lock storage utility provides further capabilities, and is a part of the
standard lock adapter implementation, but the ILockable interface does not
depend on ILockStorage.  Other implementations of ILockable may not use
ILockStorage.  However, if used by the adapter, it provides useful
capabilties.>>> from zope.app.locking.interfaces import ILockStorage
>>> util = getUtility(ILockStorage)
>>> verifyObject(ILockStorage, util)
TrueLocking eventsLocking operations (lock, unlock, break lock) fire events that can be used
by applications. Note that expiration of a lockdoes notfire an event
(because the current implementation uses a lazy expiration approach).>>> import zope.event>>> def log_event(event):
...     print event>>> zope.event.subscribers.append(log_event)>>> obj = ILockable(item2)
>>> obj.lock()
LockedEvent ...>>> obj.unlock()
UnlockedEvent ...>>> obj.lock()
LockedEvent ...>>> obj.breaklock()
BreakLockEvent ...TALES conditions based on lockingTALES expressions can use a named path adapter to get information
about the lock status for an object, including whether or not the
object can be locked.  The default registration for this adapter uses
the name “locking”, so a condition might be expressed like
“context/locking:ownLock”, for example.For objects that aren’t lockable, the adapter provides information
that makes sense:>>> from zope.component import getAdapter
>>> from zope.traversing.interfaces import IPathAdapter

>>> ns = getAdapter(42, IPathAdapter, "locking")
>>> ns.lockable
False

>>> ns.locked
False

>>> ns.lockedOut
False

>>> ns.ownLock
FalseUsing an object that’s lockable, but unlocked, also gives the expected
results:>>> ns = getAdapter(item1, IPathAdapter, "locking")
>>> ns.lockable
True

>>> ns.locked
False

>>> ns.lockedOut
False

>>> ns.ownLock
FalseIf we lock the object, the adapter indicates that the object is locked
and that we own it:>>> ob = ILockable(item1)
>>> ob.lock()
LockedEvent for ...

>>> ns.lockable
True

>>> ns.locked
True

>>> ns.lockedOut
False

>>> ns.ownLock
TrueCHANGES3.5.0 (2009-02-01)Usezope.siteinstead ofzope.app.folderin test.Remove usage of deprecatedzope.app.zapi.3.4.0 (2007-10-25)Initial release independent of the main Zope tree.