Part 3 of pedant day: replaced all tabs in Django with spaces. Python the way Guido intended it, baby!
git-svn-id: http://code.djangoproject.com/svn/django/trunk@3415 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
a926046ba6
commit
1687b025dc
|
@ -42,9 +42,9 @@
|
|||
|
||||
/* PAGINATOR */
|
||||
.paginator { font-size:11px; padding-top:10px; padding-bottom:10px; line-height:22px; margin:0; border-top:1px solid #ddd; }
|
||||
.paginator a:link, .paginator a:visited { padding:2px 6px; border:solid 1px #ccc; background:white; text-decoration:none; }
|
||||
.paginator a:link, .paginator a:visited { padding:2px 6px; border:solid 1px #ccc; background:white; text-decoration:none; }
|
||||
.paginator a.showall { padding:0 !important; border:none !important; }
|
||||
.paginator a.showall:hover { color:#036 !important; background:transparent !important; }
|
||||
.paginator .end { border-width:2px !important; margin-right:6px; }
|
||||
.paginator .end { border-width:2px !important; margin-right:6px; }
|
||||
.paginator .this-page { padding:2px 6px; font-weight:bold; font-size:13px; vertical-align:top; }
|
||||
.paginator a:hover { color:white; background:#5b80b2; border-color:#036; }
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
form .form-row p { padding-left:0; font-size:11px; }
|
||||
|
||||
/* FORM LABELS */
|
||||
form h4 { margin:0 !important; padding:0 !important; border:none !important; }
|
||||
form h4 { margin:0 !important; padding:0 !important; border:none !important; }
|
||||
label { font-weight:normal !important; color:#666; font-size:12px; }
|
||||
label.inline { margin-left:20px; }
|
||||
.required label, label.required { font-weight:bold !important; color:#333 !important; }
|
||||
.required label, label.required { font-weight:bold !important; color:#333 !important; }
|
||||
|
||||
/* RADIO BUTTONS */
|
||||
form ul.radiolist li { list-style-type:none; }
|
||||
|
|
|
@ -31,7 +31,7 @@ fieldset { margin:0; padding:0; }
|
|||
blockquote { font-size:11px; color:#777; margin-left:2px; padding-left:10px; border-left:5px solid #ddd; }
|
||||
code, pre { font-family:"Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace; background:inherit; color:#666; font-size:11px; }
|
||||
pre.literal-block { margin:10px; background:#eee; padding:6px 8px; }
|
||||
code strong { color:#930; }
|
||||
code strong { color:#930; }
|
||||
hr { clear:both; color:#eee; background-color:#eee; height:1px; border:none; margin:0; padding:0; font-size:1px; line-height:1px; }
|
||||
|
||||
/* TEXT STYLES & MODIFIERS */
|
||||
|
@ -81,7 +81,7 @@ table.orderable tbody tr td:first-child { padding-left:14px; background-image:ur
|
|||
table.orderable-initalized .order-cell, body>tr>td.order-cell { display:none; }
|
||||
|
||||
/* FORM DEFAULTS */
|
||||
input, textarea, select { margin:2px 0; padding:2px 3px; vertical-align:middle; font-family:"Lucida Grande", Verdana, Arial, sans-serif; font-weight:normal; font-size:11px; }
|
||||
input, textarea, select { margin:2px 0; padding:2px 3px; vertical-align:middle; font-family:"Lucida Grande", Verdana, Arial, sans-serif; font-weight:normal; font-size:11px; }
|
||||
textarea { vertical-align:top !important; }
|
||||
input[type=text], input[type=password], textarea, select, .vTextField { border:1px solid #ccc; }
|
||||
|
||||
|
@ -92,7 +92,7 @@ input[type=submit].default, .submit-row input.default { border:2px solid #5b80b2
|
|||
input[type=submit].default:active { background-image:url(../img/admin/default-bg-reverse.gif); background-position:top; }
|
||||
|
||||
/* MODULES */
|
||||
.module { border:1px solid #ccc; margin-bottom:5px; background:white; }
|
||||
.module { border:1px solid #ccc; margin-bottom:5px; background:white; }
|
||||
.module p, .module ul, .module h3, .module h4, .module dl, .module pre { padding-left:10px; padding-right:10px; }
|
||||
.module blockquote { margin-left:12px; }
|
||||
.module ul, .module ol { margin-left:1.5em; }
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#header { width:100%; }
|
||||
#content-main { float:left; width:100%; }
|
||||
#content-related { float:right; width:18em; position:relative; margin-right:-19em; }
|
||||
#footer { clear:both; padding:10px; }
|
||||
#footer { clear:both; padding:10px; }
|
||||
|
||||
/* COLUMN TYPES */
|
||||
.colMS { margin-right:20em !important; }
|
||||
|
@ -16,14 +16,14 @@
|
|||
.dashboard #content { width:500px; }
|
||||
|
||||
/* HEADER */
|
||||
#header { background:#417690; color:#ffc; overflow:hidden; }
|
||||
#header { background:#417690; color:#ffc; overflow:hidden; }
|
||||
#header a:link, #header a:visited { color:white; }
|
||||
#header a:hover { text-decoration:underline; }
|
||||
#branding h1 { padding:0 10px; font-size:18px; margin:8px 0; font-weight:normal; color:#f4f379; }
|
||||
#branding h2 { padding:0 10px; font-size:14px; margin:-8px 0 8px 0; font-weight:normal; color:#ffc; }
|
||||
#user-tools { position:absolute; top:0; right:0; padding:1.2em 10px; font-size:11px; text-align:right; }
|
||||
#user-tools { position:absolute; top:0; right:0; padding:1.2em 10px; font-size:11px; text-align:right; }
|
||||
|
||||
/* SIDEBAR */
|
||||
#content-related h3 { font-size:12px; color:#666; margin-bottom:3px; }
|
||||
#content-related h4 { font-size:11px; }
|
||||
#content-related .module h2 { background:#eee url(../img/admin/nav-bg.gif) bottom left repeat-x; color:#666; }
|
||||
#content-related .module h2 { background:#eee url(../img/admin/nav-bg.gif) bottom left repeat-x; color:#666; }
|
|
@ -16,7 +16,7 @@ th { text-align: right; }
|
|||
|
||||
|
||||
/* layout styles */
|
||||
#user-tools { right:auto; left:0; text-align:left; }
|
||||
#user-tools { right:auto; left:0; text-align:left; }
|
||||
div.breadcrumbs { text-align:right; }
|
||||
#content-main { float:right;}
|
||||
#content-related { float:left; margin-left:-19em; margin-right:auto;}
|
||||
|
|
|
@ -3,83 +3,83 @@
|
|||
// link when the fieldset is visible.
|
||||
|
||||
function findForm(node) {
|
||||
// returns the node of the form containing the given node
|
||||
if (node.tagName.toLowerCase() != 'form') {
|
||||
return findForm(node.parentNode);
|
||||
}
|
||||
return node;
|
||||
// returns the node of the form containing the given node
|
||||
if (node.tagName.toLowerCase() != 'form') {
|
||||
return findForm(node.parentNode);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
var CollapsedFieldsets = {
|
||||
collapse_re: /\bcollapse\b/, // Class of fieldsets that should be dealt with.
|
||||
collapsed_re: /\bcollapsed\b/, // Class that fieldsets get when they're hidden.
|
||||
collapsed_class: 'collapsed',
|
||||
init: function() {
|
||||
var fieldsets = document.getElementsByTagName('fieldset');
|
||||
var collapsed_seen = false;
|
||||
for (var i = 0, fs; fs = fieldsets[i]; i++) {
|
||||
// Collapse this fieldset if it has the correct class, and if it
|
||||
// doesn't have any errors. (Collapsing shouldn't apply in the case
|
||||
// of error messages.)
|
||||
if (fs.className.match(CollapsedFieldsets.collapse_re) && !CollapsedFieldsets.fieldset_has_errors(fs)) {
|
||||
collapsed_seen = true;
|
||||
// Give it an additional class, used by CSS to hide it.
|
||||
fs.className += ' ' + CollapsedFieldsets.collapsed_class;
|
||||
// (<a id="fieldsetcollapser3" class="collapse-toggle" href="#">Show</a>)
|
||||
var collapse_link = document.createElement('a');
|
||||
collapse_link.className = 'collapse-toggle';
|
||||
collapse_link.id = 'fieldsetcollapser' + i;
|
||||
collapse_link.onclick = new Function('CollapsedFieldsets.show('+i+'); return false;');
|
||||
collapse_link.href = '#';
|
||||
collapse_link.innerHTML = gettext('Show');
|
||||
var h2 = fs.getElementsByTagName('h2')[0];
|
||||
h2.appendChild(document.createTextNode(' ('));
|
||||
h2.appendChild(collapse_link);
|
||||
h2.appendChild(document.createTextNode(')'));
|
||||
}
|
||||
}
|
||||
if (collapsed_seen) {
|
||||
// Expand all collapsed fieldsets when form is submitted.
|
||||
addEvent(findForm(document.getElementsByTagName('fieldset')[0]), 'submit', function() { CollapsedFieldsets.uncollapse_all(); });
|
||||
}
|
||||
},
|
||||
fieldset_has_errors: function(fs) {
|
||||
// Returns true if any fields in the fieldset have validation errors.
|
||||
var divs = fs.getElementsByTagName('div');
|
||||
for (var i=0; i<divs.length; i++) {
|
||||
if (divs[i].className.match(/\berror\b/)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
show: function(fieldset_index) {
|
||||
var fs = document.getElementsByTagName('fieldset')[fieldset_index];
|
||||
// Remove the class name that causes the "display: none".
|
||||
fs.className = fs.className.replace(CollapsedFieldsets.collapsed_re, '');
|
||||
// Toggle the "Show" link to a "Hide" link
|
||||
var collapse_link = document.getElementById('fieldsetcollapser' + fieldset_index);
|
||||
collapse_link.onclick = new Function('CollapsedFieldsets.hide('+fieldset_index+'); return false;');
|
||||
collapse_link.innerHTML = gettext('Hide');
|
||||
},
|
||||
hide: function(fieldset_index) {
|
||||
var fs = document.getElementsByTagName('fieldset')[fieldset_index];
|
||||
// Add the class name that causes the "display: none".
|
||||
fs.className += ' ' + CollapsedFieldsets.collapsed_class;
|
||||
// Toggle the "Hide" link to a "Show" link
|
||||
var collapse_link = document.getElementById('fieldsetcollapser' + fieldset_index);
|
||||
collapse_re: /\bcollapse\b/, // Class of fieldsets that should be dealt with.
|
||||
collapsed_re: /\bcollapsed\b/, // Class that fieldsets get when they're hidden.
|
||||
collapsed_class: 'collapsed',
|
||||
init: function() {
|
||||
var fieldsets = document.getElementsByTagName('fieldset');
|
||||
var collapsed_seen = false;
|
||||
for (var i = 0, fs; fs = fieldsets[i]; i++) {
|
||||
// Collapse this fieldset if it has the correct class, and if it
|
||||
// doesn't have any errors. (Collapsing shouldn't apply in the case
|
||||
// of error messages.)
|
||||
if (fs.className.match(CollapsedFieldsets.collapse_re) && !CollapsedFieldsets.fieldset_has_errors(fs)) {
|
||||
collapsed_seen = true;
|
||||
// Give it an additional class, used by CSS to hide it.
|
||||
fs.className += ' ' + CollapsedFieldsets.collapsed_class;
|
||||
// (<a id="fieldsetcollapser3" class="collapse-toggle" href="#">Show</a>)
|
||||
var collapse_link = document.createElement('a');
|
||||
collapse_link.className = 'collapse-toggle';
|
||||
collapse_link.id = 'fieldsetcollapser' + i;
|
||||
collapse_link.onclick = new Function('CollapsedFieldsets.show('+i+'); return false;');
|
||||
collapse_link.href = '#';
|
||||
collapse_link.innerHTML = gettext('Show');
|
||||
var h2 = fs.getElementsByTagName('h2')[0];
|
||||
h2.appendChild(document.createTextNode(' ('));
|
||||
h2.appendChild(collapse_link);
|
||||
h2.appendChild(document.createTextNode(')'));
|
||||
}
|
||||
}
|
||||
if (collapsed_seen) {
|
||||
// Expand all collapsed fieldsets when form is submitted.
|
||||
addEvent(findForm(document.getElementsByTagName('fieldset')[0]), 'submit', function() { CollapsedFieldsets.uncollapse_all(); });
|
||||
}
|
||||
},
|
||||
fieldset_has_errors: function(fs) {
|
||||
// Returns true if any fields in the fieldset have validation errors.
|
||||
var divs = fs.getElementsByTagName('div');
|
||||
for (var i=0; i<divs.length; i++) {
|
||||
if (divs[i].className.match(/\berror\b/)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
show: function(fieldset_index) {
|
||||
var fs = document.getElementsByTagName('fieldset')[fieldset_index];
|
||||
// Remove the class name that causes the "display: none".
|
||||
fs.className = fs.className.replace(CollapsedFieldsets.collapsed_re, '');
|
||||
// Toggle the "Show" link to a "Hide" link
|
||||
var collapse_link = document.getElementById('fieldsetcollapser' + fieldset_index);
|
||||
collapse_link.onclick = new Function('CollapsedFieldsets.hide('+fieldset_index+'); return false;');
|
||||
collapse_link.innerHTML = gettext('Hide');
|
||||
},
|
||||
hide: function(fieldset_index) {
|
||||
var fs = document.getElementsByTagName('fieldset')[fieldset_index];
|
||||
// Add the class name that causes the "display: none".
|
||||
fs.className += ' ' + CollapsedFieldsets.collapsed_class;
|
||||
// Toggle the "Hide" link to a "Show" link
|
||||
var collapse_link = document.getElementById('fieldsetcollapser' + fieldset_index);
|
||||
collapse_link.onclick = new Function('CollapsedFieldsets.show('+fieldset_index+'); return false;');
|
||||
collapse_link.innerHTML = gettext('Show');
|
||||
},
|
||||
collapse_link.innerHTML = gettext('Show');
|
||||
},
|
||||
|
||||
uncollapse_all: function() {
|
||||
var fieldsets = document.getElementsByTagName('fieldset');
|
||||
for (var i=0; i<fieldsets.length; i++) {
|
||||
if (fieldsets[i].className.match(CollapsedFieldsets.collapsed_re)) {
|
||||
CollapsedFieldsets.show(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
uncollapse_all: function() {
|
||||
var fieldsets = document.getElementsByTagName('fieldset');
|
||||
for (var i=0; i<fieldsets.length; i++) {
|
||||
if (fieldsets[i].className.match(CollapsedFieldsets.collapsed_re)) {
|
||||
CollapsedFieldsets.show(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addEvent(window, 'load', CollapsedFieldsets.init);
|
||||
|
|
|
@ -13,17 +13,17 @@
|
|||
{% endif %}
|
||||
<div id="content-main">
|
||||
<form action="{{ app_path }}" method="post" id="login-form">
|
||||
<div class="form-row">
|
||||
<label for="id_username">{% trans 'Username:' %}</label> <input type="text" name="username" id="id_username" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="id_password">{% trans 'Password:' %}</label> <input type="password" name="password" id="id_password" />
|
||||
<input type="hidden" name="this_is_the_login_form" value="1" />
|
||||
<input type="hidden" name="post_data" value="{{ post_data }}" /> {% comment %}<span class="help">{% trans 'Have you <a href="/password_reset/">forgotten your password</a>?' %}</span>{% endcomment %}
|
||||
</div>
|
||||
<div class="submit-row">
|
||||
<label> </label><input type="submit" value="{% trans 'Log in' %}" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="id_username">{% trans 'Username:' %}</label> <input type="text" name="username" id="id_username" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="id_password">{% trans 'Password:' %}</label> <input type="password" name="password" id="id_password" />
|
||||
<input type="hidden" name="this_is_the_login_form" value="1" />
|
||||
<input type="hidden" name="post_data" value="{{ post_data }}" /> {% comment %}<span class="help">{% trans 'Have you <a href="/password_reset/">forgotten your password</a>?' %}</span>{% endcomment %}
|
||||
</div>
|
||||
<div class="submit-row">
|
||||
<label> </label><input type="submit" value="{% trans 'Log in' %}" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
|
|
@ -9,17 +9,17 @@
|
|||
<h1>Documentation</h1>
|
||||
|
||||
<div id="content-main">
|
||||
<h3><a href="tags/">Tags</a></h3>
|
||||
<p>List of all the template tags and their functions.</p>
|
||||
<h3><a href="tags/">Tags</a></h3>
|
||||
<p>List of all the template tags and their functions.</p>
|
||||
|
||||
<h3><a href="filters/">Filters</a></h3>
|
||||
<p>Filters are actions which can be applied to variables in a template to alter the output.</p>
|
||||
<h3><a href="filters/">Filters</a></h3>
|
||||
<p>Filters are actions which can be applied to variables in a template to alter the output.</p>
|
||||
|
||||
<h3><a href="models/">Models</a></h3>
|
||||
<p>Models are descriptions of all the objects in the system and their associated fields. Each model has a list of fields which can be accessed as template variables.</p>
|
||||
<h3><a href="models/">Models</a></h3>
|
||||
<p>Models are descriptions of all the objects in the system and their associated fields. Each model has a list of fields which can be accessed as template variables.</p>
|
||||
|
||||
<h3><a href="views/">Views</a></h3>
|
||||
<p>Each page on the public site is generated by a view. The view defines which template is used to generate the page and which objects are available to that template.</p>
|
||||
<h3><a href="views/">Views</a></h3>
|
||||
<p>Each page on the public site is generated by a view. The view defines which template is used to generate the page and which objects are available to that template.</p>
|
||||
|
||||
<h3><a href="bookmarklets/">Bookmarklets</a></h3>
|
||||
<p>Tools for your browser to quickly access admin functionality.</p>
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
<h1>Documentation</h1>
|
||||
|
||||
<div id="content-main">
|
||||
<h3>The admin documentation system requires Python's <a href="http://docutils.sf.net/">docutils</a> library.</h3>
|
||||
<h3>The admin documentation system requires Python's <a href="http://docutils.sf.net/">docutils</a> library.</h3>
|
||||
|
||||
<p>Please ask your administrators to install <a href="http://docutils.sf.net/">docutils</a>.</p>
|
||||
<p>Please ask your administrators to install <a href="http://docutils.sf.net/">docutils</a>.</p>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -51,7 +51,7 @@ class CommentManager(models.Manager):
|
|||
extra_kwargs.setdefault('select', {})
|
||||
extra_kwargs['select']['_karma_total_good'] = 'SELECT COUNT(*) FROM comments_karmascore, comments_comment WHERE comments_karmascore.comment_id=comments_comment.id AND score=1'
|
||||
extra_kwargs['select']['_karma_total_bad'] = 'SELECT COUNT(*) FROM comments_karmascore, comments_comment WHERE comments_karmascore.comment_id=comments_comment.id AND score=-1'
|
||||
return self.filter(**kwargs).extra(**extra_kwargs)
|
||||
return self.filter(**kwargs).extra(**extra_kwargs)
|
||||
|
||||
def user_is_moderator(self, user):
|
||||
if user.is_superuser:
|
||||
|
|
|
@ -6,24 +6,24 @@ system.
|
|||
|
||||
Module attributes of note:
|
||||
|
||||
Any -- Singleton used to signal either "Any Sender" or
|
||||
"Any Signal". See documentation of the _Any class.
|
||||
Anonymous -- Singleton used to signal "Anonymous Sender"
|
||||
See documentation of the _Anonymous class.
|
||||
Any -- Singleton used to signal either "Any Sender" or
|
||||
"Any Signal". See documentation of the _Any class.
|
||||
Anonymous -- Singleton used to signal "Anonymous Sender"
|
||||
See documentation of the _Anonymous class.
|
||||
|
||||
Internal attributes:
|
||||
WEAKREF_TYPES -- tuple of types/classes which represent
|
||||
weak references to receivers, and thus must be de-
|
||||
referenced on retrieval to retrieve the callable
|
||||
object
|
||||
connections -- { senderkey (id) : { signal : [receivers...]}}
|
||||
senders -- { senderkey (id) : weakref(sender) }
|
||||
used for cleaning up sender references on sender
|
||||
deletion
|
||||
sendersBack -- { receiverkey (id) : [senderkey (id)...] }
|
||||
used for cleaning up receiver references on receiver
|
||||
deletion, (considerably speeds up the cleanup process
|
||||
vs. the original code.)
|
||||
WEAKREF_TYPES -- tuple of types/classes which represent
|
||||
weak references to receivers, and thus must be de-
|
||||
referenced on retrieval to retrieve the callable
|
||||
object
|
||||
connections -- { senderkey (id) : { signal : [receivers...]}}
|
||||
senders -- { senderkey (id) : weakref(sender) }
|
||||
used for cleaning up sender references on sender
|
||||
deletion
|
||||
sendersBack -- { receiverkey (id) : [senderkey (id)...] }
|
||||
used for cleaning up receiver references on receiver
|
||||
deletion, (considerably speeds up the cleanup process
|
||||
vs. the original code.)
|
||||
"""
|
||||
from __future__ import generators
|
||||
import types, weakref
|
||||
|
@ -34,44 +34,44 @@ __cvsid__ = "$Id: dispatcher.py,v 1.9 2005/09/17 04:55:57 mcfletch Exp $"
|
|||
__version__ = "$Revision: 1.9 $"[11:-2]
|
||||
|
||||
try:
|
||||
True
|
||||
True
|
||||
except NameError:
|
||||
True = 1==1
|
||||
False = 1==0
|
||||
True = 1==1
|
||||
False = 1==0
|
||||
|
||||
class _Parameter:
|
||||
"""Used to represent default parameter values."""
|
||||
def __repr__(self):
|
||||
return self.__class__.__name__
|
||||
"""Used to represent default parameter values."""
|
||||
def __repr__(self):
|
||||
return self.__class__.__name__
|
||||
|
||||
class _Any(_Parameter):
|
||||
"""Singleton used to signal either "Any Sender" or "Any Signal"
|
||||
"""Singleton used to signal either "Any Sender" or "Any Signal"
|
||||
|
||||
The Any object can be used with connect, disconnect,
|
||||
send, or sendExact to signal that the parameter given
|
||||
Any should react to all senders/signals, not just
|
||||
a particular sender/signal.
|
||||
"""
|
||||
The Any object can be used with connect, disconnect,
|
||||
send, or sendExact to signal that the parameter given
|
||||
Any should react to all senders/signals, not just
|
||||
a particular sender/signal.
|
||||
"""
|
||||
Any = _Any()
|
||||
|
||||
class _Anonymous(_Parameter):
|
||||
"""Singleton used to signal "Anonymous Sender"
|
||||
"""Singleton used to signal "Anonymous Sender"
|
||||
|
||||
The Anonymous object is used to signal that the sender
|
||||
of a message is not specified (as distinct from being
|
||||
"any sender"). Registering callbacks for Anonymous
|
||||
will only receive messages sent without senders. Sending
|
||||
with anonymous will only send messages to those receivers
|
||||
registered for Any or Anonymous.
|
||||
The Anonymous object is used to signal that the sender
|
||||
of a message is not specified (as distinct from being
|
||||
"any sender"). Registering callbacks for Anonymous
|
||||
will only receive messages sent without senders. Sending
|
||||
with anonymous will only send messages to those receivers
|
||||
registered for Any or Anonymous.
|
||||
|
||||
Note:
|
||||
The default sender for connect is Any, while the
|
||||
default sender for send is Anonymous. This has
|
||||
the effect that if you do not specify any senders
|
||||
in either function then all messages are routed
|
||||
as though there was a single sender (Anonymous)
|
||||
being used everywhere.
|
||||
"""
|
||||
Note:
|
||||
The default sender for connect is Any, while the
|
||||
default sender for send is Anonymous. This has
|
||||
the effect that if you do not specify any senders
|
||||
in either function then all messages are routed
|
||||
as though there was a single sender (Anonymous)
|
||||
being used everywhere.
|
||||
"""
|
||||
Anonymous = _Anonymous()
|
||||
|
||||
WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
|
||||
|
@ -82,416 +82,416 @@ sendersBack = {}
|
|||
|
||||
|
||||
def connect(receiver, signal=Any, sender=Any, weak=True):
|
||||
"""Connect receiver to sender for signal
|
||||
"""Connect receiver to sender for signal
|
||||
|
||||
receiver -- a callable Python object which is to receive
|
||||
messages/signals/events. Receivers must be hashable
|
||||
objects.
|
||||
receiver -- a callable Python object which is to receive
|
||||
messages/signals/events. Receivers must be hashable
|
||||
objects.
|
||||
|
||||
if weak is True, then receiver must be weak-referencable
|
||||
(more precisely saferef.safeRef() must be able to create
|
||||
a reference to the receiver).
|
||||
if weak is True, then receiver must be weak-referencable
|
||||
(more precisely saferef.safeRef() must be able to create
|
||||
a reference to the receiver).
|
||||
|
||||
Receivers are fairly flexible in their specification,
|
||||
as the machinery in the robustApply module takes care
|
||||
of most of the details regarding figuring out appropriate
|
||||
subsets of the sent arguments to apply to a given
|
||||
receiver.
|
||||
Receivers are fairly flexible in their specification,
|
||||
as the machinery in the robustApply module takes care
|
||||
of most of the details regarding figuring out appropriate
|
||||
subsets of the sent arguments to apply to a given
|
||||
receiver.
|
||||
|
||||
Note:
|
||||
if receiver is itself a weak reference (a callable),
|
||||
it will be de-referenced by the system's machinery,
|
||||
so *generally* weak references are not suitable as
|
||||
receivers, though some use might be found for the
|
||||
facility whereby a higher-level library passes in
|
||||
pre-weakrefed receiver references.
|
||||
Note:
|
||||
if receiver is itself a weak reference (a callable),
|
||||
it will be de-referenced by the system's machinery,
|
||||
so *generally* weak references are not suitable as
|
||||
receivers, though some use might be found for the
|
||||
facility whereby a higher-level library passes in
|
||||
pre-weakrefed receiver references.
|
||||
|
||||
signal -- the signal to which the receiver should respond
|
||||
signal -- the signal 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
|
||||
necessarily Any).
|
||||
if Any, receiver will receive any signal from the
|
||||
indicated sender (which might also be Any, but is not
|
||||
necessarily Any).
|
||||
|
||||
Otherwise must be a hashable Python object other than
|
||||
None (DispatcherError raised on None).
|
||||
Otherwise must be a hashable Python object other than
|
||||
None (DispatcherError raised on None).
|
||||
|
||||
sender -- the sender to which the receiver should respond
|
||||
sender -- the sender to which the receiver should respond
|
||||
|
||||
if Any, receiver will receive the indicated signals
|
||||
from any sender.
|
||||
if Any, receiver will receive the indicated signals
|
||||
from any sender.
|
||||
|
||||
if Anonymous, receiver will only receive indicated
|
||||
signals from send/sendExact which do not specify a
|
||||
sender, or specify Anonymous explicitly as the sender.
|
||||
if Anonymous, receiver will only receive indicated
|
||||
signals from send/sendExact which do not specify a
|
||||
sender, or specify Anonymous explicitly as the sender.
|
||||
|
||||
Otherwise can be any python object.
|
||||
Otherwise can be any python object.
|
||||
|
||||
weak -- whether to use weak references to the receiver
|
||||
By default, the module will attempt to use weak
|
||||
references to the receiver objects. If this parameter
|
||||
is false, then strong references will be used.
|
||||
weak -- whether to use weak references to the receiver
|
||||
By default, the module will attempt to use weak
|
||||
references to the receiver objects. If this parameter
|
||||
is false, then strong references will be used.
|
||||
|
||||
returns None, may raise DispatcherTypeError
|
||||
"""
|
||||
if signal is None:
|
||||
raise errors.DispatcherTypeError(
|
||||
'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
|
||||
)
|
||||
if weak:
|
||||
receiver = saferef.safeRef(receiver, onDelete=_removeReceiver)
|
||||
senderkey = id(sender)
|
||||
if connections.has_key(senderkey):
|
||||
signals = connections[senderkey]
|
||||
else:
|
||||
connections[senderkey] = signals = {}
|
||||
# Keep track of senders for cleanup.
|
||||
# Is Anonymous something we want to clean up?
|
||||
if sender not in (None, Anonymous, Any):
|
||||
def remove(object, senderkey=senderkey):
|
||||
_removeSender(senderkey=senderkey)
|
||||
# Skip objects that can not be weakly referenced, which means
|
||||
# they won't be automatically cleaned up, but that's too bad.
|
||||
try:
|
||||
weakSender = weakref.ref(sender, remove)
|
||||
senders[senderkey] = weakSender
|
||||
except:
|
||||
pass
|
||||
returns None, may raise DispatcherTypeError
|
||||
"""
|
||||
if signal is None:
|
||||
raise errors.DispatcherTypeError(
|
||||
'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
|
||||
)
|
||||
if weak:
|
||||
receiver = saferef.safeRef(receiver, onDelete=_removeReceiver)
|
||||
senderkey = id(sender)
|
||||
if connections.has_key(senderkey):
|
||||
signals = connections[senderkey]
|
||||
else:
|
||||
connections[senderkey] = signals = {}
|
||||
# Keep track of senders for cleanup.
|
||||
# Is Anonymous something we want to clean up?
|
||||
if sender not in (None, Anonymous, Any):
|
||||
def remove(object, senderkey=senderkey):
|
||||
_removeSender(senderkey=senderkey)
|
||||
# Skip objects that can not be weakly referenced, which means
|
||||
# they won't be automatically cleaned up, but that's too bad.
|
||||
try:
|
||||
weakSender = weakref.ref(sender, remove)
|
||||
senders[senderkey] = weakSender
|
||||
except:
|
||||
pass
|
||||
|
||||
receiverID = id(receiver)
|
||||
# get current set, remove any current references to
|
||||
# this receiver in the set, including back-references
|
||||
if signals.has_key(signal):
|
||||
receivers = signals[signal]
|
||||
_removeOldBackRefs(senderkey, signal, receiver, receivers)
|
||||
else:
|
||||
receivers = signals[signal] = []
|
||||
try:
|
||||
current = sendersBack.get( receiverID )
|
||||
if current is None:
|
||||
sendersBack[ receiverID ] = current = []
|
||||
if senderkey not in current:
|
||||
current.append(senderkey)
|
||||
except:
|
||||
pass
|
||||
receiverID = id(receiver)
|
||||
# get current set, remove any current references to
|
||||
# this receiver in the set, including back-references
|
||||
if signals.has_key(signal):
|
||||
receivers = signals[signal]
|
||||
_removeOldBackRefs(senderkey, signal, receiver, receivers)
|
||||
else:
|
||||
receivers = signals[signal] = []
|
||||
try:
|
||||
current = sendersBack.get( receiverID )
|
||||
if current is None:
|
||||
sendersBack[ receiverID ] = current = []
|
||||
if senderkey not in current:
|
||||
current.append(senderkey)
|
||||
except:
|
||||
pass
|
||||
|
||||
receivers.append(receiver)
|
||||
receivers.append(receiver)
|
||||
|
||||
|
||||
|
||||
def disconnect(receiver, signal=Any, sender=Any, weak=True):
|
||||
"""Disconnect receiver from sender for signal
|
||||
"""Disconnect receiver from sender for signal
|
||||
|
||||
receiver -- the registered receiver to disconnect
|
||||
signal -- the registered signal to disconnect
|
||||
sender -- the registered sender to disconnect
|
||||
weak -- the weakref state to disconnect
|
||||
receiver -- the registered receiver to disconnect
|
||||
signal -- the registered signal to disconnect
|
||||
sender -- the registered sender to disconnect
|
||||
weak -- the weakref state to disconnect
|
||||
|
||||
disconnect reverses the process of connect,
|
||||
the semantics for the individual elements are
|
||||
logically equivalent to a tuple of
|
||||
(receiver, signal, sender, weak) used as a key
|
||||
to be deleted from the internal routing tables.
|
||||
(The actual process is slightly more complex
|
||||
but the semantics are basically the same).
|
||||
disconnect reverses the process of connect,
|
||||
the semantics for the individual elements are
|
||||
logically equivalent to a tuple of
|
||||
(receiver, signal, sender, weak) used as a key
|
||||
to be deleted from the internal routing tables.
|
||||
(The actual process is slightly more complex
|
||||
but the semantics are basically the same).
|
||||
|
||||
Note:
|
||||
Using disconnect is not required to cleanup
|
||||
routing when an object is deleted, the framework
|
||||
will remove routes for deleted objects
|
||||
automatically. It's only necessary to disconnect
|
||||
if you want to stop routing to a live object.
|
||||
Note:
|
||||
Using disconnect is not required to cleanup
|
||||
routing when an object is deleted, the framework
|
||||
will remove routes for deleted objects
|
||||
automatically. It's only necessary to disconnect
|
||||
if you want to stop routing to a live object.
|
||||
|
||||
returns None, may raise DispatcherTypeError or
|
||||
DispatcherKeyError
|
||||
"""
|
||||
if signal is None:
|
||||
raise errors.DispatcherTypeError(
|
||||
'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
|
||||
)
|
||||
if weak: receiver = saferef.safeRef(receiver)
|
||||
senderkey = id(sender)
|
||||
try:
|
||||
signals = connections[senderkey]
|
||||
receivers = signals[signal]
|
||||
except KeyError:
|
||||
raise errors.DispatcherKeyError(
|
||||
"""No receivers found for signal %r from sender %r""" %(
|
||||
signal,
|
||||
sender
|
||||
)
|
||||
)
|
||||
try:
|
||||
# also removes from receivers
|
||||
_removeOldBackRefs(senderkey, signal, receiver, receivers)
|
||||
except ValueError:
|
||||
raise errors.DispatcherKeyError(
|
||||
"""No connection to receiver %s for signal %s from sender %s""" %(
|
||||
receiver,
|
||||
signal,
|
||||
sender
|
||||
)
|
||||
)
|
||||
_cleanupConnections(senderkey, signal)
|
||||
returns None, may raise DispatcherTypeError or
|
||||
DispatcherKeyError
|
||||
"""
|
||||
if signal is None:
|
||||
raise errors.DispatcherTypeError(
|
||||
'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender)
|
||||
)
|
||||
if weak: receiver = saferef.safeRef(receiver)
|
||||
senderkey = id(sender)
|
||||
try:
|
||||
signals = connections[senderkey]
|
||||
receivers = signals[signal]
|
||||
except KeyError:
|
||||
raise errors.DispatcherKeyError(
|
||||
"""No receivers found for signal %r from sender %r""" %(
|
||||
signal,
|
||||
sender
|
||||
)
|
||||
)
|
||||
try:
|
||||
# also removes from receivers
|
||||
_removeOldBackRefs(senderkey, signal, receiver, receivers)
|
||||
except ValueError:
|
||||
raise errors.DispatcherKeyError(
|
||||
"""No connection to receiver %s for signal %s from sender %s""" %(
|
||||
receiver,
|
||||
signal,
|
||||
sender
|
||||
)
|
||||
)
|
||||
_cleanupConnections(senderkey, signal)
|
||||
|
||||
def getReceivers( sender = Any, signal = Any ):
|
||||
"""Get list of receivers from global tables
|
||||
"""Get list of receivers from global tables
|
||||
|
||||
This utility function allows you to retrieve the
|
||||
raw list of receivers from the connections table
|
||||
for the given sender and signal pair.
|
||||
This utility function allows you to retrieve the
|
||||
raw list of receivers from the connections table
|
||||
for the given sender and signal pair.
|
||||
|
||||
Note:
|
||||
there is no guarantee that this is the actual list
|
||||
stored in the connections table, so the value
|
||||
should be treated as a simple iterable/truth value
|
||||
rather than, for instance a list to which you
|
||||
might append new records.
|
||||
Note:
|
||||
there is no guarantee that this is the actual list
|
||||
stored in the connections table, so the value
|
||||
should be treated as a simple iterable/truth value
|
||||
rather than, for instance a list to which you
|
||||
might append new records.
|
||||
|
||||
Normally you would use liveReceivers( getReceivers( ...))
|
||||
to retrieve the actual receiver objects as an iterable
|
||||
object.
|
||||
"""
|
||||
try:
|
||||
return connections[id(sender)][signal]
|
||||
except KeyError:
|
||||
return []
|
||||
Normally you would use liveReceivers( getReceivers( ...))
|
||||
to retrieve the actual receiver objects as an iterable
|
||||
object.
|
||||
"""
|
||||
try:
|
||||
return connections[id(sender)][signal]
|
||||
except KeyError:
|
||||
return []
|
||||
|
||||
def liveReceivers(receivers):
|
||||
"""Filter sequence of receivers to get resolved, live receivers
|
||||
"""Filter sequence of receivers to get resolved, live receivers
|
||||
|
||||
This is a generator which will iterate over
|
||||
the passed sequence, checking for weak references
|
||||
and resolving them, then returning all live
|
||||
receivers.
|
||||
"""
|
||||
for receiver in receivers:
|
||||
if isinstance( receiver, WEAKREF_TYPES):
|
||||
# Dereference the weak reference.
|
||||
receiver = receiver()
|
||||
if receiver is not None:
|
||||
yield receiver
|
||||
else:
|
||||
yield receiver
|
||||
This is a generator which will iterate over
|
||||
the passed sequence, checking for weak references
|
||||
and resolving them, then returning all live
|
||||
receivers.
|
||||
"""
|
||||
for receiver in receivers:
|
||||
if isinstance( receiver, WEAKREF_TYPES):
|
||||
# Dereference the weak reference.
|
||||
receiver = receiver()
|
||||
if receiver is not None:
|
||||
yield receiver
|
||||
else:
|
||||
yield receiver
|
||||
|
||||
|
||||
|
||||
def getAllReceivers( sender = Any, signal = Any ):
|
||||
"""Get list of all receivers from global tables
|
||||
"""Get list of all receivers from global tables
|
||||
|
||||
This gets all receivers which should receive
|
||||
the given signal from sender, each receiver should
|
||||
be produced only once by the resulting generator
|
||||
"""
|
||||
receivers = {}
|
||||
for set in (
|
||||
# Get receivers that receive *this* signal from *this* sender.
|
||||
getReceivers( sender, signal ),
|
||||
# Add receivers that receive *any* signal from *this* sender.
|
||||
getReceivers( sender, Any ),
|
||||
# Add receivers that receive *this* signal from *any* sender.
|
||||
getReceivers( Any, signal ),
|
||||
# Add receivers that receive *any* signal from *any* sender.
|
||||
getReceivers( Any, Any ),
|
||||
):
|
||||
for receiver in set:
|
||||
if receiver: # filter out dead instance-method weakrefs
|
||||
try:
|
||||
if not receivers.has_key( receiver ):
|
||||
receivers[receiver] = 1
|
||||
yield receiver
|
||||
except TypeError:
|
||||
# dead weakrefs raise TypeError on hash...
|
||||
pass
|
||||
This gets all receivers which should receive
|
||||
the given signal from sender, each receiver should
|
||||
be produced only once by the resulting generator
|
||||
"""
|
||||
receivers = {}
|
||||
for set in (
|
||||
# Get receivers that receive *this* signal from *this* sender.
|
||||
getReceivers( sender, signal ),
|
||||
# Add receivers that receive *any* signal from *this* sender.
|
||||
getReceivers( sender, Any ),
|
||||
# Add receivers that receive *this* signal from *any* sender.
|
||||
getReceivers( Any, signal ),
|
||||
# Add receivers that receive *any* signal from *any* sender.
|
||||
getReceivers( Any, Any ),
|
||||
):
|
||||
for receiver in set:
|
||||
if receiver: # filter out dead instance-method weakrefs
|
||||
try:
|
||||
if not receivers.has_key( receiver ):
|
||||
receivers[receiver] = 1
|
||||
yield receiver
|
||||
except TypeError:
|
||||
# dead weakrefs raise TypeError on hash...
|
||||
pass
|
||||
|
||||
def send(signal=Any, sender=Anonymous, *arguments, **named):
|
||||
"""Send signal from sender to all connected receivers.
|
||||
"""Send signal from sender to all connected receivers.
|
||||
|
||||
signal -- (hashable) signal value, see connect for details
|
||||
signal -- (hashable) signal value, see connect for details
|
||||
|
||||
sender -- the sender of the signal
|
||||
sender -- the sender of the signal
|
||||
|
||||
if Any, only receivers registered for Any will receive
|
||||
the message.
|
||||
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
|
||||
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).
|
||||
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.
|
||||
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.
|
||||
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), ... ]
|
||||
Return a list of tuple pairs [(receiver, response), ... ]
|
||||
|
||||
if any receiver raises an error, the error propagates back
|
||||
through send, terminating the dispatch loop, so it is quite
|
||||
possible to not have all receivers called if a raises an
|
||||
error.
|
||||
"""
|
||||
# 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)):
|
||||
response = robustapply.robustApply(
|
||||
receiver,
|
||||
signal=signal,
|
||||
sender=sender,
|
||||
*arguments,
|
||||
**named
|
||||
)
|
||||
responses.append((receiver, response))
|
||||
return responses
|
||||
if any receiver raises an error, the error propagates back
|
||||
through send, terminating the dispatch loop, so it is quite
|
||||
possible to not have all receivers called if a raises an
|
||||
error.
|
||||
"""
|
||||
# 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)):
|
||||
response = robustapply.robustApply(
|
||||
receiver,
|
||||
signal=signal,
|
||||
sender=sender,
|
||||
*arguments,
|
||||
**named
|
||||
)
|
||||
responses.append((receiver, response))
|
||||
return responses
|
||||
def sendExact( signal=Any, sender=Anonymous, *arguments, **named ):
|
||||
"""Send signal only to those receivers registered for exact message
|
||||
"""Send signal only to those receivers registered for exact message
|
||||
|
||||
sendExact allows for avoiding Any/Anonymous registered
|
||||
handlers, sending only to those receivers explicitly
|
||||
registered for a particular signal on a particular
|
||||
sender.
|
||||
"""
|
||||
responses = []
|
||||
for receiver in liveReceivers(getReceivers(sender, signal)):
|
||||
response = robustapply.robustApply(
|
||||
receiver,
|
||||
signal=signal,
|
||||
sender=sender,
|
||||
*arguments,
|
||||
**named
|
||||
)
|
||||
responses.append((receiver, response))
|
||||
return responses
|
||||
sendExact allows for avoiding Any/Anonymous registered
|
||||
handlers, sending only to those receivers explicitly
|
||||
registered for a particular signal on a particular
|
||||
sender.
|
||||
"""
|
||||
responses = []
|
||||
for receiver in liveReceivers(getReceivers(sender, signal)):
|
||||
response = robustapply.robustApply(
|
||||
receiver,
|
||||
signal=signal,
|
||||
sender=sender,
|
||||
*arguments,
|
||||
**named
|
||||
)
|
||||
responses.append((receiver, response))
|
||||
return responses
|
||||
|
||||
|
||||
def _removeReceiver(receiver):
|
||||
"""Remove receiver from connections."""
|
||||
if not sendersBack:
|
||||
# During module cleanup the mapping will be replaced with None
|
||||
return False
|
||||
backKey = id(receiver)
|
||||
for senderkey in sendersBack.get(backKey,()):
|
||||
try:
|
||||
signals = connections[senderkey].keys()
|
||||
except KeyError,err:
|
||||
pass
|
||||
else:
|
||||
for signal in signals:
|
||||
try:
|
||||
receivers = connections[senderkey][signal]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
receivers.remove( receiver )
|
||||
except Exception, err:
|
||||
pass
|
||||
_cleanupConnections(senderkey, signal)
|
||||
try:
|
||||
del sendersBack[ backKey ]
|
||||
except KeyError:
|
||||
pass
|
||||
"""Remove receiver from connections."""
|
||||
if not sendersBack:
|
||||
# During module cleanup the mapping will be replaced with None
|
||||
return False
|
||||
backKey = id(receiver)
|
||||
for senderkey in sendersBack.get(backKey,()):
|
||||
try:
|
||||
signals = connections[senderkey].keys()
|
||||
except KeyError,err:
|
||||
pass
|
||||
else:
|
||||
for signal in signals:
|
||||
try:
|
||||
receivers = connections[senderkey][signal]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
receivers.remove( receiver )
|
||||
except Exception, err:
|
||||
pass
|
||||
_cleanupConnections(senderkey, signal)
|
||||
try:
|
||||
del sendersBack[ backKey ]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def _cleanupConnections(senderkey, signal):
|
||||
"""Delete any empty signals for senderkey. Delete senderkey if empty."""
|
||||
try:
|
||||
receivers = connections[senderkey][signal]
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
if not receivers:
|
||||
# No more connected receivers. Therefore, remove the signal.
|
||||
try:
|
||||
signals = connections[senderkey]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
del signals[signal]
|
||||
if not signals:
|
||||
# No more signal connections. Therefore, remove the sender.
|
||||
_removeSender(senderkey)
|
||||
"""Delete any empty signals for senderkey. Delete senderkey if empty."""
|
||||
try:
|
||||
receivers = connections[senderkey][signal]
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
if not receivers:
|
||||
# No more connected receivers. Therefore, remove the signal.
|
||||
try:
|
||||
signals = connections[senderkey]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
del signals[signal]
|
||||
if not signals:
|
||||
# No more signal connections. Therefore, remove the sender.
|
||||
_removeSender(senderkey)
|
||||
|
||||
def _removeSender(senderkey):
|
||||
"""Remove senderkey from connections."""
|
||||
_removeBackrefs(senderkey)
|
||||
try:
|
||||
del connections[senderkey]
|
||||
except KeyError:
|
||||
pass
|
||||
# Senderkey will only be in senders dictionary if sender
|
||||
# could be weakly referenced.
|
||||
try:
|
||||
del senders[senderkey]
|
||||
except:
|
||||
pass
|
||||
"""Remove senderkey from connections."""
|
||||
_removeBackrefs(senderkey)
|
||||
try:
|
||||
del connections[senderkey]
|
||||
except KeyError:
|
||||
pass
|
||||
# Senderkey will only be in senders dictionary if sender
|
||||
# could be weakly referenced.
|
||||
try:
|
||||
del senders[senderkey]
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def _removeBackrefs( senderkey):
|
||||
"""Remove all back-references to this senderkey"""
|
||||
try:
|
||||
signals = connections[senderkey]
|
||||
except KeyError:
|
||||
signals = None
|
||||
else:
|
||||
items = signals.items()
|
||||
def allReceivers( ):
|
||||
for signal,set in items:
|
||||
for item in set:
|
||||
yield item
|
||||
for receiver in allReceivers():
|
||||
_killBackref( receiver, senderkey )
|
||||
"""Remove all back-references to this senderkey"""
|
||||
try:
|
||||
signals = connections[senderkey]
|
||||
except KeyError:
|
||||
signals = None
|
||||
else:
|
||||
items = signals.items()
|
||||
def allReceivers( ):
|
||||
for signal,set in items:
|
||||
for item in set:
|
||||
yield item
|
||||
for receiver in allReceivers():
|
||||
_killBackref( receiver, senderkey )
|
||||
|
||||
def _removeOldBackRefs(senderkey, signal, receiver, receivers):
|
||||
"""Kill old sendersBack references from receiver
|
||||
"""Kill old sendersBack references from receiver
|
||||
|
||||
This guards against multiple registration of the same
|
||||
receiver for a given signal and sender leaking memory
|
||||
as old back reference records build up.
|
||||
This guards against multiple registration of the same
|
||||
receiver for a given signal and sender leaking memory
|
||||
as old back reference records build up.
|
||||
|
||||
Also removes old receiver instance from receivers
|
||||
"""
|
||||
try:
|
||||
index = receivers.index(receiver)
|
||||
# need to scan back references here and remove senderkey
|
||||
except ValueError:
|
||||
return False
|
||||
else:
|
||||
oldReceiver = receivers[index]
|
||||
del receivers[index]
|
||||
found = 0
|
||||
signals = connections.get(signal)
|
||||
if signals is not None:
|
||||
for sig,recs in connections.get(signal,{}).iteritems():
|
||||
if sig != signal:
|
||||
for rec in recs:
|
||||
if rec is oldReceiver:
|
||||
found = 1
|
||||
break
|
||||
if not found:
|
||||
_killBackref( oldReceiver, senderkey )
|
||||
return True
|
||||
return False
|
||||
Also removes old receiver instance from receivers
|
||||
"""
|
||||
try:
|
||||
index = receivers.index(receiver)
|
||||
# need to scan back references here and remove senderkey
|
||||
except ValueError:
|
||||
return False
|
||||
else:
|
||||
oldReceiver = receivers[index]
|
||||
del receivers[index]
|
||||
found = 0
|
||||
signals = connections.get(signal)
|
||||
if signals is not None:
|
||||
for sig,recs in connections.get(signal,{}).iteritems():
|
||||
if sig != signal:
|
||||
for rec in recs:
|
||||
if rec is oldReceiver:
|
||||
found = 1
|
||||
break
|
||||
if not found:
|
||||
_killBackref( oldReceiver, senderkey )
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _killBackref( receiver, senderkey ):
|
||||
"""Do the actual removal of back reference from receiver to senderkey"""
|
||||
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
|
||||
"""Do the actual removal of back reference from receiver to senderkey"""
|
||||
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
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
"""
|
||||
|
||||
class DispatcherError(Exception):
|
||||
"""Base class for all Dispatcher errors"""
|
||||
"""Base class for all Dispatcher errors"""
|
||||
class DispatcherKeyError(KeyError, DispatcherError):
|
||||
"""Error raised when unknown (sender,signal) set specified"""
|
||||
"""Error raised when unknown (sender,signal) set specified"""
|
||||
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
|
||||
|
||||
Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors
|
||||
All rights reserved.
|
||||
Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
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
|
||||
software without specific prior written permission.
|
||||
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
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
|
|
@ -3,55 +3,55 @@ from django.dispatch.dispatcher import Any, Anonymous, liveReceivers, getAllRece
|
|||
from django.dispatch.robustapply import robustApply
|
||||
|
||||
def sendRobust(
|
||||
signal=Any,
|
||||
sender=Anonymous,
|
||||
*arguments, **named
|
||||
signal=Any,
|
||||
sender=Anonymous,
|
||||
*arguments, **named
|
||||
):
|
||||
"""Send signal from sender to all connected receivers catching errors
|
||||
"""Send signal from sender to all connected receivers catching errors
|
||||
|
||||
signal -- (hashable) signal value, see connect for details
|
||||
signal -- (hashable) signal value, see connect for details
|
||||
|
||||
sender -- the sender of the signal
|
||||
sender -- the sender of the signal
|
||||
|
||||
if Any, only receivers registered for Any will receive
|
||||
the message.
|
||||
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
|
||||
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).
|
||||
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.
|
||||
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.
|
||||
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), ... ]
|
||||
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
|
||||
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
|
||||
|
|
|
@ -7,43 +7,41 @@ those which are acceptable.
|
|||
"""
|
||||
|
||||
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
|
||||
has its first argument bound
|
||||
"""
|
||||
if hasattr(receiver, '__call__'):
|
||||
# receiver is a class instance; assume it is callable.
|
||||
# Reassign receiver to the actual method that will be called.
|
||||
if hasattr( receiver.__call__, 'im_func') or hasattr( receiver.__call__, 'im_code'):
|
||||
receiver = receiver.__call__
|
||||
if hasattr( receiver, 'im_func' ):
|
||||
# an instance-method...
|
||||
return receiver, receiver.im_func.func_code, 1
|
||||
elif not hasattr( receiver, 'func_code'):
|
||||
raise ValueError('unknown reciever type %s %s'%(receiver, type(receiver)))
|
||||
return receiver, receiver.func_code, 0
|
||||
If fromMethod is true, then the callable already
|
||||
has its first argument bound
|
||||
"""
|
||||
if hasattr(receiver, '__call__'):
|
||||
# receiver is a class instance; assume it is callable.
|
||||
# Reassign receiver to the actual method that will be called.
|
||||
if hasattr( receiver.__call__, 'im_func') or hasattr( receiver.__call__, 'im_code'):
|
||||
receiver = receiver.__call__
|
||||
if hasattr( receiver, 'im_func' ):
|
||||
# an instance-method...
|
||||
return receiver, receiver.im_func.func_code, 1
|
||||
elif not hasattr( receiver, 'func_code'):
|
||||
raise ValueError('unknown reciever type %s %s'%(receiver, type(receiver)))
|
||||
return receiver, receiver.func_code, 0
|
||||
|
||||
def robustApply(receiver, *arguments, **named):
|
||||
"""Call receiver with arguments and an appropriate subset of named
|
||||
"""
|
||||
receiver, codeObject, startIndex = function( receiver )
|
||||
acceptable = codeObject.co_varnames[startIndex+len(arguments):codeObject.co_argcount]
|
||||
for name in codeObject.co_varnames[startIndex:startIndex+len(arguments)]:
|
||||
if named.has_key( name ):
|
||||
raise TypeError(
|
||||
"""Argument %r specified both positionally and as a keyword for calling %r"""% (
|
||||
name, receiver,
|
||||
)
|
||||
)
|
||||
if not (codeObject.co_flags & 8):
|
||||
# fc does not have a **kwds type parameter, therefore
|
||||
# remove unacceptable arguments.
|
||||
for arg in named.keys():
|
||||
if arg not in acceptable:
|
||||
del named[arg]
|
||||
return receiver(*arguments, **named)
|
||||
|
||||
|
||||
"""Call receiver with arguments and an appropriate subset of named
|
||||
"""
|
||||
receiver, codeObject, startIndex = function( receiver )
|
||||
acceptable = codeObject.co_varnames[startIndex+len(arguments):codeObject.co_argcount]
|
||||
for name in codeObject.co_varnames[startIndex:startIndex+len(arguments)]:
|
||||
if named.has_key( name ):
|
||||
raise TypeError(
|
||||
"""Argument %r specified both positionally and as a keyword for calling %r"""% (
|
||||
name, receiver,
|
||||
)
|
||||
)
|
||||
if not (codeObject.co_flags & 8):
|
||||
# fc does not have a **kwds type parameter, therefore
|
||||
# remove unacceptable arguments.
|
||||
for arg in named.keys():
|
||||
if arg not in acceptable:
|
||||
del named[arg]
|
||||
return receiver(*arguments, **named)
|
||||
|
|
|
@ -2,164 +2,164 @@
|
|||
import weakref, traceback
|
||||
|
||||
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
|
||||
bound method reference, will create a BoundMethodWeakref,
|
||||
otherwise creates a simple weakref.
|
||||
onDelete -- if provided, will have a hard reference stored
|
||||
to the callable to be called after the safe reference
|
||||
goes out of scope with the reference object, (either a
|
||||
weakref or a BoundMethodWeakref) as argument.
|
||||
"""
|
||||
if hasattr(target, 'im_self'):
|
||||
if target.im_self is not None:
|
||||
# Turn a bound method into a BoundMethodWeakref instance.
|
||||
# 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,)
|
||||
reference = BoundMethodWeakref(
|
||||
target=target,
|
||||
onDelete=onDelete
|
||||
)
|
||||
return reference
|
||||
if callable(onDelete):
|
||||
return weakref.ref(target, onDelete)
|
||||
else:
|
||||
return weakref.ref( target )
|
||||
target -- the object to be weakly referenced, if it's a
|
||||
bound method reference, will create a BoundMethodWeakref,
|
||||
otherwise creates a simple weakref.
|
||||
onDelete -- if provided, will have a hard reference stored
|
||||
to the callable to be called after the safe reference
|
||||
goes out of scope with the reference object, (either a
|
||||
weakref or a BoundMethodWeakref) as argument.
|
||||
"""
|
||||
if hasattr(target, 'im_self'):
|
||||
if target.im_self is not None:
|
||||
# Turn a bound method into a BoundMethodWeakref instance.
|
||||
# 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,)
|
||||
reference = BoundMethodWeakref(
|
||||
target=target,
|
||||
onDelete=onDelete
|
||||
)
|
||||
return reference
|
||||
if callable(onDelete):
|
||||
return weakref.ref(target, onDelete)
|
||||
else:
|
||||
return weakref.ref( target )
|
||||
|
||||
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
|
||||
referencing a bound method without requiring that the
|
||||
method object itself (which is normally a transient
|
||||
object) is kept alive. Instead, the BoundMethodWeakref
|
||||
object keeps weak references to both the object and the
|
||||
function which together define the instance method.
|
||||
BoundMethodWeakref objects provide a mechanism for
|
||||
referencing a bound method without requiring that the
|
||||
method object itself (which is normally a transient
|
||||
object) is kept alive. Instead, the BoundMethodWeakref
|
||||
object keeps weak references to both the object and the
|
||||
function which together define the instance method.
|
||||
|
||||
Attributes:
|
||||
key -- the identity key for the reference, calculated
|
||||
by the class's calculateKey method applied to the
|
||||
target instance method
|
||||
deletionMethods -- sequence of callable objects taking
|
||||
single argument, a reference to this object which
|
||||
will be called when *either* the target object or
|
||||
target function is garbage collected (i.e. when
|
||||
this object becomes invalid). These are specified
|
||||
as the onDelete parameters of safeRef calls.
|
||||
weakSelf -- weak reference to the target object
|
||||
weakFunc -- weak reference to the target function
|
||||
Attributes:
|
||||
key -- the identity key for the reference, calculated
|
||||
by the class's calculateKey method applied to the
|
||||
target instance method
|
||||
deletionMethods -- sequence of callable objects taking
|
||||
single argument, a reference to this object which
|
||||
will be called when *either* the target object or
|
||||
target function is garbage collected (i.e. when
|
||||
this object becomes invalid). These are specified
|
||||
as the onDelete parameters of safeRef calls.
|
||||
weakSelf -- weak reference to the target object
|
||||
weakFunc -- weak reference to the target function
|
||||
|
||||
Class Attributes:
|
||||
_allInstances -- class attribute pointing to all live
|
||||
BoundMethodWeakref objects indexed by the class's
|
||||
calculateKey(target) method applied to the target
|
||||
objects. This weak value dictionary is used to
|
||||
short-circuit creation so that multiple references
|
||||
to the same (object, function) pair produce the
|
||||
same BoundMethodWeakref instance.
|
||||
Class Attributes:
|
||||
_allInstances -- class attribute pointing to all live
|
||||
BoundMethodWeakref objects indexed by the class's
|
||||
calculateKey(target) method applied to the target
|
||||
objects. This weak value dictionary is used to
|
||||
short-circuit creation so that multiple references
|
||||
to the same (object, function) pair produce the
|
||||
same BoundMethodWeakref instance.
|
||||
|
||||
"""
|
||||
_allInstances = weakref.WeakValueDictionary()
|
||||
def __new__( cls, target, onDelete=None, *arguments,**named ):
|
||||
"""Create new instance or return current instance
|
||||
"""
|
||||
_allInstances = weakref.WeakValueDictionary()
|
||||
def __new__( cls, target, onDelete=None, *arguments,**named ):
|
||||
"""Create new instance or return current instance
|
||||
|
||||
Basically this method of construction allows us to
|
||||
short-circuit creation of references to already-
|
||||
referenced instance methods. The key corresponding
|
||||
to the target is calculated, and if there is already
|
||||
an existing reference, that is returned, with its
|
||||
deletionMethods attribute updated. Otherwise the
|
||||
new instance is created and registered in the table
|
||||
of already-referenced methods.
|
||||
"""
|
||||
key = cls.calculateKey(target)
|
||||
current =cls._allInstances.get(key)
|
||||
if current is not None:
|
||||
current.deletionMethods.append( onDelete)
|
||||
return current
|
||||
else:
|
||||
base = super( BoundMethodWeakref, cls).__new__( cls )
|
||||
cls._allInstances[key] = base
|
||||
base.__init__( target, onDelete, *arguments,**named)
|
||||
return base
|
||||
def __init__(self, target, onDelete=None):
|
||||
"""Return a weak-reference-like instance for a bound method
|
||||
Basically this method of construction allows us to
|
||||
short-circuit creation of references to already-
|
||||
referenced instance methods. The key corresponding
|
||||
to the target is calculated, and if there is already
|
||||
an existing reference, that is returned, with its
|
||||
deletionMethods attribute updated. Otherwise the
|
||||
new instance is created and registered in the table
|
||||
of already-referenced methods.
|
||||
"""
|
||||
key = cls.calculateKey(target)
|
||||
current =cls._allInstances.get(key)
|
||||
if current is not None:
|
||||
current.deletionMethods.append( onDelete)
|
||||
return current
|
||||
else:
|
||||
base = super( BoundMethodWeakref, cls).__new__( cls )
|
||||
cls._allInstances[key] = base
|
||||
base.__init__( target, onDelete, *arguments,**named)
|
||||
return base
|
||||
def __init__(self, target, onDelete=None):
|
||||
"""Return a weak-reference-like instance for a bound method
|
||||
|
||||
target -- the instance-method target for the weak
|
||||
reference, must have im_self and im_func attributes
|
||||
and be reconstructable via:
|
||||
target.im_func.__get__( target.im_self )
|
||||
which is true of built-in instance methods.
|
||||
onDelete -- optional callback which will be called
|
||||
when this weak reference ceases to be valid
|
||||
(i.e. either the object or the function is garbage
|
||||
collected). Should take a single argument,
|
||||
which will be passed a pointer to this object.
|
||||
"""
|
||||
def remove(weak, self=self):
|
||||
"""Set self.isDead to true when method or instance is destroyed"""
|
||||
methods = self.deletionMethods[:]
|
||||
del self.deletionMethods[:]
|
||||
try:
|
||||
del self.__class__._allInstances[ self.key ]
|
||||
except KeyError:
|
||||
pass
|
||||
for function in methods:
|
||||
try:
|
||||
if callable( function ):
|
||||
function( self )
|
||||
except Exception, e:
|
||||
try:
|
||||
traceback.print_exc()
|
||||
except AttributeError, err:
|
||||
print '''Exception during saferef %s cleanup function %s: %s'''%(
|
||||
self, function, e
|
||||
)
|
||||
self.deletionMethods = [onDelete]
|
||||
self.key = self.calculateKey( target )
|
||||
self.weakSelf = weakref.ref(target.im_self, remove)
|
||||
self.weakFunc = weakref.ref(target.im_func, remove)
|
||||
self.selfName = str(target.im_self)
|
||||
self.funcName = str(target.im_func.__name__)
|
||||
def calculateKey( cls, target ):
|
||||
"""Calculate the reference key for this reference
|
||||
target -- the instance-method target for the weak
|
||||
reference, must have im_self and im_func attributes
|
||||
and be reconstructable via:
|
||||
target.im_func.__get__( target.im_self )
|
||||
which is true of built-in instance methods.
|
||||
onDelete -- optional callback which will be called
|
||||
when this weak reference ceases to be valid
|
||||
(i.e. either the object or the function is garbage
|
||||
collected). Should take a single argument,
|
||||
which will be passed a pointer to this object.
|
||||
"""
|
||||
def remove(weak, self=self):
|
||||
"""Set self.isDead to true when method or instance is destroyed"""
|
||||
methods = self.deletionMethods[:]
|
||||
del self.deletionMethods[:]
|
||||
try:
|
||||
del self.__class__._allInstances[ self.key ]
|
||||
except KeyError:
|
||||
pass
|
||||
for function in methods:
|
||||
try:
|
||||
if callable( function ):
|
||||
function( self )
|
||||
except Exception, e:
|
||||
try:
|
||||
traceback.print_exc()
|
||||
except AttributeError, err:
|
||||
print '''Exception during saferef %s cleanup function %s: %s'''%(
|
||||
self, function, e
|
||||
)
|
||||
self.deletionMethods = [onDelete]
|
||||
self.key = self.calculateKey( target )
|
||||
self.weakSelf = weakref.ref(target.im_self, remove)
|
||||
self.weakFunc = weakref.ref(target.im_func, remove)
|
||||
self.selfName = str(target.im_self)
|
||||
self.funcName = str(target.im_func.__name__)
|
||||
def calculateKey( cls, target ):
|
||||
"""Calculate the reference key for this reference
|
||||
|
||||
Currently this is a two-tuple of the id()'s of the
|
||||
target object and the target function respectively.
|
||||
"""
|
||||
return (id(target.im_self),id(target.im_func))
|
||||
calculateKey = classmethod( calculateKey )
|
||||
def __str__(self):
|
||||
"""Give a friendly representation of the object"""
|
||||
return """%s( %s.%s )"""%(
|
||||
self.__class__.__name__,
|
||||
self.selfName,
|
||||
self.funcName,
|
||||
)
|
||||
__repr__ = __str__
|
||||
def __nonzero__( self ):
|
||||
"""Whether we are still a valid reference"""
|
||||
return self() is not None
|
||||
def __cmp__( self, other ):
|
||||
"""Compare with another reference"""
|
||||
if not isinstance (other,self.__class__):
|
||||
return cmp( self.__class__, type(other) )
|
||||
return cmp( self.key, other.key)
|
||||
def __call__(self):
|
||||
"""Return a strong reference to the bound method
|
||||
Currently this is a two-tuple of the id()'s of the
|
||||
target object and the target function respectively.
|
||||
"""
|
||||
return (id(target.im_self),id(target.im_func))
|
||||
calculateKey = classmethod( calculateKey )
|
||||
def __str__(self):
|
||||
"""Give a friendly representation of the object"""
|
||||
return """%s( %s.%s )"""%(
|
||||
self.__class__.__name__,
|
||||
self.selfName,
|
||||
self.funcName,
|
||||
)
|
||||
__repr__ = __str__
|
||||
def __nonzero__( self ):
|
||||
"""Whether we are still a valid reference"""
|
||||
return self() is not None
|
||||
def __cmp__( self, other ):
|
||||
"""Compare with another reference"""
|
||||
if not isinstance (other,self.__class__):
|
||||
return cmp( self.__class__, type(other) )
|
||||
return cmp( self.key, other.key)
|
||||
def __call__(self):
|
||||
"""Return a strong reference to the bound method
|
||||
|
||||
If the target cannot be retrieved, then will
|
||||
return None, otherwise returns a bound instance
|
||||
method for our object and function.
|
||||
If the target cannot be retrieved, then will
|
||||
return None, otherwise returns a bound instance
|
||||
method for our object and function.
|
||||
|
||||
Note:
|
||||
You may call this method any number of times,
|
||||
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
|
||||
Note:
|
||||
You may call this method any number of times,
|
||||
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
|
||||
|
|
|
@ -438,7 +438,7 @@ def pluralize(value, arg='s'):
|
|||
the comma is used for the singular case.
|
||||
"""
|
||||
if not ',' in arg:
|
||||
arg = ',' + arg
|
||||
arg = ',' + arg
|
||||
bits = arg.split(',')
|
||||
if len(bits) > 2:
|
||||
return ''
|
||||
|
|
|
@ -118,8 +118,8 @@ additional class on the ``a`` for that tool. These are ``.addlink`` and
|
|||
Example from a changelist page::
|
||||
|
||||
<ul class="object-tools">
|
||||
<li><a href="/stories/add/" class="addlink">Add redirect</a></li>
|
||||
</ul>
|
||||
<li><a href="/stories/add/" class="addlink">Add redirect</a></li>
|
||||
</ul>
|
||||
|
||||
.. image:: http://media.djangoproject.com/img/doc/admincss/objecttools_01.gif
|
||||
:alt: Object tools on a changelist page
|
||||
|
|
Loading…
Reference in New Issue