mirror of
https://github.com/django/django.git
synced 2025-07-05 18:29:11 +00:00
magic-removal: Upgraded django.dispatch to pydispatcher 1.0.3 (which hasn't been released yet; got it from latest CVS)
git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2636 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
c229739f2f
commit
25b712819d
@ -1,6 +1,6 @@
|
|||||||
"""Multi-consumer multi-producer dispatching mechanism
|
"""Multi-consumer multi-producer dispatching mechanism
|
||||||
"""
|
"""
|
||||||
__version__ = "1.0.0"
|
__version__ = "1.0.0"
|
||||||
__author__ = "Patrick K. O'Brien"
|
__author__ = "Patrick K. O'Brien"
|
||||||
__license__ = "BSD-style, see license.txt for details"
|
__license__ = "BSD-style, see license.txt for details"
|
||||||
|
|
||||||
|
@ -1,485 +1,497 @@
|
|||||||
"""Multiple-producer-multiple-consumer signal-dispatching
|
"""Multiple-producer-multiple-consumer signal-dispatching
|
||||||
|
|
||||||
dispatcher is the core of the PyDispatcher system,
|
dispatcher is the core of the PyDispatcher system,
|
||||||
providing the primary API and the core logic for the
|
providing the primary API and the core logic for the
|
||||||
system.
|
system.
|
||||||
|
|
||||||
Module attributes of note:
|
Module attributes of note:
|
||||||
|
|
||||||
Any -- Singleton used to signal either "Any Sender" or
|
Any -- Singleton used to signal either "Any Sender" or
|
||||||
"Any Signal". See documentation of the _Any class.
|
"Any Signal". See documentation of the _Any class.
|
||||||
Anonymous -- Singleton used to signal "Anonymous Sender"
|
Anonymous -- Singleton used to signal "Anonymous Sender"
|
||||||
See documentation of the _Anonymous class.
|
See documentation of the _Anonymous class.
|
||||||
|
|
||||||
Internal attributes:
|
Internal attributes:
|
||||||
WEAKREF_TYPES -- tuple of types/classes which represent
|
WEAKREF_TYPES -- tuple of types/classes which represent
|
||||||
weak references to receivers, and thus must be de-
|
weak references to receivers, and thus must be de-
|
||||||
referenced on retrieval to retrieve the callable
|
referenced on retrieval to retrieve the callable
|
||||||
object
|
object
|
||||||
connections -- { senderkey (id) : { signal : [receivers...]}}
|
connections -- { senderkey (id) : { signal : [receivers...]}}
|
||||||
senders -- { senderkey (id) : weakref(sender) }
|
senders -- { senderkey (id) : weakref(sender) }
|
||||||
used for cleaning up sender references on sender
|
used for cleaning up sender references on sender
|
||||||
deletion
|
deletion
|
||||||
sendersBack -- { receiverkey (id) : [senderkey (id)...] }
|
sendersBack -- { receiverkey (id) : [senderkey (id)...] }
|
||||||
used for cleaning up receiver references on receiver
|
used for cleaning up receiver references on receiver
|
||||||
deletion, (considerably speeds up the cleanup process
|
deletion, (considerably speeds up the cleanup process
|
||||||
vs. the original code.)
|
vs. the original code.)
|
||||||
"""
|
"""
|
||||||
import types, weakref
|
from __future__ import generators
|
||||||
from django.dispatch import saferef, robustapply, errors
|
import types, weakref
|
||||||
|
from django.dispatch import saferef, robustapply, errors
|
||||||
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
|
||||||
__cvsid__ = "$Id: dispatcher.py,v 1.8 2004/11/26 06:37:33 mcfletch Exp $"
|
__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
|
||||||
__version__ = "$Revision: 1.8 $"[11:-2]
|
__cvsid__ = "$Id: dispatcher.py,v 1.9 2005/09/17 04:55:57 mcfletch Exp $"
|
||||||
|
__version__ = "$Revision: 1.9 $"[11:-2]
|
||||||
class _Parameter:
|
|
||||||
"""Used to represent default parameter values."""
|
try:
|
||||||
def __repr__(self):
|
True
|
||||||
return self.__class__.__name__
|
except NameError:
|
||||||
|
True = 1==1
|
||||||
class _Any(_Parameter):
|
False = 1==0
|
||||||
"""Singleton used to signal either "Any Sender" or "Any Signal"
|
|
||||||
|
class _Parameter:
|
||||||
The Any object can be used with connect, disconnect,
|
"""Used to represent default parameter values."""
|
||||||
send, or sendExact to signal that the parameter given
|
def __repr__(self):
|
||||||
Any should react to all senders/signals, not just
|
return self.__class__.__name__
|
||||||
a particular sender/signal.
|
|
||||||
"""
|
class _Any(_Parameter):
|
||||||
Any = _Any()
|
"""Singleton used to signal either "Any Sender" or "Any Signal"
|
||||||
|
|
||||||
class _Anonymous(_Parameter):
|
The Any object can be used with connect, disconnect,
|
||||||
"""Singleton used to signal "Anonymous Sender"
|
send, or sendExact to signal that the parameter given
|
||||||
|
Any should react to all senders/signals, not just
|
||||||
The Anonymous object is used to signal that the sender
|
a particular sender/signal.
|
||||||
of a message is not specified (as distinct from being
|
"""
|
||||||
"any sender"). Registering callbacks for Anonymous
|
Any = _Any()
|
||||||
will only receive messages sent without senders. Sending
|
|
||||||
with anonymous will only send messages to those receivers
|
class _Anonymous(_Parameter):
|
||||||
registered for Any or Anonymous.
|
"""Singleton used to signal "Anonymous Sender"
|
||||||
|
|
||||||
Note:
|
The Anonymous object is used to signal that the sender
|
||||||
The default sender for connect is Any, while the
|
of a message is not specified (as distinct from being
|
||||||
default sender for send is Anonymous. This has
|
"any sender"). Registering callbacks for Anonymous
|
||||||
the effect that if you do not specify any senders
|
will only receive messages sent without senders. Sending
|
||||||
in either function then all messages are routed
|
with anonymous will only send messages to those receivers
|
||||||
as though there was a single sender (Anonymous)
|
registered for Any or Anonymous.
|
||||||
being used everywhere.
|
|
||||||
"""
|
Note:
|
||||||
Anonymous = _Anonymous()
|
The default sender for connect is Any, while the
|
||||||
|
default sender for send is Anonymous. This has
|
||||||
WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
|
the effect that if you do not specify any senders
|
||||||
|
in either function then all messages are routed
|
||||||
connections = {}
|
as though there was a single sender (Anonymous)
|
||||||
senders = {}
|
being used everywhere.
|
||||||
sendersBack = {}
|
"""
|
||||||
|
Anonymous = _Anonymous()
|
||||||
|
|
||||||
def connect(receiver, signal=Any, sender=Any, weak=True):
|
WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
|
||||||
"""Connect receiver to sender for signal
|
|
||||||
|
connections = {}
|
||||||
receiver -- a callable Python object which is to receive
|
senders = {}
|
||||||
messages/signals/events. Receivers must be hashable
|
sendersBack = {}
|
||||||
objects.
|
|
||||||
|
|
||||||
if weak is True, then receiver must be weak-referencable
|
def connect(receiver, signal=Any, sender=Any, weak=True):
|
||||||
(more precisely saferef.safeRef() must be able to create
|
"""Connect receiver to sender for signal
|
||||||
a reference to the receiver).
|
|
||||||
|
receiver -- a callable Python object which is to receive
|
||||||
Receivers are fairly flexible in their specification,
|
messages/signals/events. Receivers must be hashable
|
||||||
as the machinery in the robustApply module takes care
|
objects.
|
||||||
of most of the details regarding figuring out appropriate
|
|
||||||
subsets of the sent arguments to apply to a given
|
if weak is True, then receiver must be weak-referencable
|
||||||
receiver.
|
(more precisely saferef.safeRef() must be able to create
|
||||||
|
a reference to the receiver).
|
||||||
Note:
|
|
||||||
if receiver is itself a weak reference (a callable),
|
Receivers are fairly flexible in their specification,
|
||||||
it will be de-referenced by the system's machinery,
|
as the machinery in the robustApply module takes care
|
||||||
so *generally* weak references are not suitable as
|
of most of the details regarding figuring out appropriate
|
||||||
receivers, though some use might be found for the
|
subsets of the sent arguments to apply to a given
|
||||||
facility whereby a higher-level library passes in
|
receiver.
|
||||||
pre-weakrefed receiver references.
|
|
||||||
|
Note:
|
||||||
signal -- the signal to which the receiver should respond
|
if receiver is itself a weak reference (a callable),
|
||||||
|
it will be de-referenced by the system's machinery,
|
||||||
if Any, receiver will receive any signal from the
|
so *generally* weak references are not suitable as
|
||||||
indicated sender (which might also be Any, but is not
|
receivers, though some use might be found for the
|
||||||
necessarily Any).
|
facility whereby a higher-level library passes in
|
||||||
|
pre-weakrefed receiver references.
|
||||||
Otherwise must be a hashable Python object other than
|
|
||||||
None (DispatcherError raised on None).
|
signal -- the signal to which the receiver should respond
|
||||||
|
|
||||||
sender -- the sender to which the receiver should respond
|
if Any, receiver will receive any signal from the
|
||||||
|
indicated sender (which might also be Any, but is not
|
||||||
if Any, receiver will receive the indicated signals
|
necessarily Any).
|
||||||
from any sender.
|
|
||||||
|
Otherwise must be a hashable Python object other than
|
||||||
if Anonymous, receiver will only receive indicated
|
None (DispatcherError raised on None).
|
||||||
signals from send/sendExact which do not specify a
|
|
||||||
sender, or specify Anonymous explicitly as the sender.
|
sender -- the sender to which the receiver should respond
|
||||||
|
|
||||||
Otherwise can be any python object.
|
if Any, receiver will receive the indicated signals
|
||||||
|
from any sender.
|
||||||
weak -- whether to use weak references to the receiver
|
|
||||||
By default, the module will attempt to use weak
|
if Anonymous, receiver will only receive indicated
|
||||||
references to the receiver objects. If this parameter
|
signals from send/sendExact which do not specify a
|
||||||
is false, then strong references will be used.
|
sender, or specify Anonymous explicitly as the sender.
|
||||||
|
|
||||||
returns None, may raise DispatcherTypeError
|
Otherwise can be any python object.
|
||||||
"""
|
|
||||||
if signal is None:
|
weak -- whether to use weak references to the receiver
|
||||||
raise errors.DispatcherTypeError(
|
By default, the module will attempt to use weak
|
||||||
'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
|
references to the receiver objects. If this parameter
|
||||||
)
|
is false, then strong references will be used.
|
||||||
if weak:
|
|
||||||
receiver = saferef.safeRef(receiver, onDelete=_removeReceiver)
|
returns None, may raise DispatcherTypeError
|
||||||
senderkey = id(sender)
|
"""
|
||||||
if connections.has_key(senderkey):
|
if signal is None:
|
||||||
signals = connections[senderkey]
|
raise errors.DispatcherTypeError(
|
||||||
else:
|
'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
|
||||||
connections[senderkey] = signals = {}
|
)
|
||||||
# Keep track of senders for cleanup.
|
if weak:
|
||||||
# Is Anonymous something we want to clean up?
|
receiver = saferef.safeRef(receiver, onDelete=_removeReceiver)
|
||||||
if sender not in (None, Anonymous, Any):
|
senderkey = id(sender)
|
||||||
def remove(object, senderkey=senderkey):
|
if connections.has_key(senderkey):
|
||||||
_removeSender(senderkey=senderkey)
|
signals = connections[senderkey]
|
||||||
# Skip objects that can not be weakly referenced, which means
|
else:
|
||||||
# they won't be automatically cleaned up, but that's too bad.
|
connections[senderkey] = signals = {}
|
||||||
try:
|
# Keep track of senders for cleanup.
|
||||||
weakSender = weakref.ref(sender, remove)
|
# Is Anonymous something we want to clean up?
|
||||||
senders[senderkey] = weakSender
|
if sender not in (None, Anonymous, Any):
|
||||||
except:
|
def remove(object, senderkey=senderkey):
|
||||||
pass
|
_removeSender(senderkey=senderkey)
|
||||||
|
# Skip objects that can not be weakly referenced, which means
|
||||||
receiverID = id(receiver)
|
# they won't be automatically cleaned up, but that's too bad.
|
||||||
# get current set, remove any current references to
|
try:
|
||||||
# this receiver in the set, including back-references
|
weakSender = weakref.ref(sender, remove)
|
||||||
if signals.has_key(signal):
|
senders[senderkey] = weakSender
|
||||||
receivers = signals[signal]
|
except:
|
||||||
_removeOldBackRefs(senderkey, signal, receiver, receivers)
|
pass
|
||||||
else:
|
|
||||||
receivers = signals[signal] = []
|
receiverID = id(receiver)
|
||||||
try:
|
# get current set, remove any current references to
|
||||||
current = sendersBack.get( receiverID )
|
# this receiver in the set, including back-references
|
||||||
if current is None:
|
if signals.has_key(signal):
|
||||||
sendersBack[ receiverID ] = current = []
|
receivers = signals[signal]
|
||||||
if senderkey not in current:
|
_removeOldBackRefs(senderkey, signal, receiver, receivers)
|
||||||
current.append(senderkey)
|
else:
|
||||||
except:
|
receivers = signals[signal] = []
|
||||||
pass
|
try:
|
||||||
|
current = sendersBack.get( receiverID )
|
||||||
receivers.append(receiver)
|
if current is None:
|
||||||
|
sendersBack[ receiverID ] = current = []
|
||||||
|
if senderkey not in current:
|
||||||
|
current.append(senderkey)
|
||||||
def disconnect(receiver, signal=Any, sender=Any, weak=True):
|
except:
|
||||||
"""Disconnect receiver from sender for signal
|
pass
|
||||||
|
|
||||||
receiver -- the registered receiver to disconnect
|
receivers.append(receiver)
|
||||||
signal -- the registered signal to disconnect
|
|
||||||
sender -- the registered sender to disconnect
|
|
||||||
weak -- the weakref state to disconnect
|
|
||||||
|
def disconnect(receiver, signal=Any, sender=Any, weak=True):
|
||||||
disconnect reverses the process of connect,
|
"""Disconnect receiver from sender for signal
|
||||||
the semantics for the individual elements are
|
|
||||||
logically equivalent to a tuple of
|
receiver -- the registered receiver to disconnect
|
||||||
(receiver, signal, sender, weak) used as a key
|
signal -- the registered signal to disconnect
|
||||||
to be deleted from the internal routing tables.
|
sender -- the registered sender to disconnect
|
||||||
(The actual process is slightly more complex
|
weak -- the weakref state to disconnect
|
||||||
but the semantics are basically the same).
|
|
||||||
|
disconnect reverses the process of connect,
|
||||||
Note:
|
the semantics for the individual elements are
|
||||||
Using disconnect is not required to cleanup
|
logically equivalent to a tuple of
|
||||||
routing when an object is deleted, the framework
|
(receiver, signal, sender, weak) used as a key
|
||||||
will remove routes for deleted objects
|
to be deleted from the internal routing tables.
|
||||||
automatically. It's only necessary to disconnect
|
(The actual process is slightly more complex
|
||||||
if you want to stop routing to a live object.
|
but the semantics are basically the same).
|
||||||
|
|
||||||
returns None, may raise DispatcherTypeError or
|
Note:
|
||||||
DispatcherKeyError
|
Using disconnect is not required to cleanup
|
||||||
"""
|
routing when an object is deleted, the framework
|
||||||
if signal is None:
|
will remove routes for deleted objects
|
||||||
raise errors.DispatcherTypeError(
|
automatically. It's only necessary to disconnect
|
||||||
'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
|
if you want to stop routing to a live object.
|
||||||
)
|
|
||||||
if weak: receiver = saferef.safeRef(receiver)
|
returns None, may raise DispatcherTypeError or
|
||||||
senderkey = id(sender)
|
DispatcherKeyError
|
||||||
try:
|
"""
|
||||||
signals = connections[senderkey]
|
if signal is None:
|
||||||
receivers = signals[signal]
|
raise errors.DispatcherTypeError(
|
||||||
except KeyError:
|
'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
|
||||||
raise errors.DispatcherKeyError(
|
)
|
||||||
"""No receivers found for signal %r from sender %r""" %(
|
if weak: receiver = saferef.safeRef(receiver)
|
||||||
signal,
|
senderkey = id(sender)
|
||||||
sender
|
try:
|
||||||
)
|
signals = connections[senderkey]
|
||||||
)
|
receivers = signals[signal]
|
||||||
try:
|
except KeyError:
|
||||||
# also removes from receivers
|
raise errors.DispatcherKeyError(
|
||||||
_removeOldBackRefs(senderkey, signal, receiver, receivers)
|
"""No receivers found for signal %r from sender %r""" %(
|
||||||
except ValueError:
|
signal,
|
||||||
raise errors.DispatcherKeyError(
|
sender
|
||||||
"""No connection to receiver %s for signal %s from sender %s""" %(
|
)
|
||||||
receiver,
|
)
|
||||||
signal,
|
try:
|
||||||
sender
|
# also removes from receivers
|
||||||
)
|
_removeOldBackRefs(senderkey, signal, receiver, receivers)
|
||||||
)
|
except ValueError:
|
||||||
_cleanupConnections(senderkey, signal)
|
raise errors.DispatcherKeyError(
|
||||||
|
"""No connection to receiver %s for signal %s from sender %s""" %(
|
||||||
def getReceivers( sender = Any, signal = Any ):
|
receiver,
|
||||||
"""Get list of receivers from global tables
|
signal,
|
||||||
|
sender
|
||||||
This utility function allows you to retrieve the
|
)
|
||||||
raw list of receivers from the connections table
|
)
|
||||||
for the given sender and signal pair.
|
_cleanupConnections(senderkey, signal)
|
||||||
|
|
||||||
Note:
|
def getReceivers( sender = Any, signal = Any ):
|
||||||
there is no guarantee that this is the actual list
|
"""Get list of receivers from global tables
|
||||||
stored in the connections table, so the value
|
|
||||||
should be treated as a simple iterable/truth value
|
This utility function allows you to retrieve the
|
||||||
rather than, for instance a list to which you
|
raw list of receivers from the connections table
|
||||||
might append new records.
|
for the given sender and signal pair.
|
||||||
|
|
||||||
Normally you would use liveReceivers( getReceivers( ...))
|
Note:
|
||||||
to retrieve the actual receiver objects as an iterable
|
there is no guarantee that this is the actual list
|
||||||
object.
|
stored in the connections table, so the value
|
||||||
"""
|
should be treated as a simple iterable/truth value
|
||||||
try:
|
rather than, for instance a list to which you
|
||||||
return connections[id(sender)][signal]
|
might append new records.
|
||||||
except KeyError:
|
|
||||||
return []
|
Normally you would use liveReceivers( getReceivers( ...))
|
||||||
|
to retrieve the actual receiver objects as an iterable
|
||||||
def liveReceivers(receivers):
|
object.
|
||||||
"""Filter sequence of receivers to get resolved, live receivers
|
"""
|
||||||
|
try:
|
||||||
This is a generator which will iterate over
|
return connections[id(sender)][signal]
|
||||||
the passed sequence, checking for weak references
|
except KeyError:
|
||||||
and resolving them, then returning all live
|
return []
|
||||||
receivers.
|
|
||||||
"""
|
def liveReceivers(receivers):
|
||||||
for receiver in receivers:
|
"""Filter sequence of receivers to get resolved, live receivers
|
||||||
if isinstance( receiver, WEAKREF_TYPES):
|
|
||||||
# Dereference the weak reference.
|
This is a generator which will iterate over
|
||||||
receiver = receiver()
|
the passed sequence, checking for weak references
|
||||||
if receiver is not None:
|
and resolving them, then returning all live
|
||||||
yield receiver
|
receivers.
|
||||||
else:
|
"""
|
||||||
yield receiver
|
for receiver in receivers:
|
||||||
|
if isinstance( receiver, WEAKREF_TYPES):
|
||||||
|
# Dereference the weak reference.
|
||||||
|
receiver = receiver()
|
||||||
def getAllReceivers( sender = Any, signal = Any ):
|
if receiver is not None:
|
||||||
"""Get list of all receivers from global tables
|
yield receiver
|
||||||
|
else:
|
||||||
This gets all receivers which should receive
|
yield receiver
|
||||||
the given signal from sender, each receiver should
|
|
||||||
be produced only once by the resulting generator
|
|
||||||
"""
|
|
||||||
receivers = {}
|
def getAllReceivers( sender = Any, signal = Any ):
|
||||||
for set in (
|
"""Get list of all receivers from global tables
|
||||||
# Get receivers that receive *this* signal from *this* sender.
|
|
||||||
getReceivers( sender, signal ),
|
This gets all receivers which should receive
|
||||||
# Add receivers that receive *any* signal from *this* sender.
|
the given signal from sender, each receiver should
|
||||||
getReceivers( sender, Any ),
|
be produced only once by the resulting generator
|
||||||
# Add receivers that receive *this* signal from *any* sender.
|
"""
|
||||||
getReceivers( Any, signal ),
|
receivers = {}
|
||||||
# Add receivers that receive *any* signal from *any* sender.
|
for set in (
|
||||||
getReceivers( Any, Any ),
|
# Get receivers that receive *this* signal from *this* sender.
|
||||||
):
|
getReceivers( sender, signal ),
|
||||||
for receiver in set:
|
# Add receivers that receive *any* signal from *this* sender.
|
||||||
if receiver: # filter out dead instance-method weakrefs
|
getReceivers( sender, Any ),
|
||||||
try:
|
# Add receivers that receive *this* signal from *any* sender.
|
||||||
if not receivers.has_key( receiver ):
|
getReceivers( Any, signal ),
|
||||||
receivers[receiver] = 1
|
# Add receivers that receive *any* signal from *any* sender.
|
||||||
yield receiver
|
getReceivers( Any, Any ),
|
||||||
except TypeError:
|
):
|
||||||
# dead weakrefs raise TypeError on hash...
|
for receiver in set:
|
||||||
pass
|
if receiver: # filter out dead instance-method weakrefs
|
||||||
|
try:
|
||||||
def send(signal=Any, sender=Anonymous, *arguments, **named):
|
if not receivers.has_key( receiver ):
|
||||||
"""Send signal from sender to all connected receivers.
|
receivers[receiver] = 1
|
||||||
|
yield receiver
|
||||||
signal -- (hashable) signal value, see connect for details
|
except TypeError:
|
||||||
|
# dead weakrefs raise TypeError on hash...
|
||||||
sender -- the sender of the signal
|
pass
|
||||||
|
|
||||||
if Any, only receivers registered for Any will receive
|
def send(signal=Any, sender=Anonymous, *arguments, **named):
|
||||||
the message.
|
"""Send signal from sender to all connected receivers.
|
||||||
|
|
||||||
if Anonymous, only receivers registered to receive
|
signal -- (hashable) signal value, see connect for details
|
||||||
messages from Anonymous or Any will receive the message
|
|
||||||
|
sender -- the sender of the signal
|
||||||
Otherwise can be any python object (normally one
|
|
||||||
registered with a connect if you actually want
|
if Any, only receivers registered for Any will receive
|
||||||
something to occur).
|
the message.
|
||||||
|
|
||||||
arguments -- positional arguments which will be passed to
|
if Anonymous, only receivers registered to receive
|
||||||
*all* receivers. Note that this may raise TypeErrors
|
messages from Anonymous or Any will receive the message
|
||||||
if the receivers do not allow the particular arguments.
|
|
||||||
Note also that arguments are applied before named
|
Otherwise can be any python object (normally one
|
||||||
arguments, so they should be used with care.
|
registered with a connect if you actually want
|
||||||
|
something to occur).
|
||||||
named -- named arguments which will be filtered according
|
|
||||||
to the parameters of the receivers to only provide those
|
arguments -- positional arguments which will be passed to
|
||||||
acceptable to the receiver.
|
*all* receivers. Note that this may raise TypeErrors
|
||||||
|
if the receivers do not allow the particular arguments.
|
||||||
Return a list of tuple pairs [(receiver, response), ... ]
|
Note also that arguments are applied before named
|
||||||
|
arguments, so they should be used with care.
|
||||||
if any receiver raises an error, the error propagates back
|
|
||||||
through send, terminating the dispatch loop, so it is quite
|
named -- named arguments which will be filtered according
|
||||||
possible to not have all receivers called if a raises an
|
to the parameters of the receivers to only provide those
|
||||||
error.
|
acceptable to the receiver.
|
||||||
"""
|
|
||||||
# Call each receiver with whatever arguments it can accept.
|
Return a list of tuple pairs [(receiver, response), ... ]
|
||||||
# Return a list of tuple pairs [(receiver, response), ... ].
|
|
||||||
responses = []
|
if any receiver raises an error, the error propagates back
|
||||||
for receiver in liveReceivers(getAllReceivers(sender, signal)):
|
through send, terminating the dispatch loop, so it is quite
|
||||||
response = robustapply.robustApply(
|
possible to not have all receivers called if a raises an
|
||||||
receiver,
|
error.
|
||||||
signal=signal,
|
"""
|
||||||
sender=sender,
|
# Call each receiver with whatever arguments it can accept.
|
||||||
*arguments,
|
# Return a list of tuple pairs [(receiver, response), ... ].
|
||||||
**named
|
responses = []
|
||||||
)
|
for receiver in liveReceivers(getAllReceivers(sender, signal)):
|
||||||
responses.append((receiver, response))
|
response = robustapply.robustApply(
|
||||||
return responses
|
receiver,
|
||||||
def sendExact( signal=Any, sender=Anonymous, *arguments, **named ):
|
signal=signal,
|
||||||
"""Send signal only to those receivers registered for exact message
|
sender=sender,
|
||||||
|
*arguments,
|
||||||
sendExact allows for avoiding Any/Anonymous registered
|
**named
|
||||||
handlers, sending only to those receivers explicitly
|
)
|
||||||
registered for a particular signal on a particular
|
responses.append((receiver, response))
|
||||||
sender.
|
return responses
|
||||||
"""
|
def sendExact( signal=Any, sender=Anonymous, *arguments, **named ):
|
||||||
responses = []
|
"""Send signal only to those receivers registered for exact message
|
||||||
for receiver in liveReceivers(getReceivers(sender, signal)):
|
|
||||||
response = robustapply.robustApply(
|
sendExact allows for avoiding Any/Anonymous registered
|
||||||
receiver,
|
handlers, sending only to those receivers explicitly
|
||||||
signal=signal,
|
registered for a particular signal on a particular
|
||||||
sender=sender,
|
sender.
|
||||||
*arguments,
|
"""
|
||||||
**named
|
responses = []
|
||||||
)
|
for receiver in liveReceivers(getReceivers(sender, signal)):
|
||||||
responses.append((receiver, response))
|
response = robustapply.robustApply(
|
||||||
return responses
|
receiver,
|
||||||
|
signal=signal,
|
||||||
|
sender=sender,
|
||||||
def _removeReceiver(receiver):
|
*arguments,
|
||||||
"""Remove receiver from connections."""
|
**named
|
||||||
backKey = id(receiver)
|
)
|
||||||
for senderkey in sendersBack.get(backKey,()):
|
responses.append((receiver, response))
|
||||||
try:
|
return responses
|
||||||
signals = connections[senderkey].keys()
|
|
||||||
except KeyError,err:
|
|
||||||
pass
|
def _removeReceiver(receiver):
|
||||||
else:
|
"""Remove receiver from connections."""
|
||||||
for signal in signals:
|
if not sendersBack:
|
||||||
try:
|
# During module cleanup the mapping will be replaced with None
|
||||||
receivers = connections[senderkey][signal]
|
return False
|
||||||
except KeyError:
|
backKey = id(receiver)
|
||||||
pass
|
for senderkey in sendersBack.get(backKey,()):
|
||||||
else:
|
try:
|
||||||
try:
|
signals = connections[senderkey].keys()
|
||||||
receivers.remove( receiver )
|
except KeyError,err:
|
||||||
except Exception, err:
|
pass
|
||||||
pass
|
else:
|
||||||
_cleanupConnections(senderkey, signal)
|
for signal in signals:
|
||||||
try:
|
try:
|
||||||
del sendersBack[ backKey ]
|
receivers = connections[senderkey][signal]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
else:
|
||||||
def _cleanupConnections(senderkey, signal):
|
try:
|
||||||
"""Delete any empty signals for senderkey. Delete senderkey if empty."""
|
receivers.remove( receiver )
|
||||||
try:
|
except Exception, err:
|
||||||
receivers = connections[senderkey][signal]
|
pass
|
||||||
except:
|
_cleanupConnections(senderkey, signal)
|
||||||
pass
|
try:
|
||||||
else:
|
del sendersBack[ backKey ]
|
||||||
if not receivers:
|
except KeyError:
|
||||||
# No more connected receivers. Therefore, remove the signal.
|
pass
|
||||||
try:
|
|
||||||
signals = connections[senderkey]
|
def _cleanupConnections(senderkey, signal):
|
||||||
except KeyError:
|
"""Delete any empty signals for senderkey. Delete senderkey if empty."""
|
||||||
pass
|
try:
|
||||||
else:
|
receivers = connections[senderkey][signal]
|
||||||
del signals[signal]
|
except:
|
||||||
if not signals:
|
pass
|
||||||
# No more signal connections. Therefore, remove the sender.
|
else:
|
||||||
_removeSender(senderkey)
|
if not receivers:
|
||||||
|
# No more connected receivers. Therefore, remove the signal.
|
||||||
def _removeSender(senderkey):
|
try:
|
||||||
"""Remove senderkey from connections."""
|
signals = connections[senderkey]
|
||||||
_removeBackrefs(senderkey)
|
except KeyError:
|
||||||
try:
|
pass
|
||||||
del connections[senderkey]
|
else:
|
||||||
except KeyError:
|
del signals[signal]
|
||||||
pass
|
if not signals:
|
||||||
# Senderkey will only be in senders dictionary if sender
|
# No more signal connections. Therefore, remove the sender.
|
||||||
# could be weakly referenced.
|
_removeSender(senderkey)
|
||||||
try: del senders[senderkey]
|
|
||||||
except: pass
|
def _removeSender(senderkey):
|
||||||
|
"""Remove senderkey from connections."""
|
||||||
|
_removeBackrefs(senderkey)
|
||||||
def _removeBackrefs( senderkey):
|
try:
|
||||||
"""Remove all back-references to this senderkey"""
|
del connections[senderkey]
|
||||||
try:
|
except KeyError:
|
||||||
signals = connections[senderkey]
|
pass
|
||||||
except KeyError:
|
# Senderkey will only be in senders dictionary if sender
|
||||||
signals = None
|
# could be weakly referenced.
|
||||||
else:
|
try:
|
||||||
items = signals.items()
|
del senders[senderkey]
|
||||||
def allReceivers( ):
|
except:
|
||||||
for signal,set in items:
|
pass
|
||||||
for item in set:
|
|
||||||
yield item
|
|
||||||
for receiver in allReceivers():
|
def _removeBackrefs( senderkey):
|
||||||
_killBackref( receiver, senderkey )
|
"""Remove all back-references to this senderkey"""
|
||||||
|
try:
|
||||||
def _removeOldBackRefs(senderkey, signal, receiver, receivers):
|
signals = connections[senderkey]
|
||||||
"""Kill old sendersBack references from receiver
|
except KeyError:
|
||||||
|
signals = None
|
||||||
This guards against multiple registration of the same
|
else:
|
||||||
receiver for a given signal and sender leaking memory
|
items = signals.items()
|
||||||
as old back reference records build up.
|
def allReceivers( ):
|
||||||
|
for signal,set in items:
|
||||||
Also removes old receiver instance from receivers
|
for item in set:
|
||||||
"""
|
yield item
|
||||||
try:
|
for receiver in allReceivers():
|
||||||
index = receivers.index(receiver)
|
_killBackref( receiver, senderkey )
|
||||||
# need to scan back references here and remove senderkey
|
|
||||||
except ValueError:
|
def _removeOldBackRefs(senderkey, signal, receiver, receivers):
|
||||||
return False
|
"""Kill old sendersBack references from receiver
|
||||||
else:
|
|
||||||
oldReceiver = receivers[index]
|
This guards against multiple registration of the same
|
||||||
del receivers[index]
|
receiver for a given signal and sender leaking memory
|
||||||
found = 0
|
as old back reference records build up.
|
||||||
signals = connections.get(signal)
|
|
||||||
if signals is not None:
|
Also removes old receiver instance from receivers
|
||||||
for sig,recs in connections.get(signal,{}).iteritems():
|
"""
|
||||||
if sig != signal:
|
try:
|
||||||
for rec in recs:
|
index = receivers.index(receiver)
|
||||||
if rec is oldReceiver:
|
# need to scan back references here and remove senderkey
|
||||||
found = 1
|
except ValueError:
|
||||||
break
|
return False
|
||||||
if not found:
|
else:
|
||||||
_killBackref( oldReceiver, senderkey )
|
oldReceiver = receivers[index]
|
||||||
return True
|
del receivers[index]
|
||||||
return False
|
found = 0
|
||||||
|
signals = connections.get(signal)
|
||||||
|
if signals is not None:
|
||||||
def _killBackref( receiver, senderkey ):
|
for sig,recs in connections.get(signal,{}).iteritems():
|
||||||
"""Do the actual removal of back reference from receiver to senderkey"""
|
if sig != signal:
|
||||||
receiverkey = id(receiver)
|
for rec in recs:
|
||||||
set = sendersBack.get( receiverkey, () )
|
if rec is oldReceiver:
|
||||||
while senderkey in set:
|
found = 1
|
||||||
try:
|
break
|
||||||
set.remove( senderkey )
|
if not found:
|
||||||
except:
|
_killBackref( oldReceiver, senderkey )
|
||||||
break
|
return True
|
||||||
if not set:
|
return False
|
||||||
try:
|
|
||||||
del sendersBack[ receiverkey ]
|
|
||||||
except KeyError:
|
def _killBackref( receiver, senderkey ):
|
||||||
pass
|
"""Do the actual removal of back reference from receiver to senderkey"""
|
||||||
return True
|
receiverkey = id(receiver)
|
||||||
|
set = sendersBack.get( receiverkey, () )
|
||||||
|
while senderkey in set:
|
||||||
|
try:
|
||||||
|
set.remove( senderkey )
|
||||||
|
except:
|
||||||
|
break
|
||||||
|
if not set:
|
||||||
|
try:
|
||||||
|
del sendersBack[ receiverkey ]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
return True
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
"""Error types for dispatcher mechanism
|
"""Error types for dispatcher mechanism
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class DispatcherError(Exception):
|
class DispatcherError(Exception):
|
||||||
"""Base class for all Dispatcher errors"""
|
"""Base class for all Dispatcher errors"""
|
||||||
class DispatcherKeyError(KeyError, DispatcherError):
|
class DispatcherKeyError(KeyError, DispatcherError):
|
||||||
"""Error raised when unknown (sender,signal) set specified"""
|
"""Error raised when unknown (sender,signal) set specified"""
|
||||||
class DispatcherTypeError(TypeError, DispatcherError):
|
class DispatcherTypeError(TypeError, DispatcherError):
|
||||||
"""Error raised when inappropriate signal-type specified (None)"""
|
"""Error raised when inappropriate signal-type specified (None)"""
|
||||||
|
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
PyDispatcher License
|
PyDispatcher License
|
||||||
|
|
||||||
Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors
|
Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions
|
modification, are permitted provided that the following conditions
|
||||||
are met:
|
are met:
|
||||||
|
|
||||||
Redistributions of source code must retain the above copyright
|
Redistributions of source code must retain the above copyright
|
||||||
notice, this list of conditions and the following disclaimer.
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
Redistributions in binary form must reproduce the above
|
Redistributions in binary form must reproduce the above
|
||||||
copyright notice, this list of conditions and the following
|
copyright notice, this list of conditions and the following
|
||||||
disclaimer in the documentation and/or other materials
|
disclaimer in the documentation and/or other materials
|
||||||
provided with the distribution.
|
provided with the distribution.
|
||||||
|
|
||||||
The name of Patrick K. O'Brien, or the name of any Contributor,
|
The name of Patrick K. O'Brien, or the name of any Contributor,
|
||||||
may not be used to endorse or promote products derived from this
|
may not be used to endorse or promote products derived from this
|
||||||
software without specific prior written permission.
|
software without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
57
django/dispatch/robust.py
Normal file
57
django/dispatch/robust.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
"""Module implementing error-catching version of send (sendRobust)"""
|
||||||
|
from django.dispatch.dispatcher import Any, Anonymous, liveReceivers, getAllReceivers
|
||||||
|
from django.dispatch.robustapply import robustApply
|
||||||
|
|
||||||
|
def sendRobust(
|
||||||
|
signal=Any,
|
||||||
|
sender=Anonymous,
|
||||||
|
*arguments, **named
|
||||||
|
):
|
||||||
|
"""Send signal from sender to all connected receivers catching errors
|
||||||
|
|
||||||
|
signal -- (hashable) signal value, see connect for details
|
||||||
|
|
||||||
|
sender -- the sender of the signal
|
||||||
|
|
||||||
|
if Any, only receivers registered for Any will receive
|
||||||
|
the message.
|
||||||
|
|
||||||
|
if Anonymous, only receivers registered to receive
|
||||||
|
messages from Anonymous or Any will receive the message
|
||||||
|
|
||||||
|
Otherwise can be any python object (normally one
|
||||||
|
registered with a connect if you actually want
|
||||||
|
something to occur).
|
||||||
|
|
||||||
|
arguments -- positional arguments which will be passed to
|
||||||
|
*all* receivers. Note that this may raise TypeErrors
|
||||||
|
if the receivers do not allow the particular arguments.
|
||||||
|
Note also that arguments are applied before named
|
||||||
|
arguments, so they should be used with care.
|
||||||
|
|
||||||
|
named -- named arguments which will be filtered according
|
||||||
|
to the parameters of the receivers to only provide those
|
||||||
|
acceptable to the receiver.
|
||||||
|
|
||||||
|
Return a list of tuple pairs [(receiver, response), ... ]
|
||||||
|
|
||||||
|
if any receiver raises an error (specifically any subclass of Exception),
|
||||||
|
the error instance is returned as the result for that receiver.
|
||||||
|
"""
|
||||||
|
# Call each receiver with whatever arguments it can accept.
|
||||||
|
# Return a list of tuple pairs [(receiver, response), ... ].
|
||||||
|
responses = []
|
||||||
|
for receiver in liveReceivers(getAllReceivers(sender, signal)):
|
||||||
|
try:
|
||||||
|
response = robustApply(
|
||||||
|
receiver,
|
||||||
|
signal=signal,
|
||||||
|
sender=sender,
|
||||||
|
*arguments,
|
||||||
|
**named
|
||||||
|
)
|
||||||
|
except Exception, err:
|
||||||
|
responses.append((receiver, err))
|
||||||
|
else:
|
||||||
|
responses.append((receiver, response))
|
||||||
|
return responses
|
@ -1,49 +1,49 @@
|
|||||||
"""Robust apply mechanism
|
"""Robust apply mechanism
|
||||||
|
|
||||||
Provides a function "call", which can sort out
|
Provides a function "call", which can sort out
|
||||||
what arguments a given callable object can take,
|
what arguments a given callable object can take,
|
||||||
and subset the given arguments to match only
|
and subset the given arguments to match only
|
||||||
those which are acceptable.
|
those which are acceptable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def function( receiver ):
|
def function( receiver ):
|
||||||
"""Get function-like callable object for given receiver
|
"""Get function-like callable object for given receiver
|
||||||
|
|
||||||
returns (function_or_method, codeObject, fromMethod)
|
returns (function_or_method, codeObject, fromMethod)
|
||||||
|
|
||||||
If fromMethod is true, then the callable already
|
If fromMethod is true, then the callable already
|
||||||
has its first argument bound
|
has its first argument bound
|
||||||
"""
|
"""
|
||||||
if hasattr(receiver, '__call__'):
|
if hasattr(receiver, '__call__'):
|
||||||
# receiver is a class instance; assume it is callable.
|
# receiver is a class instance; assume it is callable.
|
||||||
# Reassign receiver to the actual method that will be called.
|
# Reassign receiver to the actual method that will be called.
|
||||||
if hasattr( receiver.__call__, 'im_func') or hasattr( receiver.__call__, 'im_code'):
|
if hasattr( receiver.__call__, 'im_func') or hasattr( receiver.__call__, 'im_code'):
|
||||||
receiver = receiver.__call__
|
receiver = receiver.__call__
|
||||||
if hasattr( receiver, 'im_func' ):
|
if hasattr( receiver, 'im_func' ):
|
||||||
# an instance-method...
|
# an instance-method...
|
||||||
return receiver, receiver.im_func.func_code, 1
|
return receiver, receiver.im_func.func_code, 1
|
||||||
elif not hasattr( receiver, 'func_code'):
|
elif not hasattr( receiver, 'func_code'):
|
||||||
raise ValueError('unknown reciever type %s %s'%(receiver, type(receiver)))
|
raise ValueError('unknown reciever type %s %s'%(receiver, type(receiver)))
|
||||||
return receiver, receiver.func_code, 0
|
return receiver, receiver.func_code, 0
|
||||||
|
|
||||||
def robustApply(receiver, *arguments, **named):
|
def robustApply(receiver, *arguments, **named):
|
||||||
"""Call receiver with arguments and an appropriate subset of named
|
"""Call receiver with arguments and an appropriate subset of named
|
||||||
"""
|
"""
|
||||||
receiver, codeObject, startIndex = function( receiver )
|
receiver, codeObject, startIndex = function( receiver )
|
||||||
acceptable = codeObject.co_varnames[startIndex+len(arguments):codeObject.co_argcount]
|
acceptable = codeObject.co_varnames[startIndex+len(arguments):codeObject.co_argcount]
|
||||||
for name in codeObject.co_varnames[startIndex:startIndex+len(arguments)]:
|
for name in codeObject.co_varnames[startIndex:startIndex+len(arguments)]:
|
||||||
if named.has_key( name ):
|
if named.has_key( name ):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"""Argument %r specified both positionally and as a keyword for calling %r"""% (
|
"""Argument %r specified both positionally and as a keyword for calling %r"""% (
|
||||||
name, receiver,
|
name, receiver,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if not (codeObject.co_flags & 8):
|
if not (codeObject.co_flags & 8):
|
||||||
# fc does not have a **kwds type parameter, therefore
|
# fc does not have a **kwds type parameter, therefore
|
||||||
# remove unacceptable arguments.
|
# remove unacceptable arguments.
|
||||||
for arg in named.keys():
|
for arg in named.keys():
|
||||||
if arg not in acceptable:
|
if arg not in acceptable:
|
||||||
del named[arg]
|
del named[arg]
|
||||||
return receiver(*arguments, **named)
|
return receiver(*arguments, **named)
|
||||||
|
|
||||||
|
|
@ -1,158 +1,165 @@
|
|||||||
"""Refactored "safe reference" from dispatcher.py"""
|
"""Refactored "safe reference" from dispatcher.py"""
|
||||||
import weakref, traceback
|
import weakref, traceback
|
||||||
|
|
||||||
def safeRef(target, onDelete = None):
|
def safeRef(target, onDelete = None):
|
||||||
"""Return a *safe* weak reference to a callable target
|
"""Return a *safe* weak reference to a callable target
|
||||||
|
|
||||||
target -- the object to be weakly referenced, if it's a
|
target -- the object to be weakly referenced, if it's a
|
||||||
bound method reference, will create a BoundMethodWeakref,
|
bound method reference, will create a BoundMethodWeakref,
|
||||||
otherwise creates a simple weakref.
|
otherwise creates a simple weakref.
|
||||||
onDelete -- if provided, will have a hard reference stored
|
onDelete -- if provided, will have a hard reference stored
|
||||||
to the callable to be called after the safe reference
|
to the callable to be called after the safe reference
|
||||||
goes out of scope with the reference object, (either a
|
goes out of scope with the reference object, (either a
|
||||||
weakref or a BoundMethodWeakref) as argument.
|
weakref or a BoundMethodWeakref) as argument.
|
||||||
"""
|
"""
|
||||||
if hasattr(target, 'im_self'):
|
if hasattr(target, 'im_self'):
|
||||||
if target.im_self is not None:
|
if target.im_self is not None:
|
||||||
# Turn a bound method into a BoundMethodWeakref instance.
|
# Turn a bound method into a BoundMethodWeakref instance.
|
||||||
# Keep track of these instances for lookup by disconnect().
|
# Keep track of these instances for lookup by disconnect().
|
||||||
assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,)
|
assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,)
|
||||||
reference = BoundMethodWeakref(
|
reference = BoundMethodWeakref(
|
||||||
target=target,
|
target=target,
|
||||||
onDelete=onDelete
|
onDelete=onDelete
|
||||||
)
|
)
|
||||||
return reference
|
return reference
|
||||||
if callable(onDelete):
|
if callable(onDelete):
|
||||||
return weakref.ref(target, onDelete)
|
return weakref.ref(target, onDelete)
|
||||||
else:
|
else:
|
||||||
return weakref.ref( target )
|
return weakref.ref( target )
|
||||||
|
|
||||||
class BoundMethodWeakref(object):
|
class BoundMethodWeakref(object):
|
||||||
"""'Safe' and reusable weak references to instance methods
|
"""'Safe' and reusable weak references to instance methods
|
||||||
|
|
||||||
BoundMethodWeakref objects provide a mechanism for
|
BoundMethodWeakref objects provide a mechanism for
|
||||||
referencing a bound method without requiring that the
|
referencing a bound method without requiring that the
|
||||||
method object itself (which is normally a transient
|
method object itself (which is normally a transient
|
||||||
object) is kept alive. Instead, the BoundMethodWeakref
|
object) is kept alive. Instead, the BoundMethodWeakref
|
||||||
object keeps weak references to both the object and the
|
object keeps weak references to both the object and the
|
||||||
function which together define the instance method.
|
function which together define the instance method.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
key -- the identity key for the reference, calculated
|
key -- the identity key for the reference, calculated
|
||||||
by the class's calculateKey method applied to the
|
by the class's calculateKey method applied to the
|
||||||
target instance method
|
target instance method
|
||||||
deletionMethods -- sequence of callable objects taking
|
deletionMethods -- sequence of callable objects taking
|
||||||
single argument, a reference to this object which
|
single argument, a reference to this object which
|
||||||
will be called when *either* the target object or
|
will be called when *either* the target object or
|
||||||
target function is garbage collected (i.e. when
|
target function is garbage collected (i.e. when
|
||||||
this object becomes invalid). These are specified
|
this object becomes invalid). These are specified
|
||||||
as the onDelete parameters of safeRef calls.
|
as the onDelete parameters of safeRef calls.
|
||||||
weakSelf -- weak reference to the target object
|
weakSelf -- weak reference to the target object
|
||||||
weakFunc -- weak reference to the target function
|
weakFunc -- weak reference to the target function
|
||||||
|
|
||||||
Class Attributes:
|
Class Attributes:
|
||||||
_allInstances -- class attribute pointing to all live
|
_allInstances -- class attribute pointing to all live
|
||||||
BoundMethodWeakref objects indexed by the class's
|
BoundMethodWeakref objects indexed by the class's
|
||||||
calculateKey(target) method applied to the target
|
calculateKey(target) method applied to the target
|
||||||
objects. This weak value dictionary is used to
|
objects. This weak value dictionary is used to
|
||||||
short-circuit creation so that multiple references
|
short-circuit creation so that multiple references
|
||||||
to the same (object, function) pair produce the
|
to the same (object, function) pair produce the
|
||||||
same BoundMethodWeakref instance.
|
same BoundMethodWeakref instance.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_allInstances = weakref.WeakValueDictionary()
|
_allInstances = weakref.WeakValueDictionary()
|
||||||
def __new__( cls, target, onDelete=None, *arguments,**named ):
|
def __new__( cls, target, onDelete=None, *arguments,**named ):
|
||||||
"""Create new instance or return current instance
|
"""Create new instance or return current instance
|
||||||
|
|
||||||
Basically this method of construction allows us to
|
Basically this method of construction allows us to
|
||||||
short-circuit creation of references to already-
|
short-circuit creation of references to already-
|
||||||
referenced instance methods. The key corresponding
|
referenced instance methods. The key corresponding
|
||||||
to the target is calculated, and if there is already
|
to the target is calculated, and if there is already
|
||||||
an existing reference, that is returned, with its
|
an existing reference, that is returned, with its
|
||||||
deletionMethods attribute updated. Otherwise the
|
deletionMethods attribute updated. Otherwise the
|
||||||
new instance is created and registered in the table
|
new instance is created and registered in the table
|
||||||
of already-referenced methods.
|
of already-referenced methods.
|
||||||
"""
|
"""
|
||||||
key = cls.calculateKey(target)
|
key = cls.calculateKey(target)
|
||||||
current =cls._allInstances.get(key)
|
current =cls._allInstances.get(key)
|
||||||
if current is not None:
|
if current is not None:
|
||||||
current.deletionMethods.append( onDelete)
|
current.deletionMethods.append( onDelete)
|
||||||
return current
|
return current
|
||||||
else:
|
else:
|
||||||
base = super( BoundMethodWeakref, cls).__new__( cls )
|
base = super( BoundMethodWeakref, cls).__new__( cls )
|
||||||
cls._allInstances[key] = base
|
cls._allInstances[key] = base
|
||||||
base.__init__( target, onDelete, *arguments,**named)
|
base.__init__( target, onDelete, *arguments,**named)
|
||||||
return base
|
return base
|
||||||
def __init__(self, target, onDelete=None):
|
def __init__(self, target, onDelete=None):
|
||||||
"""Return a weak-reference-like instance for a bound method
|
"""Return a weak-reference-like instance for a bound method
|
||||||
|
|
||||||
target -- the instance-method target for the weak
|
target -- the instance-method target for the weak
|
||||||
reference, must have im_self and im_func attributes
|
reference, must have im_self and im_func attributes
|
||||||
and be reconstructable via:
|
and be reconstructable via:
|
||||||
target.im_func.__get__( target.im_self )
|
target.im_func.__get__( target.im_self )
|
||||||
which is true of built-in instance methods.
|
which is true of built-in instance methods.
|
||||||
onDelete -- optional callback which will be called
|
onDelete -- optional callback which will be called
|
||||||
when this weak reference ceases to be valid
|
when this weak reference ceases to be valid
|
||||||
(i.e. either the object or the function is garbage
|
(i.e. either the object or the function is garbage
|
||||||
collected). Should take a single argument,
|
collected). Should take a single argument,
|
||||||
which will be passed a pointer to this object.
|
which will be passed a pointer to this object.
|
||||||
"""
|
"""
|
||||||
def remove(weak, self=self):
|
def remove(weak, self=self):
|
||||||
"""Set self.isDead to true when method or instance is destroyed"""
|
"""Set self.isDead to true when method or instance is destroyed"""
|
||||||
methods = self.deletionMethods[:]
|
methods = self.deletionMethods[:]
|
||||||
del self.deletionMethods[:]
|
del self.deletionMethods[:]
|
||||||
try:
|
try:
|
||||||
del self.__class__._allInstances[ self.key ]
|
del self.__class__._allInstances[ self.key ]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
for function in methods:
|
for function in methods:
|
||||||
try:
|
try:
|
||||||
if callable( function ):
|
if callable( function ):
|
||||||
function( self )
|
function( self )
|
||||||
except Exception:
|
except Exception, e:
|
||||||
traceback.print_exc()
|
try:
|
||||||
self.deletionMethods = [onDelete]
|
traceback.print_exc()
|
||||||
self.key = self.calculateKey( target )
|
except AttributeError, err:
|
||||||
self.weakSelf = weakref.ref(target.im_self, remove)
|
print '''Exception during saferef %s cleanup function %s: %s'''%(
|
||||||
self.weakFunc = weakref.ref(target.im_func, remove)
|
self, function, e
|
||||||
def calculateKey( cls, target ):
|
)
|
||||||
"""Calculate the reference key for this reference
|
self.deletionMethods = [onDelete]
|
||||||
|
self.key = self.calculateKey( target )
|
||||||
Currently this is a two-tuple of the id()'s of the
|
self.weakSelf = weakref.ref(target.im_self, remove)
|
||||||
target object and the target function respectively.
|
self.weakFunc = weakref.ref(target.im_func, remove)
|
||||||
"""
|
self.selfName = str(target.im_self)
|
||||||
return (id(target.im_self),id(target.im_func))
|
self.funcName = str(target.im_func.__name__)
|
||||||
calculateKey = classmethod( calculateKey )
|
def calculateKey( cls, target ):
|
||||||
def __str__(self):
|
"""Calculate the reference key for this reference
|
||||||
"""Give a friendly representation of the object"""
|
|
||||||
return """%s( %s.%s )"""%(
|
Currently this is a two-tuple of the id()'s of the
|
||||||
self.__class__.__name__,
|
target object and the target function respectively.
|
||||||
self.weakSelf(),
|
"""
|
||||||
self.weakFunc().__name__,
|
return (id(target.im_self),id(target.im_func))
|
||||||
)
|
calculateKey = classmethod( calculateKey )
|
||||||
__repr__ = __str__
|
def __str__(self):
|
||||||
def __nonzero__( self ):
|
"""Give a friendly representation of the object"""
|
||||||
"""Whether we are still a valid reference"""
|
return """%s( %s.%s )"""%(
|
||||||
return self() is not None
|
self.__class__.__name__,
|
||||||
def __cmp__( self, other ):
|
self.selfName,
|
||||||
"""Compare with another reference"""
|
self.funcName,
|
||||||
if not isinstance (other,self.__class__):
|
)
|
||||||
return cmp( self.__class__, type(other) )
|
__repr__ = __str__
|
||||||
return cmp( self.key, other.key)
|
def __nonzero__( self ):
|
||||||
def __call__(self):
|
"""Whether we are still a valid reference"""
|
||||||
"""Return a strong reference to the bound method
|
return self() is not None
|
||||||
|
def __cmp__( self, other ):
|
||||||
If the target cannot be retrieved, then will
|
"""Compare with another reference"""
|
||||||
return None, otherwise returns a bound instance
|
if not isinstance (other,self.__class__):
|
||||||
method for our object and function.
|
return cmp( self.__class__, type(other) )
|
||||||
|
return cmp( self.key, other.key)
|
||||||
Note:
|
def __call__(self):
|
||||||
You may call this method any number of times,
|
"""Return a strong reference to the bound method
|
||||||
as it does not invalidate the reference.
|
|
||||||
"""
|
If the target cannot be retrieved, then will
|
||||||
target = self.weakSelf()
|
return None, otherwise returns a bound instance
|
||||||
if target is not None:
|
method for our object and function.
|
||||||
function = self.weakFunc()
|
|
||||||
if function is not None:
|
Note:
|
||||||
return function.__get__(target)
|
You may call this method any number of times,
|
||||||
return None
|
as it does not invalidate the reference.
|
||||||
|
"""
|
||||||
|
target = self.weakSelf()
|
||||||
|
if target is not None:
|
||||||
|
function = self.weakFunc()
|
||||||
|
if function is not None:
|
||||||
|
return function.__get__(target)
|
||||||
|
return None
|
||||||
|
Loading…
x
Reference in New Issue
Block a user