[Checkins] SVN: zc.async/trunk/ add sphinx docs for pypi to revision control

Gary Poster gary at modernsongs.com
Sun Sep 21 22:38:23 EDT 2008


Log message for revision 91302:
  add sphinx docs for pypi to revision control

Changed:
  U   zc.async/trunk/MANIFEST.in
  A   zc.async/trunk/htmldocs/
  A   zc.async/trunk/htmldocs/1.5.0/
  A   zc.async/trunk/htmldocs/1.5.0/CHANGES.html
  A   zc.async/trunk/htmldocs/1.5.0/QUICKSTART_1_VIRTUALENV.html
  A   zc.async/trunk/htmldocs/1.5.0/QUICKSTART_2_GROK.html
  A   zc.async/trunk/htmldocs/1.5.0/README.html
  A   zc.async/trunk/htmldocs/1.5.0/README_1.html
  A   zc.async/trunk/htmldocs/1.5.0/README_2.html
  A   zc.async/trunk/htmldocs/1.5.0/README_3.html
  A   zc.async/trunk/htmldocs/1.5.0/README_3a.html
  A   zc.async/trunk/htmldocs/1.5.0/README_3b.html
  A   zc.async/trunk/htmldocs/1.5.0/_sources/
  A   zc.async/trunk/htmldocs/1.5.0/_sources/CHANGES.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/QUICKSTART_1_VIRTUALENV.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/QUICKSTART_2_GROK.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/README.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/README_1.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/README_2.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/README_3.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/README_3a.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/README_3b.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/catastrophes.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/ftesting.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/index.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/tips.txt
  A   zc.async/trunk/htmldocs/1.5.0/_sources/z3.txt
  A   zc.async/trunk/htmldocs/1.5.0/_static/
  A   zc.async/trunk/htmldocs/1.5.0/_static/contents.png
  A   zc.async/trunk/htmldocs/1.5.0/_static/default.css
  A   zc.async/trunk/htmldocs/1.5.0/_static/doctools.js
  A   zc.async/trunk/htmldocs/1.5.0/_static/file.png
  A   zc.async/trunk/htmldocs/1.5.0/_static/interface.js
  A   zc.async/trunk/htmldocs/1.5.0/_static/jquery.js
  A   zc.async/trunk/htmldocs/1.5.0/_static/minus.png
  A   zc.async/trunk/htmldocs/1.5.0/_static/navigation.png
  A   zc.async/trunk/htmldocs/1.5.0/_static/plus.png
  A   zc.async/trunk/htmldocs/1.5.0/_static/pygments.css
  A   zc.async/trunk/htmldocs/1.5.0/_static/rightsidebar.css
  A   zc.async/trunk/htmldocs/1.5.0/_static/searchtools.js
  A   zc.async/trunk/htmldocs/1.5.0/_static/sphinxdoc.css
  A   zc.async/trunk/htmldocs/1.5.0/_static/stickysidebar.css
  A   zc.async/trunk/htmldocs/1.5.0/_static/traditional.css
  A   zc.async/trunk/htmldocs/1.5.0/_static/zc_async.png
  A   zc.async/trunk/htmldocs/1.5.0/catastrophes.html
  A   zc.async/trunk/htmldocs/1.5.0/ftesting.html
  A   zc.async/trunk/htmldocs/1.5.0/genindex.html
  A   zc.async/trunk/htmldocs/1.5.0/index.html
  A   zc.async/trunk/htmldocs/1.5.0/modindex.html
  A   zc.async/trunk/htmldocs/1.5.0/search.html
  A   zc.async/trunk/htmldocs/1.5.0/searchindex.json
  A   zc.async/trunk/htmldocs/1.5.0/tips.html
  A   zc.async/trunk/htmldocs/1.5.0/z3.html
  A   zc.async/trunk/htmldocs/index.html

-=-
Modified: zc.async/trunk/MANIFEST.in
===================================================================
--- zc.async/trunk/MANIFEST.in	2008-09-22 02:27:06 UTC (rev 91301)
+++ zc.async/trunk/MANIFEST.in	2008-09-22 02:38:23 UTC (rev 91302)
@@ -1,3 +1,4 @@
 exclude MANIFEST.in TEST_THIS_REST_BEFORE_REGISTERING.txt buildout.cfg
 prune sphinx
+prune htmldocs
 prune bootstrap

Added: zc.async/trunk/htmldocs/1.5.0/CHANGES.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/CHANGES.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/CHANGES.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,415 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Changes &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="prev" title="Zope 3 Testing Tips and Tricks" href="ftesting.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="ftesting.html" title="Zope 3 Testing Tips and Tricks"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="changes">
+<h1 id="changes">Changes<a class="headerlink" href="#changes" title="Permalink to this headline">¶</a></h1>
+<div class="section" id="id1">
+<h2 id="id1">1.5.0 (2008-09-21)<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h2>
+<ul class="simple">
+<li>Documentation improvements.  Converted documentation into Sphinx system.</li>
+<li>Made &#8220;other&#8221; commit errors for the <tt class="docutils literal"><span class="pre">RetryCommonForever</span></tt> retry policy have
+an incremental backoff.  By default, this starts at 0 seconds, and increments
+by a second to a maximum of 60 seconds.</li>
+<li>Work around a memory leak in zope.i18nmessageid
+(<a class="reference external" href="https://bugs.launchpad.net/zope3/+bug/257657">https://bugs.launchpad.net/zope3/+bug/257657</a>).  The change should be
+backward-compatible.  It also will produce slightly smaller pickles for Jobs,
+but that was really not a particular goal.</li>
+<li>Added zc.async.partial.Partial for backward compatibility purposes.</li>
+<li>Fix support for Twisted installed reactor.</li>
+<li>Fix retry behavior for parallel and serial jobs</li>
+<li>Tweaked the uuid.txt to mention zdaemon/supervisor rather than Zope 3.</li>
+<li>Fixed some bugs in egg creation.</li>
+<li>Changed quotas to not use a container that has conflict resolution, since
+these values should be a strict maximum.</li>
+<li>We only want to claim a job if we are activated.   Make the agent check the
+<tt class="docutils literal"><span class="pre">activated</span></tt> and <tt class="docutils literal"><span class="pre">dead</span></tt> attributes of the parent dispatcher before
+claiming.</li>
+<li>When activating, also clean out jobs from the dispatcher&#8217;s agents, just as
+with deactivating.  This should protect from unusual race conditions in
+which the dispatcher got a job after being deactivated.</li>
+<li>Change dispatcher to ping before claiming jobs.</li>
+<li>when a ping reactivates a dispatcher, use new method <tt class="docutils literal"><span class="pre">reactivate</span></tt> rather
+than <tt class="docutils literal"><span class="pre">activate</span></tt>.  This fires a new <tt class="docutils literal"><span class="pre">DispatcherReactivated</span></tt> event.</li>
+<li>It&#8217;s still theoretically possible (for instance, with a
+badly-behaved long commit that causes a sibling to believe that the
+process is dead) that an async worker process would be working on a
+job that it shouldn&#8217;t be.  For instance, the job has been taken away,
+and is another process&#8217; responsibility now.  Now, whenever a
+process is about to start any work (especially a retry), it should
+double-check that the job is registered as being performed by itself.
+If not, the process should abort the transaction, make an error
+log, and give up on the job.  Write conflict errors on the job should
+protect us from the edge cases in this story.</li>
+<li>The dispatcher&#8217;s <tt class="docutils literal"><span class="pre">getActiveJobs</span></tt> method now actually tells you information
+about what&#8217;s going on in the threads at this instant, rather than what&#8217;s
+going on in the database.  The poll&#8217;s <tt class="docutils literal"><span class="pre">active</span> <span class="pre">jobs</span></tt> keys continues to
+report what was true <em>in the database</em> as of <em>the last poll</em>.  This change
+also affects the <tt class="docutils literal"><span class="pre">async</span> <span class="pre">jobs</span></tt> monitor command.</li>
+<li>The dispatcher method <tt class="docutils literal"><span class="pre">getJobInfo</span></tt> (and the monitor command <tt class="docutils literal"><span class="pre">async</span> <span class="pre">job</span></tt>)
+now returns the name of the queue for the job, the name of the agent for the
+job, and whether the job has been, or was reassigned.</li>
+<li>zc.async events inherit from &#8216;zc.component.interfaces.IObjectEvent&#8217; instead
+of a zc.async specific IObjectEvent (thanks to Satchit Haridas).</li>
+<li>Added new monitoring and introspection tools: the <tt class="docutils literal"><span class="pre">asyncdb</span></tt> zc.monitor
+command (and, for Python, the code in monitordb.py).  This code provides
+easy spellings to examine the database&#8217;s view of what is happening in
+zc.async.  Because it is the database, it also has a much longer historical
+view than the <tt class="docutils literal"><span class="pre">async</span></tt> tools.  The best way to learn about these tools is
+to read the extensive documentation provided within zc.monitor by
+using <tt class="docutils literal"><span class="pre">asyncdb</span> <span class="pre">help</span></tt> and <tt class="docutils literal"><span class="pre">asyncdb</span> <span class="pre">help</span> <span class="pre">&lt;TOOL</span> <span class="pre">NAME&gt;</span></tt>.</li>
+<li>Added new preferred way of filtering agent choices: the new <tt class="docutils literal"><span class="pre">filter</span></tt>
+attribute.  Using filters, rather than &#8220;choosers,&#8221; allows several <tt class="docutils literal"><span class="pre">asyncdb</span></tt>
+tools to filter pending jobs based on what an agent is willing to do.  It
+also is a smaller contract, and so a filter requires less code than a chooser
+in the common case.  On the other hand, using a filter alone doesn&#8217;t allow
+the agent to try to <em>prefer</em> certain tasks.</li>
+<li>Deprecated agent.chooseFirst.  It is no longer necesary, since an agent
+without a chooser and with a filter of None has the same behavior.  It is
+retained for legacy databases.</li>
+<li>Moved deprecated legacy code to new <tt class="docutils literal"><span class="pre">legacy</span></tt> module.</li>
+<li>Tried to be significantly reduce the chance of spurious timing errors in the
+tests, at the expense of causing the tests to take longer to run.</li>
+<li>monitoring support depends on the new zc.monitor package, which is not Zope
+specific.  This means non-Zope 3 apps can take advantage of the monitoring
+support.  To use, use the [monitor] target; this only adds simplejson,
+zc.ngi, and zc.monitor to the basic dependencies.</li>
+<li>Make ftesting try to join worker threads, in addition to polling thread,
+to try to eliminate intermittent test-runner warnings in ftests that a
+thread is left behind.  If the threads do not end, inform the user what jobs
+are not letting go.  (thanks to Patrick Strawderman)</li>
+</ul>
+</div>
+<div class="section" id="id2">
+<h2 id="id2">1.4.1 (2008-07-30)<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h2>
+<ul class="simple">
+<li>The new <tt class="docutils literal"><span class="pre">serial</span></tt> and <tt class="docutils literal"><span class="pre">parallel</span></tt> helpers did not allow the
+<tt class="docutils literal"><span class="pre">postprocess</span></tt> argument to be a partial closure, and were being naughty.
+Fixed.</li>
+<li>Added tests and demos for advanced features of <tt class="docutils literal"><span class="pre">serial</span></tt> and <tt class="docutils literal"><span class="pre">parallel</span></tt>.</li>
+<li>More tweaks to the new Quickstart S5 document.</li>
+</ul>
+</div>
+<div class="section" id="id3">
+<h2 id="id3">1.4.0 (2008-07-30)<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h2>
+<ul>
+<li><p class="first">Mentioned in ftesting.txt that Zope 3 users should uses zope.app.testing
+3.4.2 or newer.  Also added a summary section at the beginning of that file.</p>
+</li>
+<li><p class="first">Added logging of critical messages to __stdout__ for <tt class="docutils literal"><span class="pre">ftesting.setUp</span></tt>.
+This can help discovering problems in callback transactions.  This uses a new
+helper function , <tt class="docutils literal"><span class="pre">print_logs</span></tt>, in zc.async.testing, which is primarily
+intended to be used for quick and dirty debugging</p>
+</li>
+<li><p class="first">Changed testing.wait_for_result and testing.wait_for_annotation to ignore
+ReadConflictErrors, so they can be used more reliably in tests that use
+MappingStorage, and other storages without MVCC.</p>
+</li>
+<li><p class="first">Support &lt;type &#8216;builtin_function_or_method&#8217;&gt; for adaptation to Job.</p>
+</li>
+<li><p class="first">Add warning about long commits to tips and tricks.</p>
+</li>
+<li><p class="first">After complaining about a polling dispatcher that is deactivated not really
+being dead in the logs, reactivate.</p>
+</li>
+<li><p class="first">No longer use intermediate job to implement the success/failure addCallbacks
+behavior.  Introduce an ICallbackProxy that can be used for this kind of
+behavior instead.  This change was driven by two desires.</p>
+<ul class="simple">
+<li>Don&#8217;t log the intermediate result.  It makes logs harder to read with
+unnecessary duplications of pertinent data hidden within unimportant
+differences in the log entries.</li>
+<li>Don&#8217;t unnecessarily remember errors in success/failure callbacks.  This can
+cause unnecessary failures in unusual situations.</li>
+</ul>
+<p>The callback proxy accepts callbacks, which are added to the selected job
+(success or failure) when the job is selected.</p>
+<p>This change introduces some hopefully trivial incompatibilities, which
+basically come down to the callback being a proxy, not a real job. Use the
+convenience properties <tt class="docutils literal"><span class="pre">success</span></tt> and <tt class="docutils literal"><span class="pre">failure</span></tt> on the proxy to look at
+the respective jobs. After the proxy is evaluated, the <tt class="docutils literal"><span class="pre">job</span></tt> attribute
+will hold the job that was actually run. <tt class="docutils literal"><span class="pre">status</span></tt> and <tt class="docutils literal"><span class="pre">result</span></tt> are
+conveniences to get the status and result of the selected job.</p>
+</li>
+<li><p class="first">Add <tt class="docutils literal"><span class="pre">parallel</span></tt> and <tt class="docutils literal"><span class="pre">serial</span></tt> convenience functions to zc.async.job to make
+it trivial to schedule and process decomposed jobs.</p>
+</li>
+<li><p class="first">Add <tt class="docutils literal"><span class="pre">start</span></tt> convenience function to zc.async.configure to make it trivial
+to start up a common-case configuration of a zc.async dispatcher.</p>
+</li>
+<li><p class="first">No longer use protected attributes of callbacks in <tt class="docutils literal"><span class="pre">resumeCallbacks</span></tt>.</p>
+</li>
+<li><p class="first">The &#8220;local&#8221; code is now moved out from the dispatcher module to
+threadlocal.  This is to recognize that the local code is now modified
+outside of the dispatcher module, as described in the next bullet.</p>
+</li>
+<li><p class="first">Jobs, when called, are responsible for setting the &#8220;local&#8221; job value.  This
+means that zc.async.local.getJob() always returns the currently running job,
+whether it is a top-level job (as before) or a callback (now).</p>
+</li>
+<li><p class="first">Start on S5 QuickStart presentation (see QUICKSTART_1_VIRTUALENV.txt in
+package).</p>
+</li>
+</ul>
+</div>
+<div class="section" id="id4">
+<h2 id="id4">1.3 (2008-07-04)<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h2>
+<ul class="simple">
+<li>added &#8220;Tips and Tricks&#8221; and incorporated into the PyPI page.</li>
+<li>added <tt class="docutils literal"><span class="pre">setUp</span></tt> and <tt class="docutils literal"><span class="pre">tearDown</span></tt> hooks to Job class so that code can run
+before and after the main job&#8217;s code.  The output of <tt class="docutils literal"><span class="pre">setUp</span></tt> is passed as
+an argument to <tt class="docutils literal"><span class="pre">tearDown</span></tt> so that one can pass state to the other, if
+needed. <tt class="docutils literal"><span class="pre">setUp</span></tt> is run immediately before the actual job call.
+<tt class="docutils literal"><span class="pre">tearDown</span></tt> runs after the transaction is committed, or after it was aborted
+if there was a failure.   A retry requested by a retry policy causes the
+methods to be run again.  A failure in <tt class="docutils literal"><span class="pre">setUp</span></tt> is considered to be a
+failure in the job, as far as the retryPolicy is concerned (i.e., the job
+calls the retry policy&#8217;s <tt class="docutils literal"><span class="pre">jobError</span></tt> method).  If <tt class="docutils literal"><span class="pre">setUp</span></tt> fails, the job
+is not called, bit <tt class="docutils literal"><span class="pre">tearDown</span></tt> is.  <tt class="docutils literal"><span class="pre">tearDown</span></tt> will fail with a critical
+log message, but then processing will continue.</li>
+<li>using the new <tt class="docutils literal"><span class="pre">setUp</span></tt> and <tt class="docutils literal"><span class="pre">tearDown</span></tt> hooks, added a Zope 3-specific Job
+subclass (see zc.async.z3.Job) that remembers the zope.app.component site and
+interaction participants when instantiated. These can be mutated. Then, when
+the job is run, the <tt class="docutils literal"><span class="pre">setUp</span></tt> sets up the site and a security interaction
+with the old participants, and then the <tt class="docutils literal"><span class="pre">tearDown</span></tt> tears it all down after
+the transaction has committed.</li>
+<li>changed retry policy logs to &#8220;WARNING&#8221; level, from &#8220;INFO&#8221; level.</li>
+<li>changed many dispatcher errors to &#8220;CRITICAL&#8221; level from &#8220;ERROR&#8221; level.</li>
+<li>added &#8220;CRITICAL&#8221; level logs for &#8220;other&#8221; commit retries on the
+RetryCommonForever retry policy.</li>
+<li>added <tt class="docutils literal"><span class="pre">remove</span></tt> method on queue.</li>
+<li>added helpers for setting up and tearing down Zope 3 functional tests
+(ftesting.py), and a discussion of how to write Zope 3 functional tests with
+layers (zope.app.testing.functional) in ftesting.txt.</li>
+<li>remove obsolete retry approach for success/failure callbacks
+(<tt class="docutils literal"><span class="pre">completeStartedJobArguments</span></tt>): it is now handled by retry policies.</li>
+<li>remove odd full-path self-references within the utils module.</li>
+<li>renamed <tt class="docutils literal"><span class="pre">zc.async.utils.try_transaction_five_times</span></tt> to
+<tt class="docutils literal"><span class="pre">zc.async.utils.try_five_times</span></tt>.</li>
+<li>doc improvements and fixes (thanks to Zvezdan Petkovic and Gintautas
+Miliauskas).</li>
+<li>the <tt class="docutils literal"><span class="pre">z3</span></tt> &#8220;extra&#8221; distutils target now explicitly depends on zope.security,
+zope.app.security, and zope.app.component.  This almost certainly does not
+increase the practical dependencies of the <tt class="docutils literal"><span class="pre">z3</span></tt> extras, but it does reflect
+new direct dependencies of the z3-specific modules in the package.</li>
+</ul>
+</div>
+<div class="section" id="id5">
+<h2 id="id5">1.2 (2008-06-20)<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h2>
+<ul class="simple">
+<li>made the log for finding an activated agent report the pertinent queue&#8217;s oid
+as an unpacked integer, rather than the packed string blob. Use
+<tt class="docutils literal"><span class="pre">ZODB.utils.p64</span></tt> to convert back to an oid that the ZODB will recognize.</li>
+<li>Bugfix: in failing a job, the job thought it was in its old agent, and the
+<tt class="docutils literal"><span class="pre">fail</span></tt> call failed. This is now tested by the first example in new doctest
+<tt class="docutils literal"><span class="pre">catastrophes.txt</span></tt>.</li>
+<li>jobs no longer default to a <tt class="docutils literal"><span class="pre">begin_by</span></tt> value of one hour after the
+<tt class="docutils literal"><span class="pre">begin_after</span></tt>.  The default now is no limit.</li>
+<li>Made dispatcher much more robust to transaction errors and ZEO
+ClientDisconnected errors.</li>
+<li>Jobs now use an IRetryPolicy to decide what to do on failure within a job,
+within the commit of the result, and if the job is interrupted.  This allows
+support of transactional jobs, transactional jobs that critically must be
+run to completion, and non-transactional jobs such as communicating with an
+external service.</li>
+<li>The default retry policy supports retries for ClientDisconnected errors,
+transaction errors, and interruptions.</li>
+<li><tt class="docutils literal"><span class="pre">job.txt</span></tt> has been expanded significantly to show error handling and the
+use of retry policies. New file <tt class="docutils literal"><span class="pre">catastrophes.txt</span></tt> shows handling of other
+catastrophes, such as interruptions to polling.</li>
+<li>job errors now go in the main zc.async.event log rather than in the
+zc.async.trace log.  Successes continue to go in the trace log.</li>
+<li>callback failures go to the main log as a CRITICAL error, by default.</li>
+<li><tt class="docutils literal"><span class="pre">handleInterrupt</span></tt> is the new protocol on jobs to inform them that they were
+active in a dispatcher that is now dead. They either fail or reschedule,
+depending on the associated IRetryPolicy for the job. If they reschedule,
+this should either be a datetime or timedelta. The job calls the agent&#8217;s
+<tt class="docutils literal"><span class="pre">reschedule</span></tt> method. If the timedelta is empty or negative, or the datetime
+is earlier than now, the job is put back in the queue with a new <tt class="docutils literal"><span class="pre">putBack</span></tt>
+method on the queue. This is intended to be the opposite of <tt class="docutils literal"><span class="pre">claim</span></tt>. Jobs
+put in the queue with <tt class="docutils literal"><span class="pre">putBack</span></tt> will be pulled out before any others.</li>
+<li>convert to using zope.minmax rather than locally defined <tt class="docutils literal"><span class="pre">Atom</span></tt>.</li>
+<li>Fix (and simplify) last_ping code so as to reduce unnecessarily writing the
+state of the parent DispatcherAgents collection to the database whenever the
+atom changed.</li>
+<li>Depends on new release of zc.twist (1.3)</li>
+<li>Switched dispatcher&#8217;s in-memory storage of job and poll information to be per
+job or per poll, respectively, rather than per time period, so as to try and
+make memory usage more predictable (for instance, whether a dispatcher is
+whipping through lots of jobs quickly, or doing work more slowly).</li>
+</ul>
+</div>
+<div class="section" id="id6">
+<h2 id="id6">1.1.1 (2008-05-14)<a class="headerlink" href="#id6" title="Permalink to this headline">¶</a></h2>
+<ul class="simple">
+<li>more README tweaks.</li>
+<li>converted all reports from the dispatcher, including the monitor output,
+to use &#8220;unpacked&#8221; integer oids.  This addresses a problem that simplejson
+was having in trying to interpret the packed string blobs as unicode, and
+then making zc.ngi fall over.  To get the object, then, you&#8217;ll need to
+use <tt class="docutils literal"><span class="pre">ZODB.utils.p64</span></tt>, like this:
+<tt class="docutils literal"><span class="pre">connection.get(ZODB.utils.p64(INTEGER_OID))</span></tt>, where <tt class="docutils literal"><span class="pre">INTEGER_OID</span></tt>
+indicates the integer oid of the object you want to examine.</li>
+<li>added several more tests for the monitor code.</li>
+<li>made the <tt class="docutils literal"><span class="pre">async</span> <span class="pre">jobs</span></tt> monitor command be &#8220;up to the minute&#8221;.  Before, it
+included all of the new and active jobs from the previous poll; now, it
+also filters out those that have since completed.</li>
+<li>The <tt class="docutils literal"><span class="pre">async</span> <span class="pre">job</span></tt> command was broken, as revealed by a new monitor test.
+Fixed, which also means we need a new version of zope.bforest (1.2) for a new
+feature there.</li>
+</ul>
+</div>
+<div class="section" id="id7">
+<h2 id="id7">1.1 (2008-04-24)<a class="headerlink" href="#id7" title="Permalink to this headline">¶</a></h2>
+<ul>
+<li><p class="first">Fired events when the IQueues and IQueue objects are installed by the
+QueueInstaller (thanks to Fred Drake).</p>
+</li>
+<li><p class="first">Dispatchers make agent threads keep their connections, so each connection&#8217;s
+object cache use is optimized if the agent regularly requests jobs with
+the same objects.</p>
+</li>
+<li><p class="first">README improved (thanks to Benji York and Sebastian Ware).</p>
+</li>
+<li><p class="first">Callbacks are logged at start in the trace log.</p>
+</li>
+<li><p class="first">All job results (including callbacks) are logged, including verbose
+tracebacks if the callback generated a failure.</p>
+</li>
+<li><p class="first">Had the ThreadedDispatcherInstaller subscriber stash the thread on the
+dispatcher, so you can shut down tests like this:</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.dispatcher</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">callFromThread</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">stop</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">thread</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="mf">3</span><span class="p">)</span>
+</pre></div>
+</li>
+<li><p class="first">Added <tt class="docutils literal"><span class="pre">getQueue</span></tt> to zc.async.local as a convenience (it does what you
+could already do: <tt class="docutils literal"><span class="pre">zc.async.local.getJob().queue</span></tt>).</p>
+</li>
+<li><p class="first">Clarified that <tt class="docutils literal"><span class="pre">IQueue.pull</span></tt> is the approved way of removing scheduled jobs
+from a queue in interfaces and README.</p>
+</li>
+<li><p class="first">reports in the logs of a job&#8217;s success or failure come before callbacks are
+started.</p>
+</li>
+<li><p class="first">Added a section showing how the basic_dispatcher_policy.zcml worked, which
+then pushed the former README_3 examples into README_3b.</p>
+</li>
+<li><p class="first">Put ZPL everywhere I was supposed to.</p>
+</li>
+<li><p class="first">Moved a number of helpful testing functions out of footnotes and into
+zc.async.testing, both so that zc.async tests don&#8217;t have to redefine them
+and client packages can reuse them.</p>
+</li>
+</ul>
+</div>
+<div class="section" id="id8">
+<h2 id="id8">1.0 (2008-04-09)<a class="headerlink" href="#id8" title="Permalink to this headline">¶</a></h2>
+<p>Initial release.</p>
+</div>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Table Of Contents</h3>
+            <ul>
+<li><a class="reference external" href="">Changes</a><ul>
+<li><a class="reference external" href="#id1">1.5.0 (2008-09-21)</a></li>
+<li><a class="reference external" href="#id2">1.4.1 (2008-07-30)</a></li>
+<li><a class="reference external" href="#id3">1.4.0 (2008-07-30)</a></li>
+<li><a class="reference external" href="#id4">1.3 (2008-07-04)</a></li>
+<li><a class="reference external" href="#id5">1.2 (2008-06-20)</a></li>
+<li><a class="reference external" href="#id6">1.1.1 (2008-05-14)</a></li>
+<li><a class="reference external" href="#id7">1.1 (2008-04-24)</a></li>
+<li><a class="reference external" href="#id8">1.0 (2008-04-09)</a></li>
+</ul>
+</li>
+</ul>
+
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="ftesting.html" title="previous chapter">Zope 3 Testing Tips and Tricks</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/CHANGES.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="ftesting.html" title="Zope 3 Testing Tips and Tricks"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/QUICKSTART_1_VIRTUALENV.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/QUICKSTART_1_VIRTUALENV.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/QUICKSTART_1_VIRTUALENV.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,1216 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Quickstart with virtualenv &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="next" title="Quickstart with Grok" href="QUICKSTART_2_GROK.html" />
+    <link rel="prev" title="zc.async" href="index.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="QUICKSTART_2_GROK.html" title="Quickstart with Grok"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="index.html" title="zc.async"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="id1">
+<span id="quickstart-with-virtualenv"></span><h1 id="id1"><span id="quickstart-with-virtualenv"></span>Quickstart with <tt class="docutils literal"><span class="pre">virtualenv</span></tt><a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h1>
+<div class="section" id="prerequisites">
+<h2 id="prerequisites">Prerequisites<a class="headerlink" href="#prerequisites" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="installation">
+<h3 id="installation">Installation<a class="headerlink" href="#installation" title="Permalink to this headline">¶</a></h3>
+<p>To start, install <a class="reference external" href="http://pypi.python.org/pypi/virtualenv"><tt class="docutils literal"><span class="pre">virtualenv</span></tt></a> and create a virtual environment for our
+experiments.</p>
+<div class="sidebar">
+<p class="first sidebar-title"><tt class="docutils literal"><span class="pre">virtualenv</span></tt> and <tt class="docutils literal"><span class="pre">zc.buildout</span></tt></p>
+<p class="last">I prefer <a class="reference external" href="http://pypi.python.org/pypi/zc.buildout"><tt class="docutils literal"><span class="pre">zc.buildout</span></tt></a> for production deployments, but <a class="reference external" href="http://pypi.python.org/pypi/virtualenv"><tt class="docutils literal"><span class="pre">virtualenv</span></tt></a> is
+very nice for quick experimentation.  The other quick-start uses
+<a class="reference external" href="http://pypi.python.org/pypi/zc.buildout"><tt class="docutils literal"><span class="pre">zc.buildout</span></tt></a>.</p>
+</div>
+<pre>$ easy_install virtualenv
+$ virtualenv quickstart</pre>
+<p>Install <a class="reference external" href="http://pypi.python.org/pypi/zc.async"><tt class="docutils literal"><span class="pre">zc.async</span></tt></a> in the virtual environment.</p>
+<pre>$ cd quickstart/
+$ ./bin/easy_install zc.async</pre>
+</div>
+<div class="section" id="dependencies">
+<h3 id="dependencies">Dependencies<a class="headerlink" href="#dependencies" title="Permalink to this headline">¶</a></h3>
+<div class="sidebar">
+<p class="first sidebar-title">Example Dependencies</p>
+<p>Here&#8217;s an example listing of the site-packages brought in by a run of this
+quick-start.</p>
+<pre>$ ls lib/python2.5/site-packages/
+Twisted-8.1.0-py2.5-macosx-10.5-i386.egg
+ZConfig-2.5.1-py2.5.egg
+ZODB3-3.8.1b5-py2.5-macosx-10.5-i386.egg
+easy-install.pth
+pytz-2008c-py2.5.egg
+rwproperty-1.0-py2.5.egg
+setuptools-0.6c8-py2.5.egg
+setuptools.pth
+uuid-1.30-py2.5.egg
+zc.async-1.4.0-py2.5.egg
+zc.dict-1.2.1-py2.5.egg
+zc.queue-1.1-py2.5.egg
+zc.twist-1.3-py2.5-macosx-10.5-i386.egg
+zdaemon-2.0.2-py2.5.egg
+zope.bforest-1.2-py2.5.egg
+zope.component-3.4.0-py2.5.egg
+zope.deferredimport-3.4.0-py2.5.egg
+zope.deprecation-3.4.0-py2.5.egg
+zope.event-3.4.0-py2.5.egg
+zope.i18nmessageid-3.4.3-py2.5-macosx-10.5-i386.egg
+zope.interface-3.4.1-py2.5-macosx-10.5-i386.egg
+zope.minmax-1.1.0-py2.5.egg
+zope.proxy-3.4.1-py2.5-macosx-10.5-i386.egg
+zope.testing-3.6.0-py2.5.egg</pre>
+</div>
+<p>This installed several packages.</p>
+<ul class="simple">
+<li>the <a class="reference external" href="http://pypi.python.org/pypi/ZODB3">ZODB</a>, an object database from the Zope project;</li>
+<li><a class="reference external" href="http://pypi.python.org/pypi/Twisted">Twisted</a>, a framework for networked applications;</li>
+<li>the component architecture from the Zope project;</li>
+<li>and a few smaller packages.</li>
+</ul>
+<p>All of these, and zc.async, are distributed under BSD-like licenses such as
+LGPL and <a class="reference external" href="http://en.wikipedia.org/wiki/Zope_Public_License">ZPL</a>.</p>
+</div>
+<div class="section" id="zeo-server">
+<h3 id="zeo-server">ZEO Server<a class="headerlink" href="#zeo-server" title="Permalink to this headline">¶</a></h3>
+<p>zc.async relies on a distributed ZODB technology called ZEO (&#8220;Zope Enterprise
+Objects&#8221;) to distribute work. ZEO has a central database server to which client
+processes connect.</p>
+<p>Let&#8217;s start the ZEO Server:</p>
+<pre>$ ./bin/runzeo -a 9999 -f test.fs &amp;</pre>
+<p>That starts a database server, accessible on port 9999 of your local machine,
+saving the data in the test.fs file.</p>
+</div>
+</div>
+<div class="section" id="starting-async">
+<h2 id="starting-async">Starting <tt class="docutils literal"><span class="pre">zc.async</span></tt><a class="headerlink" href="#starting-async" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="a-client">
+<h3 id="a-client">A Client<a class="headerlink" href="#a-client" title="Permalink to this headline">¶</a></h3>
+<p>Now let&#8217;s start a Python with a client connection to the database server.</p>
+<p>Start up <tt class="docutils literal"><span class="pre">bin/python</span></tt> (not your system python, but the one in virtualenv&#8217;s
+<tt class="docutils literal"><span class="pre">quickstart/bin</span></tt>):</p>
+<pre>$ ./bin/python</pre>
+<p>This will be our single client process.</p>
+<p>You might have many, each connecting to the main database server, and each able
+to perform and/or request <tt class="docutils literal"><span class="pre">zc.async</span></tt> jobs.</p>
+</div>
+<div class="section" id="database-connection">
+<h3 id="database-connection">Database Connection<a class="headerlink" href="#database-connection" title="Permalink to this headline">¶</a></h3>
+<p>Connect to the database.</p>
+<div class="highlight"><pre><span class="kn">import</span> <span class="nn">ZEO.ClientStorage</span>
+<span class="kn">import</span> <span class="nn">ZODB</span>
+<span class="n">storage</span> <span class="o">=</span> <span class="n">ZEO</span><span class="o">.</span><span class="n">ClientStorage</span><span class="o">.</span><span class="n">ClientStorage</span><span class="p">(</span>
+    <span class="p">(</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mf">9999</span><span class="p">))</span>
+<span class="n">db</span> <span class="o">=</span> <span class="n">ZODB</span><span class="o">.</span><span class="n">DB</span><span class="p">(</span><span class="n">storage</span><span class="p">)</span>
+</pre></div>
+</div>
+<div class="section" id="start-async">
+<h3 id="start-async">Start <tt class="docutils literal"><span class="pre">zc.async</span></tt><a class="headerlink" href="#start-async" title="Permalink to this headline">¶</a></h3>
+<div class="section" id="basics">
+<h4 id="basics">Basics<a class="headerlink" href="#basics" title="Permalink to this headline">¶</a></h4>
+<p>Now we do some basic configuration.  This first bit installs some default
+adapters.  You might not ever have to worry too much about them.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.configure</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">base</span><span class="p">()</span>
+</pre></div>
+</div>
+<div class="section" id="policy">
+<h4 id="policy">Policy<a class="headerlink" href="#policy" title="Permalink to this headline">¶</a></h4>
+<p>This second part is policy, and if you ever put zc.async in production, you&#8217;ll
+want to understand what&#8217;s going on here.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">poll_interval</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
+</pre></div>
+<p>Now the system has a <tt class="docutils literal"><span class="pre">dispatcher</span></tt> polling for jobs every second.  As we&#8217;ll
+see below, a <tt class="docutils literal"><span class="pre">dispatcher</span></tt> polls for jobs in one or more queues, to assign
+them to worker threads.</p>
+</div>
+</div>
+</div>
+<div class="section" id="using-async">
+<h2 id="using-async">Using <tt class="docutils literal"><span class="pre">zc.async</span></tt><a class="headerlink" href="#using-async" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="the-queue">
+<h3 id="the-queue">The Queue<a class="headerlink" href="#the-queue" title="Permalink to this headline">¶</a></h3>
+<p>The <tt class="docutils literal"><span class="pre">start</span></tt> function also installed a queue.  To get zc.async to do work, you
+put a job in a queue, and commit the transaction.</p>
+<p>First, let&#8217;s get the queue that we have installed.  We need to open a
+connection to the database.  Then we get the queue.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">q</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IQueue</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
+</pre></div>
+</div>
+<div class="section" id="a-job">
+<h3 id="a-job">A Job<a class="headerlink" href="#a-job" title="Permalink to this headline">¶</a></h3>
+<div class="sidebar">
+<p class="first sidebar-title">A Silly Example</p>
+<p>This is a silly example. Imagine instead that this was some really
+long-running job. Maybe you have lots of these jobs coming in, and you need
+to have many machines to claim jobs and perform them, so that you can
+scale. Maybe this job divides itself up into parallel or serial jobs, and
+this parent job isn&#8217;t done until all the children jobs run to completion.</p>
+<p class="last">Or maybe this is a silly example.</p>
+</div>
+<p>Let&#8217;s put a job in our queue.  This silly example will return the current time.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">time</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">)</span>
+</pre></div>
+<p>It&#8217;s not done yet.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="o">.</span><span class="n">result</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="o">.</span><span class="n">status</span>
+<span class="go">u&#39;pending-status&#39;</span>
+</pre></div>
+</div>
+<div class="section" id="a-transaction">
+<h3 id="a-transaction">A Transaction<a class="headerlink" href="#a-transaction" title="Permalink to this headline">¶</a></h3>
+<p>We have to commit the transaction for the dispatcher to see the job.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+</div>
+<div class="section" id="a-result">
+<h3 id="a-result">A Result<a class="headerlink" href="#a-result" title="Permalink to this headline">¶</a></h3>
+<p>Now wait a second and then try this.  &#8220;transaction.begin&#8221; will sync up our
+database with database changes made elsewhere.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="o">.</span><span class="n">result</span>
+<span class="go">1216179006.856108</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="o">.</span><span class="n">status</span>
+<span class="go">u&#39;completed-status&#39;</span>
+</pre></div>
+</div>
+<div class="section" id="another-job-and-closures">
+<h3 id="another-job-and-closures">Another Job (And Closures)<a class="headerlink" href="#another-job-and-closures" title="Permalink to this headline">¶</a></h3>
+<p>You can also make closures.  The Job class accepts arguments similarly to
+the Python 2.5 <tt class="xref docutils literal"><span class="pre">functools.partial()</span></tt>: <tt class="docutils literal"><span class="pre">Job(func,</span> <span class="pre">\*args,</span>
+<span class="pre">\*\*keywords)</span></tt>. This instantiates a new callable (a Job instance) with
+partial application of the given arguments and keywords.  You can then
+pass the job instance to the
+<tt class="xref docutils literal"><span class="pre">put()</span></tt> method.</p>
+<p>Generating RSA keys is actually a reasonable real-world use case for
+something like this.</p>
+<div class="highlight"><pre><span class="kn">import</span> <span class="nn">subprocess</span>
+<span class="n">j</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span>
+    <span class="n">subprocess</span><span class="o">.</span><span class="n">call</span><span class="p">,</span>
+    <span class="p">[</span><span class="s">&#39;openssl&#39;</span><span class="p">,</span> <span class="s">&#39;genrsa&#39;</span><span class="p">,</span> <span class="s">&#39;-out&#39;</span><span class="p">,</span>
+     <span class="s">&#39;key.pem&#39;</span><span class="p">,</span> <span class="s">&#39;1024&#39;</span><span class="p">]))</span>
+<span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<p>We need to begin the transaction to see the result...</p>
+<div class="highlight"><pre><span class="n">j</span><span class="o">.</span><span class="n">result</span>
+<span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="n">j</span><span class="o">.</span><span class="n">result</span>
+</pre></div>
+<p>...which in this case is simply <tt class="docutils literal"><span class="pre">0</span></tt>, indicating a successful UNIX
+process.</p>
+<div class="highlight"><pre><span class="mf">0</span>
+</pre></div>
+<p>We can open the file to show the result.</p>
+<div class="highlight"><pre><span class="n">subprocess</span><span class="o">.</span><span class="n">call</span><span class="p">([</span><span class="s">&#39;cat&#39;</span><span class="p">,</span> <span class="s">&#39;key.pem&#39;</span><span class="p">])</span>
+</pre></div>
+<p>This will show you the key, which should look something like this:</p>
+<pre>-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQCYAZW+HjDGJhRHnUlZZWqhrGOxU2K/RhssmcMs0JLnWI2cWmZ+
+...
+CEcz6ZbO8zm4AEGI/dqLicZh3bhunhflAovW6WxbNKLENQ==
+-----END RSA PRIVATE KEY-----
+0</pre>
+</div>
+</div>
+<div class="section" id="running-your-own-code">
+<h2 id="running-your-own-code">Running Your Own Code<a class="headerlink" href="#running-your-own-code" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="a-monte-carlo-simulation">
+<h3 id="a-monte-carlo-simulation">A Monte Carlo Simulation<a class="headerlink" href="#a-monte-carlo-simulation" title="Permalink to this headline">¶</a></h3>
+<div class="sidebar">
+<p class="first sidebar-title">Another Silly Example</p>
+<p class="last">Monte Carlo simulations are generally better suited to other tasks in
+real-world use; pi is typically calculated by <a class="reference external" href="http://en.wikipedia.org/wiki/Computing_Ï€">far more sophisticated
+means</a>.</p>
+</div>
+<p>We&#8217;ve now seen some simple examples from the standard library.  But how do you
+get your own work done?</p>
+<p>Let&#8217;s say we want to implement an old chestnut of a problem: use a <a class="reference external" href="http://en.wikipedia.org/wiki/Monte_Carlo_method">Monte
+Carlo simulation</a> (that is, &#8220;throwing darts and analyzing the results&#8221;) to
+<a class="reference external" href="http://math.fullerton.edu/mathews/n2003/MonteCarloPiMod.html">calculate pi</a>.  This will use <tt class="docutils literal"><span class="pre">zc.async</span></tt> much like the map-reduce approach
+of <a class="reference external" href="http://hadoop.apache.org/core/">Hadoop</a>: we will want to distribute the work of running the simulation to
+multiple machines so the result can be done faster.</p>
+</div>
+<div class="section" id="picklable-callables-and-arguments">
+<h3 id="picklable-callables-and-arguments">Picklable Callables and Arguments<a class="headerlink" href="#picklable-callables-and-arguments" title="Permalink to this headline">¶</a></h3>
+<p>You want a job to have a reference to your own callable, so the job will get
+the work you define performed.</p>
+<p>This reference, of the job to your callable, will need to be persisted in the
+database.</p>
+<div class="sidebar">
+<p class="first sidebar-title">ZODB Persistence Rules</p>
+<p>We don&#8217;t need these now.  But if you are really curious about ZODB
+persistence rules, here&#8217;s a start.</p>
+<ul class="simple">
+<li>Anything pickleable can be persisted.  Module global functions can be
+pickled (by name), for instance, and will come in handy for our examples.</li>
+<li>Custom classes should typically inherit from persistent.Persistent.
+Instances of persistent.Persistent subclasses are each stored as a single
+record in the database, and references to them are handled efficiently.</li>
+<li>Subclasses of persistent.Persistent produce instances that recognize when
+their <em>direct</em> attributes have been written, and inform the database that
+they are dirty, for the next committed transaction.  Standard,
+non-persistent-aware mutables such as lists, sets and dicts do <em>not</em> know
+when they have been changed, and are best avoided, or at least not
+mutated. A variety of persistent-aware replacements for these types are
+available.</li>
+<li>Use the transaction module to commit and abort transactions in the ZODB.</li>
+</ul>
+<p>More advanced concerns include the following.</p>
+<ul class="simple">
+<li>An optimistic implementation of <a class="reference external" href="http://en.wikipedia.org/wiki/Multiversion_concurrency_control">MVCC</a> in the ZODB means that you should be
+careful reading (not writing) one persistent.Persistent instance (record)
+and writing another persistent.Persistent instance with a value that
+depends on the first, read value within a transaction.</li>
+<li>Concurrent transactions can produce write conflict errors.  Transactions
+are often automatically retried in <tt class="docutils literal"><span class="pre">zc.async</span></tt> and other ZODB-based systems if
+conflict errors are encountered.  If this is not desired for a given
+<tt class="docutils literal"><span class="pre">zc.async</span></tt> job, it should be given a <tt class="docutils literal"><span class="pre">zc.async.job.NeverRetry</span></tt> retry
+policy, as discussed elsewhere in this documentation.</li>
+</ul>
+<p class="last">For ZODB documentation see <a class="reference external" href="http://www.zope.org/Wikis/ZODB/guide/zodb.html">http://www.zope.org/Wikis/ZODB/guide/zodb.html</a></p>
+</div>
+<p>Because zc.async uses the ZODB for its persistence mechanism, the ZODB&#8217;s
+persistence rules are in effect.</p>
+<p>Luckily, these are fairly simple.</p>
+<p>For now, we&#8217;ll stay as simple as it gets: if you use <em>module global functions</em>
+and <em>immutables</em>, and share software across instances, you&#8217;ll be fine.</p>
+<p>ZODB allows a lot more, if you&#8217;re willing to follow a few more rules, but that
+one rule will get us moving for this quick-start.</p>
+</div>
+<div class="section" id="process-uuids">
+<h3 id="process-uuids">Process UUIDs<a class="headerlink" href="#process-uuids" title="Permalink to this headline">¶</a></h3>
+<p>Exit the Python interpreter (control-D).  Look around in the directory
+(<tt class="docutils literal"><span class="pre">ls</span></tt>): you should see a few database files, the key.pem you created, and a
+new file: <tt class="docutils literal"><span class="pre">uuid.txt</span></tt>.  It should look something like this:</p>
+<pre>$ cat uuid.txt
+afd1e0d0-52e1-11dd-879b-0017f2c49bdd
+------------------------------------------------------------------------
+The value above (and this file) is created and used by the zc.async
+package. It is intended to uniquely identify this software instance when
+it is used to start a zc.async dispatcher.  This allows multiple
+dispatchers, each in its own software instance, to connect to a single
+database to do work.
+
+In order to decide where to look for this file (or to create it, if
+necessary), the module looks in ``os.environ['ZC_ASYNC_UUID']`` for a
+file name.
+
+If you are using zdaemon (http://pypi.python.org/pypi/zdaemon) to
+daemonize your process, you can set this in a zdaemon environment section
+of your zdaemon.conf. Supervisor (http://supervisord.org/) also provides
+this functionality. Other similar tools probably do as well.
+
+If the ``ZC_ASYNC_UUID`` is not found in the environment, it will use
+``os.path.join(os.getgwd(), 'uuid.txt')`` as the file name.
+
+To get a new identifier for this software instance, delete this file,
+restart Python, and import zc.async.instanceuuid.  This file will be
+recreated with a new value.</pre>
+<p>That text is intended to be self-explanatory, so hopefully it made sense to
+you.  We&#8217;ll handle these UUIDs more explicitly in a moment.</p>
+</div>
+<div class="section" id="make-a-file">
+<h3 id="make-a-file">Make a File<a class="headerlink" href="#make-a-file" title="Permalink to this headline">¶</a></h3>
+<p>Make a new Python file.  Let&#8217;s call it <tt class="docutils literal"><span class="pre">pi.py</span></tt>.
+Save this file in <tt class="docutils literal"><span class="pre">lib/python2.5/site-packages/</span></tt>.</p>
+<p>Use the following for the file content.</p>
+<div class="sidebar">
+<p class="first sidebar-title">Code Walkthrough</p>
+<p>This code walkthrough focuses on the elements needed to use <tt class="docutils literal"><span class="pre">zc.async</span></tt>,
+rather than the calculation of pi.  Numerous explanations of this Monte
+Carlo simulation to calculate pi are on the internet.  I liked <a class="reference external" href="http://math.fullerton.edu/mathews/n2003/MonteCarloPiMod.html">this one</a>.</p>
+<p>We begin with some imports.</p>
+<p>The <tt class="docutils literal"><span class="pre">generate_sample</span></tt> code will be run on multiple processes to produce
+samples for our Monte Carlo function simulation.  It is ignorant of
+<tt class="docutils literal"><span class="pre">zc.async</span></tt>.</p>
+<p>Once the samples are all done, we&#8217;ll reduce the results with
+<tt class="docutils literal"><span class="pre">process_samples</span></tt>.  It will return an approximation of pi, with
+accuracy strongly influenced by the total size of the aggregated samples,
+assuming even distribution of the random numbers.  As you&#8217;ll see
+soon, we&#8217;ll be using a <tt class="docutils literal"><span class="pre">zc.async</span></tt> convenience function for
+<tt class="xref docutils literal"><span class="pre">parallel()</span></tt> jobs that gives all of the completed
+jobs that have been running in parallel to this, the postprocessing
+call. Therefore, <tt class="docutils literal"><span class="pre">process_samples</span></tt> gets the <tt class="docutils literal"><span class="pre">result</span></tt> of
+<tt class="docutils literal"><span class="pre">generate_sample</span></tt> off of each job. Other than that, this function
+is ignorant of <tt class="docutils literal"><span class="pre">zc.async</span></tt>.</p>
+<p class="last">The last code block should look similar to our previous example of starting
+up a dispatcher, except this one uses the main, installed Twisted reactor,
+rather than a threaded instance.  It creates the database, configures
+zc.async, and starts the reactor.  We use a short poll_interval so we can
+have a perceptually snappy response in this quick start.</p>
+</div>
+<table class="highlighttable"><tr><td class="linenos"><pre> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32</pre></td><td class="code"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">random</span>
+<span class="kn">import</span> <span class="nn">math</span>
+
+<span class="kn">import</span> <span class="nn">ZEO.ClientStorage</span>
+<span class="kn">import</span> <span class="nn">ZODB</span>
+<span class="kn">import</span> <span class="nn">twisted.internet.reactor</span>
+
+<span class="kn">import</span> <span class="nn">zc.async.configure</span>
+
+<span class="k">def</span> <span class="nf">generate_sample</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mf">100000</span><span class="p">):</span>
+    <span class="n">count</span> <span class="o">=</span> <span class="mf">0</span>
+    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">size</span><span class="p">):</span>
+        <span class="k">if</span> <span class="n">math</span><span class="o">.</span><span class="n">hypot</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">())</span> <span class="o">&lt;</span> <span class="mf">1</span><span class="p">:</span>
+            <span class="n">count</span> <span class="o">+=</span> <span class="mf">1</span>
+    <span class="k">return</span> <span class="n">count</span><span class="p">,</span> <span class="n">size</span>
+
+<span class="k">def</span> <span class="nf">process_samples</span><span class="p">(</span><span class="o">*</span><span class="n">sample_jobs</span><span class="p">):</span>
+    <span class="n">count</span> <span class="o">=</span> <span class="mf">0</span> 
+    <span class="n">size</span> <span class="o">=</span> <span class="mf">0</span>
+    <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">sample_jobs</span><span class="p">:</span>
+        <span class="n">count</span> <span class="o">+=</span> <span class="n">j</span><span class="o">.</span><span class="n">result</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span>
+        <span class="n">size</span> <span class="o">+=</span> <span class="n">j</span><span class="o">.</span><span class="n">result</span><span class="p">[</span><span class="mf">1</span><span class="p">]</span>
+    <span class="k">return</span> <span class="mf">4.0</span> <span class="o">*</span> <span class="n">count</span> <span class="o">/</span> <span class="n">size</span>
+
+<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;__main__&#39;</span><span class="p">:</span>
+    <span class="n">storage</span> <span class="o">=</span> <span class="n">ZEO</span><span class="o">.</span><span class="n">ClientStorage</span><span class="o">.</span><span class="n">ClientStorage</span><span class="p">(</span>
+        <span class="p">(</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mf">9999</span><span class="p">))</span>
+    <span class="n">db</span> <span class="o">=</span> <span class="n">ZODB</span><span class="o">.</span><span class="n">DB</span><span class="p">(</span><span class="n">storage</span><span class="p">)</span>
+    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">base</span><span class="p">()</span>
+    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">start</span><span class="p">(</span>
+        <span class="n">db</span><span class="p">,</span> <span class="n">poll_interval</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">twisted</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+    <span class="n">twisted</span><span class="o">.</span><span class="n">internet</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
+</pre></div>
+</td></tr></table></div>
+<div class="section" id="our-first-monte-carlo-experiment">
+<h3 id="our-first-monte-carlo-experiment">Our First Monte Carlo Experiment<a class="headerlink" href="#our-first-monte-carlo-experiment" title="Permalink to this headline">¶</a></h3>
+<p>We&#8217;ll need the ZEO server running.  If you&#8217;ve gone through this file from the
+start, it should still be running.  If not, use this:</p>
+<pre>$ ./bin/runzeo -a 9999 -f test.fs &amp;</pre>
+<p>Now, for our first experiment with the Monte Carlo simulation, we&#8217;ll start a
+single worker process.</p>
+<p>Enter this in the terminal:</p>
+<pre>$ ./bin/python lib/python2.5/site-packages/pi.py &amp;</pre>
+<p>That will start our worker process.  Now let&#8217;s start an interpreter.</p>
+<pre>$ ./bin/python</pre>
+<p>Now get a database and a connection, as we&#8217;ve seen before.  We&#8217;ll also set up
+the base zc.async configuration.</p>
+<div class="highlight"><pre><span class="kn">import</span> <span class="nn">ZEO.ClientStorage</span>
+<span class="kn">import</span> <span class="nn">ZODB</span>
+<span class="n">storage</span> <span class="o">=</span> <span class="n">ZEO</span><span class="o">.</span><span class="n">ClientStorage</span><span class="o">.</span><span class="n">ClientStorage</span><span class="p">(</span>
+    <span class="p">(</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mf">9999</span><span class="p">))</span>
+<span class="n">db</span> <span class="o">=</span> <span class="n">ZODB</span><span class="o">.</span><span class="n">DB</span><span class="p">(</span><span class="n">storage</span><span class="p">)</span>
+<span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+<span class="kn">import</span> <span class="nn">zc.async.configure</span>
+<span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">base</span><span class="p">()</span>
+</pre></div>
+<p>We don&#8217;t have any adapters installed, so <tt class="docutils literal"><span class="pre">zc.async.interfaces.IQueue(conn)</span></tt>
+won&#8217;t work.  This will though, and still looks pretty good:</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.queue</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">q</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">getDefaultQueue</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
+</pre></div>
+<p>Now we can start some jobs in <tt class="xref docutils literal"><span class="pre">parallel()</span></tt>.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">pi</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.job</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">parallel</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">pi</span><span class="o">.</span><span class="n">generate_sample</span><span class="p">,</span> <span class="n">pi</span><span class="o">.</span><span class="n">generate_sample</span><span class="p">,</span> <span class="n">pi</span><span class="o">.</span><span class="n">generate_sample</span><span class="p">,</span>
+<span class="gp">... </span>    <span class="n">postprocess</span><span class="o">=</span><span class="n">pi</span><span class="o">.</span><span class="n">process_samples</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<p>Wait a few seconds.  If the result is empty (None), begin the transaction again
+and check the result again.  Eventually, these next two lines should give you a
+result: an approximation of pi.</p>
+<div class="highlight"><pre><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="n">j</span><span class="o">.</span><span class="n">result</span>
+</pre></div>
+<p>For one run, I got <tt class="docutils literal"><span class="pre">3.1386666666666665</span></tt>.  Cool.</p>
+</div>
+<div class="section" id="closures">
+<h3 id="closures">Closures<a class="headerlink" href="#closures" title="Permalink to this headline">¶</a></h3>
+<p>We&#8217;ve already seen Jobs used as closures in the openssl example.  You can also
+use them to pass a different <tt class="docutils literal"><span class="pre">size</span></tt> argument to <tt class="docutils literal"><span class="pre">generate_sample</span></tt>.</p>
+<p>Let&#8217;s try it.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">j</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">parallel</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">pi</span><span class="o">.</span><span class="n">generate_sample</span><span class="p">,</span> <span class="mf">1000000</span><span class="p">),</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">pi</span><span class="o">.</span><span class="n">generate_sample</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mf">1000000</span><span class="p">),</span>
+<span class="gp">... </span>    <span class="n">postprocess</span><span class="o">=</span><span class="n">pi</span><span class="o">.</span><span class="n">process_samples</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<p>Wait a bit, again.  Retry these two lines until you get a result.  It should
+be well under a minute on most machines.</p>
+<div class="highlight"><pre><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="n">j</span><span class="o">.</span><span class="n">result</span>
+</pre></div>
+<p>My run got <tt class="docutils literal"><span class="pre">3.1434359999999999</span></tt>.  Cool.</p>
+</div>
+<div class="section" id="configuration">
+<h3 id="configuration">Configuration<a class="headerlink" href="#configuration" title="Permalink to this headline">¶</a></h3>
+<p>Unfortunately, the parallel runs we&#8217;ve done so far would actually make the
+calculation go slower than if we did it in a single function call!  That&#8217;s
+because we ran it in a single Python worker process, and the overhead of
+threads just makes the jobs a bit less efficient.  Threads help for some uses
+of <tt class="docutils literal"><span class="pre">zc.async</span></tt>, but not this one.</p>
+<p>Let&#8217;s assume you are running these experiments on a machine with two processor
+cores.  We should actually start two worker processes then, one for each core.
+We&#8217;ll need to make sure each worker process has its own OID.</p>
+<p>Moreover, we need to configure each process to only take one
+<tt class="docutils literal"><span class="pre">generate_sample</span></tt> job at a time.  Let&#8217;s adjust our code to do that.</p>
+<div class="section" id="agents">
+<h4 id="agents">Agents<a class="headerlink" href="#agents" title="Permalink to this headline">¶</a></h4>
+<p>A worker process regularly polls the database for new jobs.  The software
+component that polls is called a <tt class="docutils literal"><span class="pre">dispatcher</span></tt>. Dispatchers look in the
+database to ask their personal <tt class="docutils literal"><span class="pre">agent</span></tt> (or agents) to determine what
+they should get their threads to do.  Think of the <tt class="docutils literal"><span class="pre">agent</span></tt> as a
+&#8220;<a class="reference external" href="http://en.wikipedia.org/wiki/Talent_agent">talent agent</a>&#8221; or a &#8220;booking agent&#8221; for the process.</p>
+<p>By default, using the zc.async.configure helpers, each dispatcher is given a
+single agent that will choose the first job in the queue, and that wants to run
+no more than three jobs at a time.</p>
+<p>For our Monte Carlo job, we&#8217;ll give each process an additional agent. The
+new agent will only accept up to one instance of a <tt class="docutils literal"><span class="pre">generate_sample</span></tt>
+job at a time.</p>
+<p>We will also reconfigure the existing agent to accept up to three of
+anything <em>except</em> <tt class="docutils literal"><span class="pre">generate_sample</span></tt>.</p>
+<p>We&#8217;ll do that in our file.  Here&#8217;s the revised version.  The only changes
+are the imports, the three new functions, and setting up
+<tt class="docutils literal"><span class="pre">install_agent</span></tt> in the <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span> <span class="pre">'__main__':</span></tt> block.</p>
+<div class="sidebar">
+<p class="first sidebar-title">Code Walkthrough for Changes</p>
+<p>We import three new modules from <tt class="docutils literal"><span class="pre">zc.async</span></tt>, :mod:zc.async.queue,
+:mod:zc.async.instanceuuid, and :mod:zc.async.agent.  The
+<tt class="xref docutils literal"><span class="pre">IAgent</span></tt>
+implementation (<tt class="xref docutils literal"><span class="pre">zc.async.agent.Agent</span></tt>) uses a function called a
+<tt class="xref docutils literal"><span class="pre">chooser</span></tt> to determine its
+policy for choosing agents.  We define two chooser functions, one for
+each agent: <tt class="docutils literal"><span class="pre">choose_generate_sample</span></tt> and <tt class="docutils literal"><span class="pre">choose_another</span></tt>.  Then
+we have a function <tt class="docutils literal"><span class="pre">install_agent</span></tt> to set up the old agent to use
+<tt class="docutils literal"><span class="pre">choose_another</span></tt>, and create a new agent of <tt class="docutils literal"><span class="pre">size=1</span></tt> with the
+<tt class="docutils literal"><span class="pre">choose_generate_sample</span></tt> chooser.  We make sure this function is
+installed to run when the Twisted reactor starts. That&#8217;s it.</p>
+<p>It&#8217;s important to note that the references to these functions are
+persistent, and by name.  If you change the location or name of these
+functions, you will need to keep the old names and locations around,
+at least for long enough to switch your agents to use the new names.
+This is a general pattern for module globals&#8211;functions and
+classes&#8211;to which the ZODB has direct references or instances.</p>
+<p>We could have accomplished the same basic policy changes with several
+different agent configurations. For instance, we could have written a
+chooser that looked through its agents&#8217;s current jobs to verify that
+it did not have another <tt class="docutils literal"><span class="pre">generate_sample</span></tt>.</p>
+<p class="last">The only trick to this kind of agent configuration is that you always
+want to have at least some catch-all dispatchers who are willing to do
+just about any jobs (what our <tt class="docutils literal"><span class="pre">choose_another</span></tt> choose accomplishes).
+This supports jobs that the <tt class="docutils literal"><span class="pre">zc.async</span></tt> system sometimes needs to run for
+exceptional circumstances.</p>
+</div>
+<table class="highlighttable"><tr><td class="linenos"><pre> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64</pre></td><td class="code"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">random</span>
+<span class="kn">import</span> <span class="nn">math</span>
+
+<span class="kn">import</span> <span class="nn">ZEO.ClientStorage</span>
+<span class="kn">import</span> <span class="nn">ZODB</span>
+<span class="kn">import</span> <span class="nn">transaction</span>
+<span class="kn">import</span> <span class="nn">twisted.internet.reactor</span>
+
+<span class="kn">import</span> <span class="nn">zc.async.configure</span>
+<span class="kn">import</span> <span class="nn">zc.async.queue</span>
+<span class="kn">import</span> <span class="nn">zc.async.instanceuuid</span>
+<span class="kn">import</span> <span class="nn">zc.async.agent</span>
+
+<span class="k">def</span> <span class="nf">generate_sample</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mf">100000</span><span class="p">):</span>
+    <span class="n">count</span> <span class="o">=</span> <span class="mf">0</span>
+    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">size</span><span class="p">):</span>
+        <span class="k">if</span> <span class="n">math</span><span class="o">.</span><span class="n">hypot</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">())</span> <span class="o">&lt;</span> <span class="mf">1</span><span class="p">:</span>
+            <span class="n">count</span> <span class="o">+=</span> <span class="mf">1</span>
+    <span class="k">return</span> <span class="n">count</span><span class="p">,</span> <span class="n">size</span>
+
+<span class="k">def</span> <span class="nf">process_samples</span><span class="p">(</span><span class="o">*</span><span class="n">sample_jobs</span><span class="p">):</span>
+    <span class="n">count</span> <span class="o">=</span> <span class="mf">0</span> 
+    <span class="n">size</span> <span class="o">=</span> <span class="mf">0</span>
+    <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">sample_jobs</span><span class="p">:</span>
+        <span class="n">count</span> <span class="o">+=</span> <span class="n">j</span><span class="o">.</span><span class="n">result</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span>
+        <span class="n">size</span> <span class="o">+=</span> <span class="n">j</span><span class="o">.</span><span class="n">result</span><span class="p">[</span><span class="mf">1</span><span class="p">]</span>
+    <span class="k">return</span> <span class="mf">4.0</span> <span class="o">*</span> <span class="n">count</span> <span class="o">/</span> <span class="n">size</span>
+
+<span class="k">def</span> <span class="nf">choose_generate_sample</span><span class="p">(</span><span class="n">agent</span><span class="p">):</span>
+    <span class="k">return</span> <span class="n">agent</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">(</span>
+        <span class="k">lambda</span> <span class="n">j</span><span class="p">:</span> <span class="n">j</span><span class="o">.</span><span class="n">callable</span><span class="o">.</span><span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;generate_sample&#39;</span><span class="p">)</span>
+
+<span class="k">def</span> <span class="nf">choose_another</span><span class="p">(</span><span class="n">agent</span><span class="p">):</span>
+    <span class="k">return</span> <span class="n">agent</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">(</span>
+        <span class="k">lambda</span> <span class="n">j</span><span class="p">:</span> <span class="n">j</span><span class="o">.</span><span class="n">callable</span><span class="o">.</span><span class="n">__name__</span> <span class="o">!=</span> <span class="s">&#39;generate_sample&#39;</span><span class="p">)</span>
+
+<span class="k">def</span> <span class="nf">install_agent</span><span class="p">(</span><span class="n">db</span><span class="p">):</span>
+    <span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+    <span class="k">try</span><span class="p">:</span>
+        <span class="n">q</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">getDefaultQueue</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
+        <span class="k">try</span><span class="p">:</span>
+            <span class="n">dispatcher</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">dispatchers</span><span class="p">[</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">instanceuuid</span><span class="o">.</span><span class="n">UUID</span><span class="p">]</span>
+        <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
+            <span class="n">twisted</span><span class="o">.</span><span class="n">internet</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">install_agent</span><span class="p">,</span> <span class="n">db</span><span class="p">)</span>
+        <span class="k">else</span><span class="p">:</span>
+            <span class="k">if</span> <span class="s">&#39;generate_sample&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">dispatcher</span><span class="p">:</span>
+                <span class="n">agent</span> <span class="o">=</span> <span class="n">dispatcher</span><span class="p">[</span><span class="s">&#39;main&#39;</span><span class="p">]</span>
+                <span class="n">agent</span><span class="o">.</span><span class="n">chooser</span> <span class="o">=</span> <span class="n">choose_another</span>
+                <span class="n">dispatcher</span><span class="p">[</span><span class="s">&#39;generate_sample&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">agent</span><span class="o">.</span><span class="n">Agent</span><span class="p">(</span>
+                    <span class="n">choose_generate_sample</span><span class="p">,</span> <span class="mf">1</span><span class="p">)</span>
+                <span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+    <span class="k">finally</span><span class="p">:</span>
+        <span class="n">transaction</span><span class="o">.</span><span class="n">abort</span><span class="p">()</span>
+        <span class="n">conn</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+
+<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;__main__&#39;</span><span class="p">:</span>
+    <span class="n">storage</span> <span class="o">=</span> <span class="n">ZEO</span><span class="o">.</span><span class="n">ClientStorage</span><span class="o">.</span><span class="n">ClientStorage</span><span class="p">(</span>
+        <span class="p">(</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mf">9999</span><span class="p">))</span>
+    <span class="n">db</span> <span class="o">=</span> <span class="n">ZODB</span><span class="o">.</span><span class="n">DB</span><span class="p">(</span><span class="n">storage</span><span class="p">)</span>
+    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">base</span><span class="p">()</span>
+    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">start</span><span class="p">(</span>
+        <span class="n">db</span><span class="p">,</span> <span class="n">poll_interval</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">twisted</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+    <span class="n">twisted</span><span class="o">.</span><span class="n">internet</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">callWhenRunning</span><span class="p">(</span><span class="n">install_agent</span><span class="p">,</span> <span class="n">db</span><span class="p">)</span>
+    <span class="n">twisted</span><span class="o">.</span><span class="n">internet</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
+</pre></div>
+</td></tr></table></div>
+</div>
+<div class="section" id="demonstration">
+<h3 id="demonstration">Demonstration<a class="headerlink" href="#demonstration" title="Permalink to this headline">¶</a></h3>
+<p>Now let&#8217;s start up our workers, and see how they work.  We&#8217;re going to
+have two workers now, and they will each need separate UUIDs.  A really
+simple approach will be to make two separate working directories for the
+two worker processes.  (We also could use the environmental variable,
+<tt class="docutils literal"><span class="pre">ZC_ASYNC_UUID</span></tt>, described in the <a class="reference internal" href="#process-uuids">Process UUIDs</a> section above.)</p>
+<pre>$ mkdir worker1
+$ mv uuid.txt worker1
+$ cd worker1
+$ ../bin/python ../lib/python2.5/site-packages/pi.py &amp;
+$ cd ..
+$ mkdir worker2
+$ cd worker2
+$ ../bin/python ../lib/python2.5/site-packages/pi.py &amp;</pre>
+<p>Now we&#8217;ll start the Python process in which we will test our code.  We&#8217;ll move
+to the main directory, but as long as we don&#8217;t start another worker, it doesn&#8217;t
+really matter.</p>
+<pre>$ cd ..
+$ ./bin/python</pre>
+<p>And now, our test.</p>
+<div class="highlight"><pre><span class="kn">import</span> <span class="nn">ZEO.ClientStorage</span>
+<span class="kn">import</span> <span class="nn">ZODB</span>
+<span class="n">storage</span> <span class="o">=</span> <span class="n">ZEO</span><span class="o">.</span><span class="n">ClientStorage</span><span class="o">.</span><span class="n">ClientStorage</span><span class="p">(</span>
+    <span class="p">(</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mf">9999</span><span class="p">))</span>
+<span class="n">db</span> <span class="o">=</span> <span class="n">ZODB</span><span class="o">.</span><span class="n">DB</span><span class="p">(</span><span class="n">storage</span><span class="p">)</span>
+<span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+<span class="kn">import</span> <span class="nn">zc.async.configure</span>
+<span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">base</span><span class="p">()</span>
+<span class="kn">import</span> <span class="nn">pi</span>
+<span class="kn">import</span> <span class="nn">zc.async.job</span>
+<span class="kn">import</span> <span class="nn">zc.async.queue</span>
+<span class="n">q</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">getDefaultQueue</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
+<span class="n">j</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">parallel</span><span class="p">(</span>
+    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">pi</span><span class="o">.</span><span class="n">generate_sample</span><span class="p">,</span> <span class="mf">5000000</span><span class="p">),</span>
+    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">pi</span><span class="o">.</span><span class="n">generate_sample</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mf">5000000</span><span class="p">),</span>
+    <span class="n">postprocess</span><span class="o">=</span><span class="n">pi</span><span class="o">.</span><span class="n">process_samples</span><span class="p">))</span>
+<span class="kn">import</span> <span class="nn">transaction</span>
+<span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<p>Wait a few seconds and then try these lines.</p>
+<div class="highlight"><pre><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="n">j</span><span class="o">.</span><span class="n">result</span>
+</pre></div>
+<p>If the result is empty (None), repeat those two lines again (thatæ is, begin
+the transaction again and check the result again).  Eventually, these lines
+should give you a result: an approximation of pi.</p>
+<p>Just to prove to ourselves that we saved some time, let&#8217;s do a comparison test:
+the same number of samples, but not in parallel.</p>
+<div class="highlight"><pre><span class="n">j2</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">parallel</span><span class="p">(</span>
+    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">pi</span><span class="o">.</span><span class="n">generate_sample</span><span class="p">,</span> <span class="mf">10000000</span><span class="p">),</span>
+    <span class="n">postprocess</span><span class="o">=</span><span class="n">pi</span><span class="o">.</span><span class="n">process_samples</span><span class="p">))</span>
+<span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="n">j2</span><span class="o">.</span><span class="n">result</span>
+</pre></div>
+<p>Once both jobs are complete, compare their run-time.</p>
+<div class="highlight"><pre><span class="n">j</span><span class="o">.</span><span class="n">active_end</span> <span class="o">-</span> <span class="n">j</span><span class="o">.</span><span class="n">active_start</span>
+<span class="n">j2</span><span class="o">.</span><span class="n">active_end</span> <span class="o">-</span> <span class="n">j2</span><span class="o">.</span><span class="n">active_start</span>
+</pre></div>
+<p>On my machine, even in this simple, short example, running in parallel
+with a job per processor/core took 7.8 seconds, while running all in one
+process took 13.4 seconds.</p>
+</div>
+</div>
+<div class="section" id="monitoring">
+<h2 id="monitoring">Monitoring<a class="headerlink" href="#monitoring" title="Permalink to this headline">¶</a></h2>
+<p>Soon, you may want to be able to monitor or introspect what&#8217;s going on in your
+zc.async work.  The package provides several tools to do that.  We&#8217;ll take a
+look at a few here.</p>
+<p>We will be turning on a monitor port.  This port should be protected behind a
+firewall, like your ZEO ports.</p>
+<p>If you like the functionality that we describe here but would prefer to expose
+it in a different manner, note that most of the Python functions in
+<tt class="docutils literal"><span class="pre">monitor.py</span></tt> and <tt class="docutils literal"><span class="pre">monitordb.py</span></tt> power the zc.async commands in the monitor
+port, and can be used without the monitor itself.</p>
+<p>To enable the monitoring port, we need to install some extra dependencies for
+zc.async: &#8220;[monitor]&#8221;.  Exit the Python interpreter an make sure you are in the
+top &#8220;quickstart&#8221; directory, and then enter this command:</p>
+<blockquote>
+$ ./bin/easy_install zc.async[monitor]</blockquote>
+<p>If you take a glance at the output, you&#8217;ll see we&#8217;ve only added a few
+dependencies: <tt class="docutils literal"><span class="pre">simplejson</span></tt>, <tt class="docutils literal"><span class="pre">zc.ngi</span></tt>, and <tt class="docutils literal"><span class="pre">zc.monitor</span></tt>.</p>
+<p>Now let&#8217;s turn on the port and the zc.async commands.</p>
+<p>At the top of the <tt class="docutils literal"><span class="pre">pi.py</span></tt> file, add some imports:</p>
+<div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span>
+<span class="kn">import</span> <span class="nn">zope.component</span>
+<span class="kn">import</span> <span class="nn">zc.monitor</span>
+<span class="kn">import</span> <span class="nn">zc.monitor.interfaces</span>
+<span class="kn">import</span> <span class="nn">zc.async.monitor</span>
+<span class="kn">import</span> <span class="nn">zc.async.monitordb</span>
+</pre></div>
+<p>Then down in the <tt class="docutils literal"><span class="pre">if</span> <span class="pre">__name__</span> <span class="pre">==</span> <span class="pre">'__main__':</span></tt> block, add these lines at the
+top.</p>
+<div class="highlight"><pre><span class="n">monitor_port</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;MONITOR_PORT&#39;</span><span class="p">)</span>
+<span class="k">if</span> <span class="n">monitor_port</span><span class="p">:</span>
+    <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">interactive</span><span class="p">,</span> <span class="n">zc</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">quit</span><span class="p">,</span> <span class="n">zc</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">help</span><span class="p">,</span>
+              <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">async</span><span class="p">,</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">monitordb</span><span class="o">.</span><span class="n">asyncdb</span><span class="p">):</span>
+        <span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideUtility</span><span class="p">(</span>
+            <span class="n">f</span><span class="p">,</span> <span class="n">zc</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IMonitorPlugin</span><span class="p">,</span> <span class="n">f</span><span class="o">.</span><span class="n">__name__</span><span class="p">)</span>
+    <span class="n">zc</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">monitor_port</span><span class="p">))</span>
+</pre></div>
+<p>The file should look like this now.</p>
+<table class="highlighttable"><tr><td class="linenos"><pre> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77</pre></td><td class="code"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">os</span>
+<span class="kn">import</span> <span class="nn">random</span>
+<span class="kn">import</span> <span class="nn">math</span>
+
+<span class="kn">import</span> <span class="nn">ZEO.ClientStorage</span>
+<span class="kn">import</span> <span class="nn">ZODB</span>
+<span class="kn">import</span> <span class="nn">transaction</span>
+<span class="kn">import</span> <span class="nn">twisted.internet.reactor</span>
+<span class="kn">import</span> <span class="nn">zc.monitor</span>
+<span class="kn">import</span> <span class="nn">zc.monitor.interfaces</span>
+<span class="kn">import</span> <span class="nn">zope.component</span>
+
+<span class="kn">import</span> <span class="nn">zc.async.configure</span>
+<span class="kn">import</span> <span class="nn">zc.async.queue</span>
+<span class="kn">import</span> <span class="nn">zc.async.instanceuuid</span>
+<span class="kn">import</span> <span class="nn">zc.async.agent</span>
+<span class="kn">import</span> <span class="nn">zc.async.monitor</span>
+<span class="kn">import</span> <span class="nn">zc.async.monitordb</span>
+
+<span class="k">def</span> <span class="nf">generate_sample</span><span class="p">(</span><span class="n">size</span><span class="o">=</span><span class="mf">100000</span><span class="p">):</span>
+    <span class="n">count</span> <span class="o">=</span> <span class="mf">0</span>
+    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">size</span><span class="p">):</span>
+        <span class="k">if</span> <span class="n">math</span><span class="o">.</span><span class="n">hypot</span><span class="p">(</span><span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">(),</span> <span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">())</span> <span class="o">&lt;</span> <span class="mf">1</span><span class="p">:</span>
+            <span class="n">count</span> <span class="o">+=</span> <span class="mf">1</span>
+    <span class="k">return</span> <span class="n">count</span><span class="p">,</span> <span class="n">size</span>
+
+<span class="k">def</span> <span class="nf">process_samples</span><span class="p">(</span><span class="o">*</span><span class="n">sample_jobs</span><span class="p">):</span>
+    <span class="n">count</span> <span class="o">=</span> <span class="mf">0</span> 
+    <span class="n">size</span> <span class="o">=</span> <span class="mf">0</span>
+    <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">sample_jobs</span><span class="p">:</span>
+        <span class="n">count</span> <span class="o">+=</span> <span class="n">j</span><span class="o">.</span><span class="n">result</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span>
+        <span class="n">size</span> <span class="o">+=</span> <span class="n">j</span><span class="o">.</span><span class="n">result</span><span class="p">[</span><span class="mf">1</span><span class="p">]</span>
+    <span class="k">return</span> <span class="mf">4.0</span> <span class="o">*</span> <span class="n">count</span> <span class="o">/</span> <span class="n">size</span>
+
+<span class="k">def</span> <span class="nf">choose_generate_sample</span><span class="p">(</span><span class="n">agent</span><span class="p">):</span>
+    <span class="k">return</span> <span class="n">agent</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">(</span>
+        <span class="k">lambda</span> <span class="n">j</span><span class="p">:</span> <span class="n">j</span><span class="o">.</span><span class="n">callable</span><span class="o">.</span><span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;generate_sample&#39;</span><span class="p">)</span>
+
+<span class="k">def</span> <span class="nf">choose_another</span><span class="p">(</span><span class="n">agent</span><span class="p">):</span>
+    <span class="k">return</span> <span class="n">agent</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">(</span>
+        <span class="k">lambda</span> <span class="n">j</span><span class="p">:</span> <span class="n">j</span><span class="o">.</span><span class="n">callable</span><span class="o">.</span><span class="n">__name__</span> <span class="o">!=</span> <span class="s">&#39;generate_sample&#39;</span><span class="p">)</span>
+
+<span class="k">def</span> <span class="nf">install_agent</span><span class="p">(</span><span class="n">db</span><span class="p">):</span>
+    <span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+    <span class="k">try</span><span class="p">:</span>
+        <span class="n">q</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">getDefaultQueue</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
+        <span class="k">try</span><span class="p">:</span>
+            <span class="n">dispatcher</span> <span class="o">=</span> <span class="n">q</span><span class="o">.</span><span class="n">dispatchers</span><span class="p">[</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">instanceuuid</span><span class="o">.</span><span class="n">UUID</span><span class="p">]</span>
+        <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
+            <span class="n">twisted</span><span class="o">.</span><span class="n">internet</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">callLater</span><span class="p">(</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">install_agent</span><span class="p">,</span> <span class="n">db</span><span class="p">)</span>
+        <span class="k">else</span><span class="p">:</span>
+            <span class="k">if</span> <span class="s">&#39;generate_sample&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">dispatcher</span><span class="p">:</span>
+                <span class="n">agent</span> <span class="o">=</span> <span class="n">dispatcher</span><span class="p">[</span><span class="s">&#39;main&#39;</span><span class="p">]</span>
+                <span class="n">agent</span><span class="o">.</span><span class="n">chooser</span> <span class="o">=</span> <span class="n">choose_another</span>
+                <span class="n">dispatcher</span><span class="p">[</span><span class="s">&#39;generate_sample&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">agent</span><span class="o">.</span><span class="n">Agent</span><span class="p">(</span>
+                    <span class="n">choose_generate_sample</span><span class="p">,</span> <span class="mf">1</span><span class="p">)</span>
+                <span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+    <span class="k">finally</span><span class="p">:</span>
+        <span class="n">transaction</span><span class="o">.</span><span class="n">abort</span><span class="p">()</span>
+        <span class="n">conn</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+
+<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&#39;__main__&#39;</span><span class="p">:</span>
+    <span class="n">monitor_port</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;MONITOR_PORT&#39;</span><span class="p">)</span>
+    <span class="k">if</span> <span class="n">monitor_port</span><span class="p">:</span>
+        <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">interactive</span><span class="p">,</span> <span class="n">zc</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">quit</span><span class="p">,</span> <span class="n">zc</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">help</span><span class="p">,</span>
+                  <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">async</span><span class="p">,</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">monitordb</span><span class="o">.</span><span class="n">asyncdb</span><span class="p">):</span>
+            <span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideUtility</span><span class="p">(</span>
+                <span class="n">f</span><span class="p">,</span> <span class="n">zc</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IMonitorPlugin</span><span class="p">,</span> <span class="n">f</span><span class="o">.</span><span class="n">__name__</span><span class="p">)</span>
+        <span class="n">zc</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="n">start</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">monitor_port</span><span class="p">))</span>
+    <span class="n">storage</span> <span class="o">=</span> <span class="n">ZEO</span><span class="o">.</span><span class="n">ClientStorage</span><span class="o">.</span><span class="n">ClientStorage</span><span class="p">(</span>
+        <span class="p">(</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="mf">9999</span><span class="p">))</span>
+    <span class="n">db</span> <span class="o">=</span> <span class="n">ZODB</span><span class="o">.</span><span class="n">DB</span><span class="p">(</span><span class="n">storage</span><span class="p">)</span>
+    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">base</span><span class="p">()</span>
+    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">start</span><span class="p">(</span>
+        <span class="n">db</span><span class="p">,</span> <span class="n">poll_interval</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">twisted</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+    <span class="n">twisted</span><span class="o">.</span><span class="n">internet</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">callWhenRunning</span><span class="p">(</span><span class="n">install_agent</span><span class="p">,</span> <span class="n">db</span><span class="p">)</span>
+    <span class="n">twisted</span><span class="o">.</span><span class="n">internet</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
+</pre></div>
+</td></tr></table><p>Now stop and restart your two worker instances, this time providing two
+different ports in the environment for each worker.  Here&#8217;s one way to do it.
+First we&#8217;ll shut down the previous instances.  If you used the lines above to
+start them before, type <tt class="docutils literal"><span class="pre">fg</span></tt> and RETURN, and then CTRL-C to stop worker 2;
+and then do the same thing (<tt class="docutils literal"><span class="pre">fg</span></tt> and RETURN, and then CTRL-C) to stop worker
+1.</p>
+<pre>$ cd worker1
+$ MONITOR_PORT=9991 ../bin/python ../lib/python2.5/site-packages/pi.py &amp;
+$ cd ../worker2
+$ MONITOR_PORT=9992 ../bin/python ../lib/python2.5/site-packages/pi.py &amp;</pre>
+<p>Now you can open connections to these two ports, 9991 and 9992, and query the
+worker (using the <tt class="docutils literal"><span class="pre">async</span></tt> command, primarily) and the state of zc.async in
+the database itself (the <tt class="docutils literal"><span class="pre">asyncdb</span></tt> command).</p>
+<p>The easiest way to experiment with this is using telnet.  Try this.</p>
+<pre>$ telnet 127.0.0.1 9991</pre>
+<p>You should see something like this:</p>
+<pre>Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.</pre>
+<p>Now you can experiment with commands.  Try this (followed by a RETURN):</p>
+<div class="highlight"><pre><span class="n">help</span>
+</pre></div>
+<p>You should see something like this:</p>
+<pre>Supported commands:
+  async -- Monitor zc.async activity in this process.
+  asyncdb -- Monitor and introspect zc.async activity in the database.
+  help -- Get help about server commands
+  interactive -- Turn on monitor's interactive mode
+  quit -- Quit the monitor
+Connection closed by foreign host.
+$</pre>
+<p>Hm, it dumped us straight back to the shell!  <tt class="docutils literal"><span class="pre">zc.monitor</span></tt> behaves that way
+tp be friendly to automated monitoring processes using the port.  We can use
+the <tt class="docutils literal"><span class="pre">interactive</span></tt> command to make things a bit more pleasant for ourselves.</p>
+<p>Reuse the telnet command shown above, or maybe connect to 9992 (<tt class="docutils literal"><span class="pre">telnet</span>
+<span class="pre">127.0.0.1</span> <span class="pre">9992</span></tt>) to see that you can.  This time, type the <tt class="docutils literal"><span class="pre">interactive</span></tt>
+command first.  You should see this reply:</p>
+<pre>Interactive mode on.  Use "quit" To exit.</pre>
+<p>Now experiment!  Try some of these commands to see what you get.</p>
+<pre>help async
+
+async help
+
+async help status
+
+async status
+
+async help poll
+
+async poll
+
+async UUID
+
+async utcnow
+
+help asyncdb
+
+asyncdb help
+
+asyncdb help UUIDs
+
+asyncdb UUIDs
+
+asyncdb lastcompleted
+
+asyncdb count completed
+
+asyncdb count completed uuid:THIS</pre>
+<p>That last command shows how many jobs this worker has completed for as long as
+the database has records, which by default means between seven and eight days.
+The help for <tt class="docutils literal"><span class="pre">asyncdb</span> <span class="pre">count</span></tt>, which is available from <tt class="docutils literal"><span class="pre">asyncdb</span> <span class="pre">help</span> <span class="pre">count</span></tt>,
+is not short, but tells you the many options available.</p>
+<p>When you are done, use the <tt class="docutils literal"><span class="pre">quit</span></tt> command to exit the telnet connection to
+the monitor port.</p>
+<p>Another important tool is logging.  The <tt class="docutils literal"><span class="pre">zc.async.event</span></tt> logger gets
+important events&#8211;pay special attention to critical events!  <tt class="docutils literal"><span class="pre">zc.async.trace</span></tt>
+lets you follow along with every detail, if so desired.</p>
+<p>These monitoring and introspection tools, combined with logging, provide
+powerful tools to keep track of your zc.async work.</p>
+</div>
+<div class="section" id="other-configuration">
+<h2 id="other-configuration">Other Configuration<a class="headerlink" href="#other-configuration" title="Permalink to this headline">¶</a></h2>
+<p>We&#8217;re at the end of this quickstart.  To close, here&#8217;s a quick survey of some
+other configuration opportunities available that we haven&#8217;t seen here.</p>
+<ul class="simple">
+<li>Callbacks are a very important per-job configuration.  You can add them to
+a job, to be run unconditionally, conditionally if the result is an
+instance of a <tt class="docutils literal"><span class="pre">twisted.python.failure.Failure</span></tt>, or conditionally if
+the result is not a <tt class="docutils literal"><span class="pre">Failure</span></tt>.  See
+<tt class="xref docutils literal"><span class="pre">addCallback()</span></tt>,
+<tt class="xref docutils literal"><span class="pre">addCallbacks()</span></tt>, and
+<tt class="xref docutils literal"><span class="pre">callbacks</span></tt></li>
+</ul>
+<div class="admonition note">
+<p class="first admonition-title">Note</p>
+<p class="last">Unlike Twisted callbacks, all callbacks for the same job get the same
+result; if you would like to chain results, the callbacks themselves
+are Jobs, so attach a callback to your callback.</p>
+</div>
+<ul class="simple">
+<li>You can request that a job <tt class="xref docutils literal"><span class="pre">begin_after</span></tt>
+a given timezone-aware datetime.  If not given, this defaults to now, for the
+purposes of calculating the effective
+<tt class="xref docutils literal"><span class="pre">begin_by</span></tt> datetime, described below.</li>
+<li>You can specify that a job should <tt class="xref docutils literal"><span class="pre">begin_by</span></tt>
+a given duration (datetime.timedelta) <em>after</em> the jobs&#8217;s
+<tt class="xref docutils literal"><span class="pre">begin_after</span></tt> value.  When the queue
+:gets
+ready to offer the job for an agent to choose, if the effective
+<tt class="docutils literal"><span class="pre">begin_by</span></tt> value has passed, the queue will instead offer a call to
+the job&#8217;s
+<tt class="xref docutils literal"><span class="pre">fail()</span></tt> method.</li>
+</ul>
+<div class="admonition note">
+<p class="first admonition-title">Note</p>
+<p class="last">There is no built-in way to stop a running job, short of stopping the
+process.  This can be approximated by use in your job of
+<tt class="xref docutils literal"><span class="pre">getLiveAnnotation()</span></tt> to poll for stop requests; or the
+brave can write some C to use <a class="reference external" href="http://docs.python.org/api/threads.html">PyThreadState_SetAsyncExc</a>.</p>
+</div>
+<ul class="simple">
+<li>You can set up quotas for certain arbitrary quota names that you define.
+This is a limit: no more than the given quota can run at once, total,
+across all workers.  This can let you decrease the chance of conflict
+errors for long-running jobs that write to the same data structures.  See
+<tt class="xref docutils literal"><span class="pre">quotas</span></tt>,
+<tt class="xref docutils literal"><span class="pre">IQuotas</span></tt>, and
+<tt class="xref docutils literal"><span class="pre">quota_names</span></tt>.</li>
+<li>Retry policies determine how jobs should be retried if they raise an
+uncaught exception while running, if their commit raises an error, or if
+they are interrupted.  They can be configured per job, or as defaults for
+callback and non-callback jobs.  See
+<tt class="xref docutils literal"><span class="pre">IRetryPolicy</span></tt>,
+<tt class="xref docutils literal"><span class="pre">retry_policy_factory</span></tt>,
+<tt class="xref docutils literal"><span class="pre">getRetryPolicy()</span></tt>; the default retry policies
+<tt class="xref docutils literal"><span class="pre">RetryCommonFourTimes</span></tt>,
+<tt class="xref docutils literal"><span class="pre">RetryCommonForever</span></tt> and
+<tt class="xref docutils literal"><span class="pre">NeverRetry</span></tt>; and the <tt class="docutils literal"><span class="pre">retry_policy_factory</span></tt> argument
+to <tt class="xref docutils literal"><span class="pre">addCallback()</span></tt>,
+<tt class="xref docutils literal"><span class="pre">addCallbacks()</span></tt>, and
+<tt class="xref docutils literal"><span class="pre">put()</span></tt>.</li>
+<li>The <tt class="xref docutils literal"><span class="pre">failure_log_level</span></tt> determines at what
+level Failure results for a given job should be logged.  This is usually
+logging.ERROR, and logging.CRITICAL for callbacks.  This can be set directly
+on the job, or applied via the <tt class="docutils literal"><span class="pre">failure_log_level</span></tt> argument to
+<tt class="xref docutils literal"><span class="pre">addCallback()</span></tt>,
+<tt class="xref docutils literal"><span class="pre">addCallbacks()</span></tt>, and
+<tt class="xref docutils literal"><span class="pre">put()</span></tt>.</li>
+<li>Custom job subclasses can take advantage of
+<tt class="xref docutils literal"><span class="pre">setUp()</span></tt> and
+<tt class="xref docutils literal"><span class="pre">tearDown()</span></tt> hooks to set up and tear down
+state.  An example is the Zope 3-specific <tt class="xref docutils literal"><span class="pre">Job</span></tt>.</li>
+<li>A custom queue might support broadcasting jobs across dispatchers, or
+targeting specific dispatchers.  These features may be developed for <tt class="docutils literal"><span class="pre">zc.async</span></tt>
+itself at a later date.</li>
+</ul>
+<p>There are many other topics to discuss&#8211;testing, debugging, and Zope 3
+integration, for instance&#8211;but this is a quick start, so we&#8217;ll end here.</p>
+</div>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Table Of Contents</h3>
+            <ul>
+<li><a class="reference external" href="">Quickstart with <tt class="docutils literal"><span class="pre">virtualenv</span></tt></a><ul>
+<li><a class="reference external" href="#prerequisites">Prerequisites</a><ul>
+<li><a class="reference external" href="#installation">Installation</a></li>
+<li><a class="reference external" href="#dependencies">Dependencies</a></li>
+<li><a class="reference external" href="#zeo-server">ZEO Server</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#starting-async">Starting <tt class="docutils literal"><span class="pre">zc.async</span></tt></a><ul>
+<li><a class="reference external" href="#a-client">A Client</a></li>
+<li><a class="reference external" href="#database-connection">Database Connection</a></li>
+<li><a class="reference external" href="#start-async">Start <tt class="docutils literal"><span class="pre">zc.async</span></tt></a><ul>
+<li><a class="reference external" href="#basics">Basics</a></li>
+<li><a class="reference external" href="#policy">Policy</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a class="reference external" href="#using-async">Using <tt class="docutils literal"><span class="pre">zc.async</span></tt></a><ul>
+<li><a class="reference external" href="#the-queue">The Queue</a></li>
+<li><a class="reference external" href="#a-job">A Job</a></li>
+<li><a class="reference external" href="#a-transaction">A Transaction</a></li>
+<li><a class="reference external" href="#a-result">A Result</a></li>
+<li><a class="reference external" href="#another-job-and-closures">Another Job (And Closures)</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#running-your-own-code">Running Your Own Code</a><ul>
+<li><a class="reference external" href="#a-monte-carlo-simulation">A Monte Carlo Simulation</a></li>
+<li><a class="reference external" href="#picklable-callables-and-arguments">Picklable Callables and Arguments</a></li>
+<li><a class="reference external" href="#process-uuids">Process UUIDs</a></li>
+<li><a class="reference external" href="#make-a-file">Make a File</a></li>
+<li><a class="reference external" href="#our-first-monte-carlo-experiment">Our First Monte Carlo Experiment</a></li>
+<li><a class="reference external" href="#closures">Closures</a></li>
+<li><a class="reference external" href="#configuration">Configuration</a><ul>
+<li><a class="reference external" href="#agents">Agents</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#demonstration">Demonstration</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#monitoring">Monitoring</a></li>
+<li><a class="reference external" href="#other-configuration">Other Configuration</a></li>
+</ul>
+</li>
+</ul>
+
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="index.html" title="previous chapter"><tt class="docutils literal"><span class="pre">zc.async</span></tt></a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="QUICKSTART_2_GROK.html" title="next chapter">Quickstart with Grok</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/QUICKSTART_1_VIRTUALENV.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="QUICKSTART_2_GROK.html" title="Quickstart with Grok"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="index.html" title="zc.async"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/QUICKSTART_2_GROK.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/QUICKSTART_2_GROK.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/QUICKSTART_2_GROK.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,253 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Quickstart with Grok &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="next" title="Introduction" href="README.html" />
+    <link rel="prev" title="Quickstart with virtualenv" href="QUICKSTART_1_VIRTUALENV.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README.html" title="Introduction"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="QUICKSTART_1_VIRTUALENV.html" title="Quickstart with virtualenv"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="quickstart-with-grok">
+<h1 id="quickstart-with-grok">Quickstart with <a class="reference external" href="http://grok.zope.org/">Grok</a><a class="headerlink" href="#quickstart-with-grok" title="Permalink to this headline">¶</a></h1>
+<div class="section" id="goals">
+<h2 id="goals">Goals<a class="headerlink" href="#goals" title="Permalink to this headline">¶</a></h2>
+<p>In this quickstart, we will use zc.async to make a small web application that
+is a Python Package Index (PyPI, <a class="reference external" href="http://pypi.python.org/">http://pypi.python.org/</a>) helper portal.  We&#8217;ll
+call it &#8220;My PyPI,&#8221; to be cute.</p>
+<p><em>My PyPI</em> will let you subscribe to changes of specific packages, rather than
+the entire package index; and will let you associate external web pages with
+packages for you and others to see and search on.</p>
+<p>We&#8217;ll make a number of &#8220;toy app&#8221; decisions to keep the story quick, but it
+should be a good example for how to leverage zc.async.</p>
+<p>Also for simplicity, we&#8217;ll assume that we are making several instances on the
+same machine, such as you might do with a few processors at your disposal.  To
+get the true advantage of high availability in production, you&#8217;d want at least
+two boxes, with a deployment of a ZEO server (or equivalent, for RelStorage),
+some kind of redundancy for your database (ZRS, or slony for RelStorage plus
+PostgreSQL) and instructions for each box on how to connect to the ZEO primary.</p>
+<p>This quickstart is more complex than the <a class="reference external" href="QUICKSTART_1_VIRTUALENV.html#quickstart-with-virtualenv"><em>Quickstart with virtualenv</em></a>.  I
+suggest you read that through before this one.</p>
+<ul class="simple">
+<li>That previous quickstart introduces <tt class="docutils literal"><span class="pre">zc.async</span></tt> through the Python interpreter
+for a very casual and quick start.</li>
+<li>It also is more &#8220;pure-Python&#8221; with very little understanding needed of
+additional frameworks to follow and use the examples.</li>
+</ul>
+<p>This quickstart instead uses the following somewhat &#8220;heavier&#8221; technologies.</p>
+<ul>
+<li><p class="first"><a class="reference external" href="http://pypi.python.org/pypi/zc.buildout"><tt class="docutils literal"><span class="pre">zc.buildout</span></tt></a> is a way of producing repeatable software build-outs, for
+development and conceivably for deployment.  It is written in Python, but is
+not Python-specific, and it has found use as a <tt class="docutils literal"><span class="pre">make</span></tt> replacement for many
+projects.</p>
+</li>
+<li><p class="first"><a class="reference external" href="http://grok.zope.org/">Grok</a> is a web framework emerging from &#8220;Zope 3&#8221; technologies.  From their
+website:</p>
+<blockquote>
+<p>Grok is a web application framework for Python developers. It is aimed at
+both beginners and very experienced web developers. Grok has an emphasis on
+agile development. Grok is easy and powerful.</p>
+</blockquote>
+</li>
+</ul>
+<p>This guide, then, takes a somewhat slower definition of &#8220;quick&#8221; for its
+&#8220;quickstart&#8221;, in exchange for more guidance and care with a view towards
+production-readiness.</p>
+</div>
+<div class="section" id="prerequisites">
+<h2 id="prerequisites">Prerequisites<a class="headerlink" href="#prerequisites" title="Permalink to this headline">¶</a></h2>
+<div class="sidebar">
+<p class="first sidebar-title">Building Python 2.4.5 on OS X Leopard</p>
+<p>Unfortunately, building a clean, standalone workable Python 2.4.5 on OS X is
+not obvious.  This is what I recommend, if you are working on that platform.</p>
+<p>First you need macports.  Go to <a class="reference external" href="http://www.macports.org/">http://www.macports.org/</a> and download the
+newest version.  It doesn&#8217;t seem to set up the manual path correctly, so
+after the installation add this to your <tt class="docutils literal"><span class="pre">~/.profile</span></tt> (or in a similar
+place):</p>
+<pre>export MANPATH=/opt/local/man:$MANPATH</pre>
+<p>You&#8217;ll need a new terminal session (or other shell magic if you know it) for
+these changes to take effect.  The easiest thing to do is close the shell
+you are working in and open a new one.</p>
+<p>Download a source distribution of Python 2.4.5.  You may have your own
+approach as to where to put things, but I go with this pattern: <tt class="docutils literal"><span class="pre">~/src</span></tt>
+holds expanded source trees, <tt class="docutils literal"><span class="pre">~/opt</span></tt> holds our local Python, and I develop
+in <tt class="docutils literal"><span class="pre">~/dev</span></tt>.</p>
+<p>We will want readline and need zlib from macports.</p>
+<pre>$ sudo port -c install readline
+$ sudo port -c install zlib</pre>
+<p>Now we&#8217;ll do the usual dance, with a couple of ugly extra steps.</p>
+<p><strong>Note: replace ``/Users/gary/opt/py`` in the below with your own desired
+location!</strong></p>
+<pre>$ MACOSX_DEPLOYMENT_TARGET=10.5 ./configure \
+  --prefix=/Users/gary/opt/py \
+  LDFLAGS=-L/opt/local/lib \
+  OPT=-I/opt/local/include
+$ make
+$ make install</pre>
+<p class="last">Now, given my <tt class="docutils literal"><span class="pre">--prefix</span></tt>, I&#8217;ll find my python in
+<tt class="docutils literal"><span class="pre">/Users/gary/opt/py/bin/python</span></tt>.</p>
+</div>
+<p>As of this writing, Grok requires Python 2.4.  Moreover, for more repeatable
+installations, many developers strongly recommend using a &#8220;clean&#8221;, non-system
+Python, to reduce the probability of unnecessary or spurious problems (in your
+software <em>or</em> in your system!).  Therefore, consider building your own Python
+2.4 for your development.</p>
+<p>We&#8217;ll also expect that your Python has <a class="reference external" href="http://peak.telecommunity.com/DevCenter/EasyInstall"><tt class="docutils literal"><span class="pre">easy_install</span></tt></a>.  If it doesn&#8217;t, you can
+just download <a class="reference external" href="http://peak.telecommunity.com/dist/ez_setup.py">ez_setup.py</a> and then run it with your local, development
+Python (e.g., <tt class="docutils literal"><span class="pre">~/opt/py/bin/python</span> <span class="pre">ez_setup.py</span></tt>). This will install the
+easy_install command for your development Python in the same bin directory as
+your Python (e.g., <tt class="docutils literal"><span class="pre">~/opt/py/bin/easy_install</span></tt>).</p>
+</div>
+<div class="section" id="grokproject">
+<h2 id="grokproject"><a class="reference external" href="http://pypi.python.org/pypi/grokproject">grokproject</a><a class="headerlink" href="#grokproject" title="Permalink to this headline">¶</a></h2>
+<div class="sidebar">
+<p class="first sidebar-title"><tt class="docutils literal"><span class="pre">zc.buildout</span></tt> Conveniences</p>
+<p>You may want to consider the following conveniences if you are building many
+projects with <tt class="docutils literal"><span class="pre">zc.buildout</span></tt>.  They make zc.buildout keep two shared
+collections across all of your zc.buildout projects.  This can significantly
+speed up the time to buildout new applications.  One shared collection is a
+download cache of source distributions and eggs.  The other is an egg cache
+only, for both the downloaded eggs and the eggs generated on your machine.</p>
+<p>In your home directory, make a <tt class="docutils literal"><span class="pre">.buildout</span></tt> directory.  In that directory,
+make two sub-directories, <tt class="docutils literal"><span class="pre">eggs</span></tt> and <tt class="docutils literal"><span class="pre">download-cache</span></tt>.  Also in
+<tt class="docutils literal"><span class="pre">.buildout</span></tt>, create a file named <tt class="docutils literal"><span class="pre">default.cfg</span></tt> with the following
+content, where <tt class="docutils literal"><span class="pre">/Users/gary</span></tt> is replaced with the path to your home
+directory:</p>
+<pre>[buildout]
+eggs-directory=/Users/gary/.buildout/eggs
+download-cache=/Users/gary/.buildout/download-cache</pre>
+<p>There are many other possible settings to make here (for instance, we could
+specify the clean Python you built here), but these are all I
+currently bother with.</p>
+<p>It is also worth mentioning that, as of this writing, setuptools builds eggs
+in such a way as to confuse the Python debugger.  If you use the Python
+debugger and discover that you want to see the lines in an egg and can&#8217;t,
+the following line (or something like it) will help for non-zipped eggs:</p>
+<pre>find ~/.buildout/eggs-aside/ -name '*.pyc' -exec rm {} \;</pre>
+</div>
+<p>Grok has a pleasantly convenient way to start a project.  It is called
+<a class="reference external" href="http://pypi.python.org/pypi/grokproject">grokproject</a>.  Use your local Python&#8217;s <tt class="docutils literal"><span class="pre">easy_install</span></tt> to install it.  For
+instance, I might type <tt class="docutils literal"><span class="pre">~/opt/py/bin/easy_install</span> <span class="pre">grokproject</span></tt>.</p>
+<p>After it runs, it should have installed the <tt class="docutils literal"><span class="pre">grokproject</span></tt> command in the same
+bin directory as your local Python (e.g., <tt class="docutils literal"><span class="pre">~/opt/py/bin/grokproject</span></tt>).</p>
+</div>
+<div class="section" id="skeleton">
+<h2 id="skeleton">Skeleton<a class="headerlink" href="#skeleton" title="Permalink to this headline">¶</a></h2>
+<p>Now we will use <a class="reference external" href="http://pypi.python.org/pypi/grokproject">grokproject</a> to make a skeleton of our package.  Let&#8217;s
+call the project &#8220;mypypi&#8221;.  Go to a directory in which you want to develop
+our package.  Then use the newly installed <tt class="docutils literal"><span class="pre">grokproject</span></tt> command to create</p>
+<p>XXX</p>
+<ul class="simple">
+<li>include zc.async in setup.py; mention versions.cfg</li>
+<li>set up ZEO</li>
+<li>set up multiple instances</li>
+<li>zope.app.testing = 3.4.1 -&gt; 3.4.2</li>
+<li>set up interpreter</li>
+<li>set up z3monitor</li>
+<li>make separate debug instance</li>
+</ul>
+</div>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Table Of Contents</h3>
+            <ul>
+<li><a class="reference external" href="">Quickstart with Grok</a><ul>
+<li><a class="reference external" href="#goals">Goals</a></li>
+<li><a class="reference external" href="#prerequisites">Prerequisites</a></li>
+<li><a class="reference external" href="#grokproject">grokproject</a></li>
+<li><a class="reference external" href="#skeleton">Skeleton</a></li>
+</ul>
+</li>
+</ul>
+
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="QUICKSTART_1_VIRTUALENV.html" title="previous chapter">Quickstart with <tt class="docutils literal docutils literal"><span class="pre">virtualenv</span></tt></a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="README.html" title="next chapter">Introduction</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/QUICKSTART_2_GROK.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README.html" title="Introduction"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="QUICKSTART_1_VIRTUALENV.html" title="Quickstart with virtualenv"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/README.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/README.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/README.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,338 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Introduction &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="next" title="Usage" href="README_1.html" />
+    <link rel="prev" title="Quickstart with Grok" href="QUICKSTART_2_GROK.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README_1.html" title="Usage"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="QUICKSTART_2_GROK.html" title="Quickstart with Grok"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="introduction">
+<h1 id="introduction">Introduction<a class="headerlink" href="#introduction" title="Permalink to this headline">¶</a></h1>
+<div class="section" id="goals">
+<h2 id="goals">Goals<a class="headerlink" href="#goals" title="Permalink to this headline">¶</a></h2>
+<p>The zc.async package provides a way to schedule jobs to be performed
+out-of-band from your current thread.  The job might be done in another thread
+or another process, possibly on another machine.  Here are some example core
+use cases.</p>
+<ul class="simple">
+<li>You want to let users do something that requires a lot of system
+resources from your application, such as creating a large PDF.  Naively
+done, six or seven simultaneous PDF requests will consume your
+application thread pool and could make your application unresponsive to
+any other users.</li>
+<li>You want to let users spider a web site; communicate with a credit card
+company; query a large, slow LDAP database on another machine; or do
+some other action that generates network requests from the server.
+System resources might not be a problem, but, again, if something goes
+wrong, several requests could make your application unresponsive.</li>
+<li>Perhaps because of resource contention, you want to serialize work
+that can be done asynchronously, such as updating a single data structure
+like a catalog index.</li>
+<li>You want to decompose and parallelize a single job across many machines so
+it can be finished faster.</li>
+<li>You have an application job that you discover is taking longer than users can
+handle, even after you optimize it.  You want a quick fix to move the work
+out-of-band.</li>
+</ul>
+<p>Many of these core use cases involve end-users being able to start potentially
+expensive processes, on demand.  Basic scheduled tasks are also provided by this
+package, though recurrence must be something you arrange.</p>
+</div>
+<div class="section" id="history">
+<h2 id="history">History<a class="headerlink" href="#history" title="Permalink to this headline">¶</a></h2>
+<p>This is a second-generation design.  The first generation was <cite>zasync</cite>,
+a mission-critical and successful Zope 2 product in use for a number of
+high-volume Zope 2 installations.  <a class="footnote-reference" href="#async-history" id="id1">[1]</a> It&#8217;s worthwhile noting
+that zc.async has absolutely no backwards compatibility with zasync and
+zc.async does not require Zope (although it can be used in conjunction with
+it).</p>
+</div>
+<div class="section" id="design-overview">
+<h2 id="design-overview">Design Overview<a class="headerlink" href="#design-overview" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="overview-usage">
+<h3 id="overview-usage">Overview: Usage<a class="headerlink" href="#overview-usage" title="Permalink to this headline">¶</a></h3>
+<p>Looking at the design from the perspective of regular usage, your code obtains
+a <tt class="docutils literal"><span class="pre">queue</span></tt>, which is a place to register jobs to be performed asynchronously.</p>
+<p>Your application calls <tt class="docutils literal"><span class="pre">put</span></tt> on the queue to register a job.  The job must be
+a pickleable, callable object.  A global function, a callable persistent
+object, a method of a persistent object, or a special zc.async.job.Job object
+(discussed later) are all examples of suitable objects.  The job by default is
+registered to be performed as soon as possible, but can be registered to be
+called at a certain time.</p>
+<p>The <tt class="docutils literal"><span class="pre">put</span></tt> call will return a zc.async.job.Job object.  This object represents
+both the callable and its deferred result.  It has information about the job
+requested, the current state of the job, and the result of performing the job.</p>
+<p>An example spelling for registering a job might be <tt class="docutils literal"><span class="pre">self.pending_result</span> <span class="pre">=</span>
+<span class="pre">queue.put(self.performSpider)</span></tt>.  The returned object can be stored and polled
+to see when the job is complete; or the job can be configured to do additional
+work when it completes (such as storing the result in a data structure).</p>
+</div>
+<div class="section" id="overview-mechanism">
+<h3 id="overview-mechanism">Overview: Mechanism<a class="headerlink" href="#overview-mechanism" title="Permalink to this headline">¶</a></h3>
+<p>Multiple processes, typically spread across multiple machines, can
+connect to the queue and claim and perform work.  As with other
+collections of processes that share pickled objects, these processes
+generally should share the same software (though some variations on this
+constraint should be possible).</p>
+<p>A process that should claim and perform work, in addition to a database
+connection and the necessary software, needs a <tt class="docutils literal"><span class="pre">dispatcher</span></tt> with a
+<tt class="docutils literal"><span class="pre">reactor</span></tt> to provide a heartbeat.  The dispatcher will rely on one or more
+persistent <tt class="docutils literal"><span class="pre">agents</span></tt> in the queue (in the database) to determine which jobs
+it should perform.</p>
+<p>A <tt class="docutils literal"><span class="pre">dispatcher</span></tt> is in charge of dispatching queued work for a given
+process to worker threads.  It works with one or more queues and a
+single reactor.  It has a universally unique identifier (UUID), which is
+usually an identifier of the application instance in which it is
+running.  The dispatcher starts jobs in dedicated threads.</p>
+<p>A <tt class="docutils literal"><span class="pre">reactor</span></tt> is something that can provide an eternal loop, or heartbeat,
+to power the dispatcher.  It can be the main twisted reactor (in the
+main thread); another instance of a twisted reactor (in a child thread);
+or any object that implements a small subset of the twisted reactor
+interface (see discussion in dispatcher.txt, and example testing reactor in
+testing.py, used below).</p>
+<p>An <tt class="docutils literal"><span class="pre">agent</span></tt> is a persistent object in a queue that is associated with a
+dispatcher and is responsible for picking jobs and keeping track of
+them. Zero or more agents within a queue can be associated with a
+dispatcher.  Each agent for a given dispatcher in a given queue is
+identified uniquely with a name <a class="footnote-reference" href="#identifying-agent" id="id2">[2]</a>.</p>
+<p>Generally, these work together as follows.  The reactor calls the
+dispatcher. The dispatcher tries to find the mapping of queues in the
+database root under a key of <tt class="docutils literal"><span class="pre">zc.async</span></tt> (see constant
+zc.async.interfaces.KEY).  If it finds the mapping, it iterates
+over the queues (the mapping&#8217;s values) and asks each queue for the
+agents associated with the dispatcher&#8217;s UUID.  The dispatcher then is
+responsible for seeing what jobs its agents want to do from the queue,
+and providing threads and connections for the work to be done.  The
+dispatcher then asks the reactor to call itself again in a few seconds.</p>
+<p class="rubric">Footnotes</p>
+<table class="docutils footnote" frame="void" id="async-history" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id1">[1]</a></td><td><p class="first">The first generation, <tt class="docutils literal"><span class="pre">zasync</span></tt>, had the following goals:</p>
+<ul class="simple">
+<li>be scalable, so that another process or machine could do the asynchronous
+work;</li>
+<li>support lengthy jobs outside of the ZODB;</li>
+<li>support lengthy jobs inside the ZODB;</li>
+<li>be recoverable, so that crashes would not lose work;</li>
+<li>be discoverable, so that logs and web interfaces give a view into the
+work being done asynchronously;</li>
+<li>be easily extendible, to do new jobs; and</li>
+<li>support graceful job expiration and cancellation.</li>
+</ul>
+<p>It met its goals well in some areas and adequately in others.</p>
+<p>Based on experience with the first generation, this second generation
+identifies several areas of improvement from the first design, and adds
+several goals.</p>
+<ul class="last">
+<li><p class="first">Improvements</p>
+<ul>
+<li><p class="first">More carefully delineate the roles of the comprising components.</p>
+<p>The zc.async design has three main components, as divided by their
+roles: persistent deferreds, now called jobs; job queues (the original
+zasync&#8217;s &#8220;asynchronous call manager&#8221;); and dispatchers (the original
+zasync ZEO client). The zasync 1.x design blurred the lines between the
+three components such that the component parts could only be replaced
+with difficulty, if at all. A goal for the 2.x design is to clearly
+define the role for each of three components such that, for instance, a
+user of a queue does not need to know about the dispatcher or the
+agents.</p>
+</li>
+<li><p class="first">Improve scalability of asynchronous workers.</p>
+<p>The 1.x line was initially designed for a single asynchronous worker,
+which could be put on another machine thanks to ZEO. Tarek Ziade of
+Nuxeo wrote zasyncdispatcher, which allowed multiple asynchronous
+workers to accept work, allowing multiple processes and multiple
+machines to divide and conquer. It worked around the limitations of the
+original zasync design to provide even more scalability. However, it
+was forced to divide up work well before a given worker looks at the
+queue.</p>
+<p>While dividing work earlier allows guesses and heuristics a chance to
+predict what worker might be more free in the future, a more reliable
+approach is to let the worker gauge whether it should take a job at the
+time the job is taken. Perhaps the worker will choose based on the
+worker&#8217;s load, or other concurrent jobs in the process, or other
+details. A goal for the 2.x line is to more directly support this type
+of scalability.</p>
+</li>
+<li><p class="first">Improve scalability of registering jobs.</p>
+<p>The 1.x line initially wasn&#8217;t concerned about very many concurrent
+asynchronous requests. When this situation was encountered, it caused
+ConflictErrors between the worker process reading the deferred queue
+and the code that was adding the deferreds. Thanks to Nuxeo, this
+problem was addressed in the 1.x line. A goal for the new version is to
+include and improve upon the 1.x solution.</p>
+</li>
+<li><p class="first">Make it even simpler to provide new jobs.</p>
+<p>In the first version, <cite>plugins</cite> performed jobs. They had a specific API
+and they had to be configured. A goal for the new version is to require
+no specific API for jobs, and to not require any configuration.</p>
+</li>
+<li><p class="first">Improve report information, especially through the web.</p>
+<p>The component that the first version of zasync provided to do the
+asynchronous work, the zasync client, provided very verbose logs of the
+jobs done, but they were hard to read and also did not have a through-
+the-web parallel. Two goals for the new version are to improve the
+usefulness of the filesystem logs and to include more complete
+visibility of the status of the provided asynchronous clients.</p>
+</li>
+<li><p class="first">Make it easier to configure and start, especially for small
+deployments.</p>
+<p>A significant barrier to experimentation and deployment of the 1.x line
+was the difficulty in configuration. The 1.x line relied on ZConfig for
+zasync client configuration, demanding non-extensible
+similar-yet-subtly-different .conf files like the Zope conf files. The
+2.x line provides code that Zope 3 can configure to run in the same
+process as a standard Zope 3 application. This means that development
+instances can start a zasync quickly and easily. It also means that
+processes can be reallocated on the fly during production use, so that
+a machine being used as a zasync process can quickly be converted to a
+web server, if needed, and vice versa.</p>
+</li>
+</ul>
+</li>
+<li><p class="first">New goals</p>
+<ul>
+<li><p class="first">Support intermediate return calls so that jobs can report back how they
+are doing.</p>
+<p>A frequent request from users of zasync 1.x was the ability for a long-
+running asynchronous process to report back progress to the original
+requester. The 2.x line addresses this with three changes:</p>
+<ul class="simple">
+<li>jobs are annotatable;</li>
+<li>jobs should not be modified in an asynchronous worker that does work
+(though they may be read);</li>
+<li>jobs can request another job in a synchronous process that annotates
+the job with progress status or other information.</li>
+</ul>
+<p>Because of relatively recent changes in ZODB&#8211;multi version concurrency
+control&#8211;this simple pattern should not generate conflict errors.</p>
+</li>
+<li><p class="first">Support time-delayed calls.</p>
+<p>Retries and other use cases make time-delayed deferred calls desirable.
+The new design supports these sort of calls.</p>
+</li>
+</ul>
+</li>
+</ul>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="identifying-agent" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id2">[2]</a></td><td>The combination of a queue name plus a
+dispatcher UUID plus an agent name uniquely identifies an agent.</td></tr>
+</tbody>
+</table>
+</div>
+</div>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Table Of Contents</h3>
+            <ul>
+<li><a class="reference external" href="">Introduction</a><ul>
+<li><a class="reference external" href="#goals">Goals</a></li>
+<li><a class="reference external" href="#history">History</a></li>
+<li><a class="reference external" href="#design-overview">Design Overview</a><ul>
+<li><a class="reference external" href="#overview-usage">Overview: Usage</a></li>
+<li><a class="reference external" href="#overview-mechanism">Overview: Mechanism</a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="QUICKSTART_2_GROK.html" title="previous chapter">Quickstart with Grok</a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="README_1.html" title="next chapter">Usage</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/README.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README_1.html" title="Usage"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="QUICKSTART_2_GROK.html" title="Quickstart with Grok"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/README_1.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/README_1.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/README_1.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,1281 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Usage &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="next" title="Configuration (without Zope 3)" href="README_2.html" />
+    <link rel="prev" title="Introduction" href="README.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README_2.html" title="Configuration (without Zope 3)"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README.html" title="Introduction"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="id1">
+<span id="usage"></span><h1 id="id1"><span id="usage"></span>Usage<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h1>
+<div class="section" id="overview-and-basics">
+<h2 id="overview-and-basics">Overview and Basics<a class="headerlink" href="#overview-and-basics" title="Permalink to this headline">¶</a></h2>
+<p>The basic usage of zc.async does not depend on a particular configuration
+of the back-end mechanism for getting the jobs done.  Moreover, on some
+teams, it will be the responsibility of one person or group to configure
+zc.async, but a service available to the code of all team members.  Therefore,
+we begin our detailed discussion with regular usage, assuming configuration
+has already happened.  Subsequent sections discuss configuring zc.async
+with and without Zope 3.</p>
+<p>So, let&#8217;s assume we have a queue with dispatchers, reactors and agents all
+waiting to fulfill jobs placed into the queue.  We start with a connection
+object, <tt class="docutils literal"><span class="pre">conn</span></tt>, and some convenience functions introduced along the way that
+help us simulate time passing and work being done <a class="footnote-reference" href="#usagesetup" id="id2">[1]</a>.</p>
+<div class="section" id="obtaining-the-queue">
+<h3 id="obtaining-the-queue">Obtaining the queue<a class="headerlink" href="#obtaining-the-queue" title="Permalink to this headline">¶</a></h3>
+<p>First, how do we get the queue?  Your installation may have some
+conveniences.  For instance, the Zope 3 configuration described below
+makes it possible to get the primary queue with an adaptation call like
+<tt class="docutils literal"><span class="pre">zc.async.interfaces.IQueue(a_persistent_object_with_db_connection)</span></tt>.</p>
+<p>But failing that, queues are always expected to be in a zc.async.queue.Queues
+mapping found off the ZODB root in a key defined by the constant
+zc.async.interfaces.KEY.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">KEY</span>
+<span class="go">&#39;zc.async&#39;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">root</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queues</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">KEY</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.queue</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">isinstance</span><span class="p">(</span><span class="n">queues</span><span class="p">,</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">Queues</span><span class="p">)</span>
+<span class="go">True</span>
+</pre></div>
+<p>As the name implies, <tt class="docutils literal"><span class="pre">queues</span></tt> is a collection of queues. As discussed later,
+it&#8217;s possible to have multiple queues, as a tool to distribute and control
+work. We will assume a convention of a queue being available in the &#8216;&#8217; (empty
+string).</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">queues</span><span class="o">.</span><span class="n">keys</span><span class="p">()</span>
+<span class="go">[&#39;&#39;]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span> <span class="o">=</span> <span class="n">queues</span><span class="p">[</span><span class="s">&#39;&#39;</span><span class="p">]</span>
+</pre></div>
+</div>
+<div class="section" id="queue-put">
+<h3 id="queue-put"><tt class="docutils literal"><span class="pre">queue.put</span></tt><a class="headerlink" href="#queue-put" title="Permalink to this headline">¶</a></h3>
+<p>Now we want to actually get some work done.  The simplest case is simple
+to perform: pass a persistable callable to the queue&#8217;s <tt class="docutils literal"><span class="pre">put</span></tt> method and
+commit the transaction.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">send_message</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">print</span> <span class="s">&quot;imagine this sent a message to another machine&quot;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">send_message</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<p>Note that this won&#8217;t really work in an interactive session: the callable needs
+to be picklable, as discussed above, so <tt class="docutils literal"><span class="pre">send_message</span></tt> would need to be
+a module global, for instance.</p>
+<p>The <tt class="docutils literal"><span class="pre">put</span></tt> returned a job.  Now we need to wait for the job to be
+performed.  We would normally do this by really waiting.  For our
+examples, we will use a helper method on the testing reactor to <tt class="docutils literal"><span class="pre">wait_for</span></tt>
+the job to be completed.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">imagine this sent a message to another machine</span>
+</pre></div>
+<p>We also could have used the method of a persistent object.  Here&#8217;s another
+quick example.</p>
+<p>First we define a simple persistent.Persistent subclass and put an instance of
+it in the database <a class="footnote-reference" href="#commit-for-multidatabase" id="id3">[2]</a>.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">persistent</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">class</span> <span class="nc">Demo</span><span class="p">(</span><span class="n">persistent</span><span class="o">.</span><span class="n">Persistent</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">counter</span> <span class="o">=</span> <span class="mf">0</span>
+<span class="gp">... </span>    <span class="k">def</span> <span class="nf">increase</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mf">1</span><span class="p">):</span>
+<span class="gp">... </span>        <span class="bp">self</span><span class="o">.</span><span class="n">counter</span> <span class="o">+=</span> <span class="n">value</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span><span class="p">[</span><span class="s">&#39;demo&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">Demo</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<p>Now we can put the <tt class="docutils literal"><span class="pre">demo.increase</span></tt> method in the queue.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">root</span><span class="p">[</span><span class="s">&#39;demo&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">counter</span>
+<span class="go">0</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">root</span><span class="p">[</span><span class="s">&#39;demo&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">increase</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span><span class="p">[</span><span class="s">&#39;demo&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">counter</span>
+<span class="go">1</span>
+</pre></div>
+</blockquote>
+<p>The method was called, and the persistent object modified!</p>
+<p>To reiterate, only pickleable callables such as global functions and the
+methods of persistent objects can be used. This rules out, for instance,
+lambdas and other functions created dynamically. As we&#8217;ll see below, the job
+instance can help us out there somewhat by offering closure-like features.</p>
+</div>
+<div class="section" id="queue-pull-and-queue-remove">
+<h3 id="queue-pull-and-queue-remove"><tt class="docutils literal"><span class="pre">queue.pull</span></tt> and <tt class="docutils literal"><span class="pre">queue.remove</span></tt><a class="headerlink" href="#queue-pull-and-queue-remove" title="Permalink to this headline">¶</a></h3>
+<p>If you put a job into a queue and it hasn&#8217;t been claimed yet and you want to
+cancel the job, <tt class="docutils literal"><span class="pre">pull</span></tt> or <tt class="docutils literal"><span class="pre">remove</span></tt> it from the queue.</p>
+<p>The <tt class="docutils literal"><span class="pre">pull</span></tt> method removes the first job, or takes an integer index.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
+<span class="go">0</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">send_message</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">send_message</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
+<span class="go">2</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">pull</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span> <span class="o">==</span> <span class="p">[</span><span class="n">job2</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">job1</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span> <span class="o">==</span> <span class="p">[</span><span class="n">job2</span><span class="p">,</span> <span class="n">job1</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">pull</span><span class="p">(</span><span class="o">-</span><span class="mf">1</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">pull</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
+<span class="go">0</span>
+</pre></div>
+<p>The <tt class="docutils literal"><span class="pre">remove</span></tt> method removes the specific given job.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">send_message</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">send_message</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
+<span class="go">2</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">job1</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span> <span class="o">==</span> <span class="p">[</span><span class="n">job2</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">job1</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span> <span class="o">==</span> <span class="p">[</span><span class="n">job2</span><span class="p">,</span> <span class="n">job1</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">job1</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span> <span class="o">==</span> <span class="p">[</span><span class="n">job2</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">job2</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
+<span class="go">0</span>
+</pre></div>
+</div>
+<div class="section" id="scheduled-calls">
+<h3 id="scheduled-calls">Scheduled Calls<a class="headerlink" href="#scheduled-calls" title="Permalink to this headline">¶</a></h3>
+<p>When using <tt class="docutils literal"><span class="pre">put</span></tt>, you can also pass a datetime.datetime to schedule a call. A
+datetime without a timezone is considered to be in the UTC timezone.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">datetime</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">pytz</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">pytz</span><span class="o">.</span><span class="n">UTC</span><span class="p">)</span>
+<span class="go">datetime.datetime(2006, 8, 10, 15, 44, 33, 211, tzinfo=&lt;UTC&gt;)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">send_message</span><span class="p">,</span> <span class="n">begin_after</span><span class="o">=</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="mf">2006</span><span class="p">,</span> <span class="mf">8</span><span class="p">,</span> <span class="mf">10</span><span class="p">,</span> <span class="mf">15</span><span class="p">,</span> <span class="mf">56</span><span class="p">,</span> <span class="n">tzinfo</span><span class="o">=</span><span class="n">pytz</span><span class="o">.</span><span class="n">UTC</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">begin_after</span>
+<span class="go">datetime.datetime(2006, 8, 10, 15, 56, tzinfo=&lt;UTC&gt;)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">attempts</span><span class="o">=</span><span class="mf">2</span><span class="p">)</span> <span class="c"># +5 virtual seconds</span>
+<span class="go">TIME OUT</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">attempts</span><span class="o">=</span><span class="mf">2</span><span class="p">)</span> <span class="c"># +5 virtual seconds</span>
+<span class="go">TIME OUT</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">pytz</span><span class="o">.</span><span class="n">UTC</span><span class="p">)</span>
+<span class="go">datetime.datetime(2006, 8, 10, 15, 44, 43, 211, tzinfo=&lt;UTC&gt;)</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">set_now</span><span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="mf">2006</span><span class="p">,</span> <span class="mf">8</span><span class="p">,</span> <span class="mf">10</span><span class="p">,</span> <span class="mf">15</span><span class="p">,</span> <span class="mf">56</span><span class="p">,</span> <span class="n">tzinfo</span><span class="o">=</span><span class="n">pytz</span><span class="o">.</span><span class="n">UTC</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">imagine this sent a message to another machine</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">(</span><span class="n">pytz</span><span class="o">.</span><span class="n">UTC</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">job</span><span class="o">.</span><span class="n">begin_after</span>
+<span class="go">True</span>
+</pre></div>
+</blockquote>
+<p>If you set a time that has already passed, it will be run as if it had
+been set to run as soon as possible <a class="footnote-reference" href="#already-passed" id="id4">[3]</a>...unless the job
+has already timed out, in which case the job fails with an
+abort <a class="footnote-reference" href="#already-passed-timed-out" id="id5">[4]</a>.</p>
+<p>The queue&#8217;s <tt class="docutils literal"><span class="pre">put</span></tt> method is the essential API. <tt class="docutils literal"><span class="pre">pull</span></tt> is used rarely. Other
+methods are used to introspect, but are not needed for basic usage.</p>
+<p>But what is that result of the <tt class="docutils literal"><span class="pre">put</span></tt> call in the examples above?  A
+job?  What do you do with that?</p>
+</div>
+</div>
+<div class="section" id="jobs">
+<h2 id="jobs">Jobs<a class="headerlink" href="#jobs" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="overview">
+<h3 id="overview">Overview<a class="headerlink" href="#overview" title="Permalink to this headline">¶</a></h3>
+<p>The result of a call to <tt class="docutils literal"><span class="pre">put</span></tt> returns an <tt class="docutils literal"><span class="pre">IJob</span></tt>. The job represents the
+pending result. This object has a lot of functionality that&#8217;s explored in other
+documents in this package, and demonstrated a bit below, but here&#8217;s a summary.</p>
+<ul class="simple">
+<li>You can introspect, and even modify, the call and its arguments.</li>
+<li>You can specify that the job should be run serially with others of a given
+identifier.</li>
+<li>You can specify other calls that should be made on the basis of the result of
+this call.</li>
+<li>You can persist a reference to it, and periodically (after syncing your
+connection with the database, which happens whenever you begin or commit a
+transaction) check its <tt class="docutils literal"><span class="pre">status</span></tt> to see if it is equal to
+<tt class="docutils literal"><span class="pre">zc.async.interfaces.COMPLETED</span></tt>. When it is, the call has run to completion,
+either to success or an exception.</li>
+<li>You can look at the result of the call (once <tt class="docutils literal"><span class="pre">COMPLETED</span></tt>). It might be the
+result you expect, or a <tt class="docutils literal"><span class="pre">zc.twist.Failure</span></tt>, a subclass of
+<tt class="docutils literal"><span class="pre">twisted.python.failure.Failure</span></tt>, which is a way to safely communicate
+exceptions across connections and machines and processes.</li>
+</ul>
+</div>
+<div class="section" id="results">
+<h3 id="results">Results<a class="headerlink" href="#results" title="Permalink to this headline">¶</a></h3>
+<p>So here&#8217;s a simple story.  What if you want to get a result back from a
+call?  Look at the job.result after the call is <tt class="docutils literal"><span class="pre">COMPLETED</span></tt>.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">imaginaryNetworkCall</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="c"># let&#39;s imagine this makes a network call...</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="s">&quot;200 OK&quot;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">imaginaryNetworkCall</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">None</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">PENDING</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&#39;200 OK&#39;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">COMPLETED</span>
+<span class="go">True</span>
+</pre></div>
+</div>
+<div class="section" id="closures">
+<h3 id="closures">Closures<a class="headerlink" href="#closures" title="Permalink to this headline">¶</a></h3>
+<p>What&#8217;s more, you can pass a Job to the <tt class="docutils literal"><span class="pre">put</span></tt> call.  This means that you
+aren&#8217;t constrained to simply having simple non-argument calls performed
+asynchronously, but you can pass a job with a call, arguments, and
+keyword arguments&#8211;effectively, a kind of closure.  Here&#8217;s a quick example.
+We&#8217;ll use the demo object, and its increase method, that we introduced
+above, but this time we&#8217;ll include some arguments <a class="footnote-reference" href="#job" id="id6">[5]</a>.</p>
+<p>With positional arguments:</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">root</span><span class="p">[</span><span class="s">&#39;demo&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">increase</span><span class="p">,</span> <span class="mf">5</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span><span class="p">[</span><span class="s">&#39;demo&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">counter</span>
+<span class="go">6</span>
+</pre></div>
+<p>With keyword arguments (<tt class="docutils literal"><span class="pre">value</span></tt>):</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">root</span><span class="p">[</span><span class="s">&#39;demo&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">increase</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="mf">10</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span><span class="p">[</span><span class="s">&#39;demo&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">counter</span>
+<span class="go">16</span>
+</pre></div>
+<p>Note that arguments to these jobs can be any persistable object.</p>
+</div>
+<div class="section" id="failures">
+<h3 id="failures">Failures<a class="headerlink" href="#failures" title="Permalink to this headline">¶</a></h3>
+<p>What happens if a call raises an exception?  The return value is a Failure.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">I_am_a_bad_bad_function</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">foo</span> <span class="o">+</span> <span class="n">bar</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">I_am_a_bad_bad_function</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&lt;zc.twist.Failure exceptions.NameError&gt;</span>
+</pre></div>
+<p>Failures can provide useful information such as tracebacks.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">job</span><span class="o">.</span><span class="n">result</span><span class="o">.</span><span class="n">getTraceback</span><span class="p">()</span>
+<span class="gp">... </span><span class="c"># doctest: +ELLIPSIS +NORMALIZE_WHITESPACE</span>
+<span class="gp">...</span>
+<span class="go">exceptions.NameError: global name &#39;foo&#39; is not defined</span>
+<span class="go">&lt;BLANKLINE&gt;</span>
+</pre></div>
+</div>
+<div class="section" id="callbacks">
+<h3 id="callbacks">Callbacks<a class="headerlink" href="#callbacks" title="Permalink to this headline">¶</a></h3>
+<p>You can register callbacks to handle the result of a job, whether a
+Failure or another result.</p>
+<p>Note that, unlike callbacks on a Twisted deferred, these callbacks do not
+change the result of the original job. Since callbacks are jobs, you can chain
+results, but generally callbacks for the same job all get the same result as
+input.</p>
+<p>Also note that, during execution of a callback, there is no guarantee that
+the callback will be processed on the same machine as the main call.  Also,
+some of the <tt class="docutils literal"><span class="pre">local</span></tt> functions, discussed below, will not work as desired.</p>
+<p>Here&#8217;s a simple example of reacting to a success.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">I_scribble_on_strings</span><span class="p">(</span><span class="n">string</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">string</span> <span class="o">+</span> <span class="s">&quot;: SCRIBBLED&quot;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">imaginaryNetworkCall</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback</span> <span class="o">=</span> <span class="n">job</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">I_scribble_on_strings</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&#39;200 OK&#39;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&#39;200 OK: SCRIBBLED&#39;</span>
+</pre></div>
+<p>Here&#8217;s a more complex example of handling a Failure, and then chaining
+a subsequent callback.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">I_handle_NameErrors</span><span class="p">(</span><span class="n">failure</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">failure</span><span class="o">.</span><span class="n">trap</span><span class="p">(</span><span class="ne">NameError</span><span class="p">)</span> <span class="c"># see twisted.python.failure.Failure docs</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="s">&#39;I handled a name error&#39;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">I_am_a_bad_bad_function</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback1</span> <span class="o">=</span> <span class="n">job</span><span class="o">.</span><span class="n">addCallbacks</span><span class="p">(</span><span class="n">failure</span><span class="o">=</span><span class="n">I_handle_NameErrors</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback2</span> <span class="o">=</span> <span class="n">callback1</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">I_scribble_on_strings</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&lt;zc.twist.Failure exceptions.NameError&gt;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback1</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&#39;I handled a name error&#39;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback2</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&#39;I handled a name error: SCRIBBLED&#39;</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="advanced-techniques-and-tools">
+<h2 id="advanced-techniques-and-tools">Advanced Techniques and Tools<a class="headerlink" href="#advanced-techniques-and-tools" title="Permalink to this headline">¶</a></h2>
+<p><strong>Important</strong></p>
+<p>The job and its functionality described above are the core zc.async tools.</p>
+<p>The following are advanced techniques and tools of various complexities. You
+can use zc.async very productively without ever understanding or using them. If
+the following do not make sense to you now, please just move on for now.</p>
+<div class="section" id="zc-async-local">
+<h3 id="zc-async-local">zc.async.local<a class="headerlink" href="#zc-async-local" title="Permalink to this headline">¶</a></h3>
+<p>Jobs always run their callables in a thread, within the context of a
+connection to the ZODB. The callables have access to five special
+thread-local functions if they need them for special uses.  These are
+available off of zc.async.local.</p>
+<dl class="docutils">
+<dt><tt class="docutils literal"><span class="pre">zc.async.local.getJob()</span></tt></dt>
+<dd>The <tt class="docutils literal"><span class="pre">getJob</span></tt> function can be used to examine the job, to get
+a connection off of <tt class="docutils literal"><span class="pre">_p_jar</span></tt>, to get the queue into which the job
+was put, or other uses.</dd>
+<dt><tt class="docutils literal"><span class="pre">zc.async.local.getQueue()</span></tt></dt>
+<dd>The <tt class="docutils literal"><span class="pre">getQueue</span></tt> function can be used to examine the queue, to put another
+task into the queue, or other uses. It is sugar for
+<tt class="docutils literal"><span class="pre">zc.async.local.getJob().queue</span></tt>.</dd>
+<dt><tt class="docutils literal"><span class="pre">zc.async.local.setLiveAnnotation(name,</span> <span class="pre">value,</span> <span class="pre">job=None)</span></tt></dt>
+<dd><p class="first">The <tt class="docutils literal"><span class="pre">setLiveAnnotation</span></tt> tells the agent to set an annotation on a job,
+by default the current job, <em>in another connection</em>.  This makes it
+possible to send messages about progress or for coordination while in the
+middle of other work.</p>
+<p class="last">As a simple rule, only send immutable objects like strings or
+numbers as values <a class="footnote-reference" href="#setliveannotation" id="id7">[6]</a>.</p>
+</dd>
+<dt><tt class="docutils literal"><span class="pre">zc.async.local.getLiveAnnotation(name,</span> <span class="pre">default=None,</span> <span class="pre">timeout=0,</span> <span class="pre">poll=1,</span> <span class="pre">job=None)</span></tt></dt>
+<dd><p class="first">The <tt class="docutils literal"><span class="pre">getLiveAnnotation</span></tt> tells the agent to get an annotation for a job,
+by default the current job, <em>from another connection</em>.  This makes it
+possible to send messages about progress or for coordination while in the
+middle of other work.</p>
+<p>As a simple rule, only ask for annotation values that will be
+immutable objects like strings or numbers <a class="footnote-reference" href="#getliveannotation" id="id8">[7]</a>.</p>
+<p class="last">If the <tt class="docutils literal"><span class="pre">timeout</span></tt> argument is set to a positive float or int, the function
+will wait at least that number of seconds until an annotation of the
+given name is available. Otherwise, it will return the <tt class="docutils literal"><span class="pre">default</span></tt> if the
+name is not present in the annotations. The <tt class="docutils literal"><span class="pre">poll</span></tt> argument specifies
+approximately how often to poll for the annotation, in seconds (to be more
+precise, a subsequent poll will be min(poll, remaining seconds until
+timeout) seconds away).</p>
+</dd>
+<dt><tt class="docutils literal"><span class="pre">zc.async.local.getReactor()</span></tt></dt>
+<dd>The <tt class="docutils literal"><span class="pre">getReactor</span></tt> function returns the job&#8217;s dispatcher&#8217;s reactor.  The
+<tt class="docutils literal"><span class="pre">getLiveAnnotation</span></tt> and <tt class="docutils literal"><span class="pre">setLiveAnnotation</span></tt> functions use this,
+along with the zc.twist package, to work their magic; if you are feeling
+adventurous, you can do the same.</dd>
+<dt><tt class="docutils literal"><span class="pre">zc.async.local.getDispatcher()</span></tt></dt>
+<dd>The <tt class="docutils literal"><span class="pre">getDispatcher</span></tt> function returns the job&#8217;s dispatcher.  This might
+be used to analyze its non-persistent poll data structure, for instance
+(described later in configuration discussions).</dd>
+</dl>
+<p>Let&#8217;s give three of those a whirl. We will write a function that examines the
+job&#8217;s state while it is being called, and sets the state in an annotation, then
+waits for our flag to finish.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">annotateStatus</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">setLiveAnnotation</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="s">&#39;zc.async.test.status&#39;</span><span class="p">,</span>
+<span class="gp">... </span>        <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">getJob</span><span class="p">()</span><span class="o">.</span><span class="n">status</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">getLiveAnnotation</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="s">&#39;zc.async.test.flag&#39;</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">5</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="mf">42</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">annotateStatus</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">time</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">wait_for_annotation</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">reactor</span><span class="o">.</span><span class="n">time_flies</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">poll_interval</span><span class="p">)</span> <span class="c"># starts thread</span>
+<span class="gp">... </span>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">10</span><span class="p">):</span>
+<span class="gp">... </span>        <span class="k">while</span> <span class="n">reactor</span><span class="o">.</span><span class="n">time_passes</span><span class="p">():</span>
+<span class="gp">... </span>            <span class="k">pass</span>
+<span class="gp">... </span>        <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">... </span>        <span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">job</span><span class="o">.</span><span class="n">annotations</span><span class="p">:</span>
+<span class="gp">... </span>            <span class="k">break</span>
+<span class="gp">... </span>        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="k">print</span> <span class="s">&#39;Timed out&#39;</span> <span class="o">+</span> <span class="nb">repr</span><span class="p">(</span><span class="nb">dict</span><span class="p">(</span><span class="n">job</span><span class="o">.</span><span class="n">annotations</span><span class="p">))</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">wait_for_annotation</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="s">&#39;zc.async.test.status&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">annotations</span><span class="p">[</span><span class="s">&#39;zc.async.test.status&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="p">(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">ACTIVE</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">ACTIVE</span>
+<span class="go">True</span>
+</pre></div>
+<p><a class="footnote-reference" href="#stats-1" id="id9">[8]</a></p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">annotations</span><span class="p">[</span><span class="s">&#39;zc.async.test.flag&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">42</span>
+</pre></div>
+<p><a class="footnote-reference" href="#stats-2" id="id10">[9]</a> <tt class="docutils literal"><span class="pre">getReactor</span></tt> and <tt class="docutils literal"><span class="pre">getDispatcher</span></tt> are for advanced use
+cases and are not explored further here.</p>
+</div>
+<div class="section" id="job-quotas">
+<h3 id="job-quotas">Job Quotas<a class="headerlink" href="#job-quotas" title="Permalink to this headline">¶</a></h3>
+<p>One class of asynchronous jobs are ideally serialized.  For instance,
+you may want to reduce or eliminate the chance of conflict errors when
+updating a text index.  One way to do this kind of serialization is to
+use the <tt class="docutils literal"><span class="pre">quota_names</span></tt> attribute of the job.</p>
+<p>For example, let&#8217;s first show two non-serialized jobs running at the
+same time, and then two serialized jobs created at the same time.
+The first part of the example does not use queue_names, to show a contrast.</p>
+<p>For our parallel jobs, we&#8217;ll do something that would create a deadlock
+if they were serial.  Notice that we are mutating the job arguments after
+creation to accomplish this, which is supported.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">waitForParallel</span><span class="p">(</span><span class="n">other</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">setLiveAnnotation</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="s">&#39;zc.async.test.flag&#39;</span><span class="p">,</span> <span class="bp">True</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">getLiveAnnotation</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="s">&#39;zc.async.test.flag&#39;</span><span class="p">,</span> <span class="n">job</span><span class="o">=</span><span class="n">other</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">0.4</span><span class="p">,</span> <span class="n">poll</span><span class="o">=</span><span class="mf">0</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">waitForParallel</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">waitForParallel</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">job2</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">job1</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job1</span><span class="p">,</span> <span class="n">job2</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">COMPLETED</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">COMPLETED</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span><span class="o">.</span><span class="n">result</span> <span class="ow">is</span> <span class="n">job2</span><span class="o">.</span><span class="n">result</span> <span class="ow">is</span> <span class="bp">None</span>
+<span class="go">True</span>
+</pre></div>
+<p>On the other hand, for our serial jobs, we&#8217;ll do something that would fail
+if it were parallel.  We&#8217;ll rely on <tt class="docutils literal"><span class="pre">quota_names</span></tt>.</p>
+<p>Quotas verge on configuration, which is not what this section is about,
+because they must be configured on the queue.  However, they also affect
+usage, so we show them here.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">pause</span><span class="p">(</span><span class="n">other</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">setLiveAnnotation</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="s">&#39;zc.async.test.flag&#39;</span><span class="p">,</span> <span class="bp">True</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="n">res</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">getLiveAnnotation</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="s">&#39;zc.async.test.flag&#39;</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">0.4</span><span class="p">,</span> <span class="n">poll</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">job</span><span class="o">=</span><span class="n">other</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">pause</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">imaginaryNetworkCall</span><span class="p">)</span>
+</pre></div>
+<p>You can&#8217;t put a name in <tt class="docutils literal"><span class="pre">quota_names</span></tt> unless the quota has been created
+in the queue.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span><span class="o">.</span><span class="n">quota_names</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;test&#39;</span><span class="p">,)</span>
+<span class="gp">...</span>
+<span class="go">ValueError: (&#39;unknown quota name&#39;, &#39;test&#39;)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s">&#39;test&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span><span class="o">.</span><span class="n">quota_names</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;test&#39;</span><span class="p">,)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span><span class="o">.</span><span class="n">quota_names</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;test&#39;</span><span class="p">,)</span>
+</pre></div>
+<p>Now we can see the two jobs being performed serially.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">job2</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">time_flies</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">poll_interval</span><span class="p">)</span>
+<span class="go">1</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">10</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">... </span>    <span class="k">if</span> <span class="n">job1</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">ACTIVE</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="k">break</span>
+<span class="gp">... </span>    <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
+<span class="gp">... </span><span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>    <span class="k">print</span> <span class="s">&#39;TIME OUT&#39;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">PENDING</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span><span class="o">.</span><span class="n">annotations</span><span class="p">[</span><span class="s">&#39;zc.async.test.flag&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="bp">False</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job1</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job2</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">job1</span><span class="o">.</span><span class="n">result</span>
+<span class="go">None</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">job2</span><span class="o">.</span><span class="n">result</span>
+<span class="go">200 OK</span>
+</pre></div>
+<p>Quotas can be configured for limits greater than one at a time, if desired.
+This may be valuable when a needed resource is only available in limited
+numbers at a time.</p>
+<p>Note that, while quotas are valuable tools for doing serialized work such as
+updating a text index, other optimization features sometimes useful for this
+sort of task, such as collapsing similar jobs, are not provided directly by
+this package. This functionality could be trivially built on top of zc.async,
+however <a class="footnote-reference" href="#idea-for-collapsing-jobs" id="id11">[10]</a>.</p>
+</div>
+<div class="section" id="returning-jobs">
+<h3 id="returning-jobs">Returning Jobs<a class="headerlink" href="#returning-jobs" title="Permalink to this headline">¶</a></h3>
+<p>Our examples so far have done work directly.  What if the job wants to
+orchestrate other work?  One way this can be done is to return another
+job.  The result of the inner job will be the result of the first
+job once the inner job is finished.  This approach can be used to
+break up the work of long running processes; to be more cooperative to
+other jobs; and to make parts of a job that can be parallelized available
+to more workers.</p>
+<div class="section" id="serialized-work">
+<h4 id="serialized-work">Serialized Work<a class="headerlink" href="#serialized-work" title="Permalink to this headline">¶</a></h4>
+<p>First, consider a serialized example.  This simple pattern is one approach.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">second_job</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="c"># imagine a lot of work goes on...</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">value</span> <span class="o">*</span> <span class="mf">2</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">first_job</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="c"># imagine a lot of work goes on...</span>
+<span class="gp">... </span>    <span class="n">intermediate_value</span> <span class="o">=</span> <span class="mf">21</span>
+<span class="gp">... </span>    <span class="n">queue</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">getJob</span><span class="p">()</span><span class="o">.</span><span class="n">queue</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="n">second_job</span><span class="p">,</span> <span class="n">intermediate_value</span><span class="p">))</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">first_job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">attempts</span><span class="o">=</span><span class="mf">3</span><span class="p">)</span>
+<span class="go">TIME OUT</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">agent</span><span class="p">)</span>
+<span class="go">1</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">attempts</span><span class="o">=</span><span class="mf">3</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">42</span>
+</pre></div>
+<p>The job is now out of the agent.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">agent</span><span class="p">)</span>
+<span class="go">0</span>
+</pre></div>
+<p>The second_job could also have returned a job, allowing for additional
+legs.  Once the last job returns a real result, it will cascade through the
+past jobs back up to the original one.</p>
+<p>A different approach could have used callbacks.  Using callbacks can be
+somewhat more complicated to follow, but can allow for a cleaner
+separation of code: dividing code that does work from code that orchestrates
+the jobs. The <tt class="docutils literal"><span class="pre">serial</span></tt> helper function in the job module uses this pattern.
+Here&#8217;s a quick example of the helper function <a class="footnote-reference" href="#define-longer-wait" id="id12">[11]</a>.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">job_zero</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="mf">0</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">job_one</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="mf">1</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">job_two</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="mf">2</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">postprocess</span><span class="p">(</span><span class="n">zero</span><span class="p">,</span> <span class="n">one</span><span class="p">,</span> <span class="n">two</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">zero</span><span class="o">.</span><span class="n">result</span><span class="p">,</span> <span class="n">one</span><span class="o">.</span><span class="n">result</span><span class="p">,</span> <span class="n">two</span><span class="o">.</span><span class="n">result</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">serial</span><span class="p">(</span><span class="n">job_zero</span><span class="p">,</span> <span class="n">job_one</span><span class="p">,</span> <span class="n">job_two</span><span class="p">,</span>
+<span class="gp">... </span>                                    <span class="n">postprocess</span><span class="o">=</span><span class="n">postprocess</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">wait_repeatedly</span><span class="p">()</span>
+<span class="gp">... </span><span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">TIME OUT...</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">(0, 1, 2)</span>
+</pre></div>
+</blockquote>
+<p><a class="footnote-reference" href="#extra-serial-tricks" id="id13">[12]</a></p>
+<p>The <tt class="docutils literal"><span class="pre">parallel</span></tt> example we use below follows a similar pattern.</p>
+</div>
+<div class="section" id="parallelized-work">
+<h4 id="parallelized-work">Parallelized Work<a class="headerlink" href="#parallelized-work" title="Permalink to this headline">¶</a></h4>
+<p>Now how can we set up parallel jobs?  There are other good ways, but we
+can describe one way that avoids potential problems with the
+current-as-of-this-writing (ZODB 3.8 and trunk) default optimistic MVCC
+serialization behavior in the ZODB.  The solution uses callbacks, which
+also allows us to cleanly divide the &#8220;work&#8221; code from the synchronization
+code, as described in the previous paragraph.</p>
+<p>First, we&#8217;ll define the jobs that do work.  <tt class="docutils literal"><span class="pre">job_A</span></tt>, <tt class="docutils literal"><span class="pre">job_B</span></tt>, and
+<tt class="docutils literal"><span class="pre">job_C</span></tt> will be jobs that can be done in parallel, and
+<tt class="docutils literal"><span class="pre">postprocess</span></tt> will be a function that assembles the job results for a
+final result.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">job_A</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="c"># imaginary work...</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="mf">7</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">job_B</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="c"># imaginary work...</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="mf">14</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">job_C</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="c"># imaginary work...</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="mf">21</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">postprocess</span><span class="p">(</span><span class="o">*</span><span class="n">jobs</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="c"># this callable represents one that needs to wait for the</span>
+<span class="gp">... </span>    <span class="c"># parallel jobs to be done before it can process them and return</span>
+<span class="gp">... </span>    <span class="c"># the final result</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">job</span><span class="o">.</span><span class="n">result</span> <span class="k">for</span> <span class="n">job</span> <span class="ow">in</span> <span class="n">jobs</span><span class="p">)</span>
+<span class="gp">...</span>
+</pre></div>
+<p>This can be handled by a convenience function, <tt class="docutils literal"><span class="pre">parallel</span></tt>, that will arrange
+everything for you.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">parallel</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">job_A</span><span class="p">,</span> <span class="n">job_B</span><span class="p">,</span> <span class="n">job_C</span><span class="p">,</span> <span class="n">postprocess</span><span class="o">=</span><span class="n">postprocess</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<p>Now we just wait for the result.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">wait_repeatedly</span><span class="p">()</span>
+<span class="gp">... </span><span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">TIME OUT...</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">42</span>
+</pre></div>
+</blockquote>
+<p>Ta-da! <a class="footnote-reference" href="#extra-parallel-tricks" id="id14">[13]</a></p>
+<p>Now, how did this work?  Let&#8217;s look at a simple implementation directly.  We&#8217;ll
+use a slightly different postprocess, that expects results directly rather than
+the jobs.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">postprocess</span><span class="p">(</span><span class="o">*</span><span class="n">results</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="c"># this callable represents one that needs to wait for the</span>
+<span class="gp">... </span>    <span class="c"># parallel jobs to be done before it can process them and return</span>
+<span class="gp">... </span>    <span class="c"># the final result</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
+<span class="gp">...</span>
+</pre></div>
+<p>This code works with jobs to get everything done. Note, in the callback
+function, that mutating the same object we are checking (job.args) is the way
+we are enforcing necessary serializability with MVCC turned on.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">callback</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">result</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">job</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">job</span><span class="o">.</span><span class="n">args</span><span class="p">)</span> <span class="o">==</span> <span class="mf">3</span><span class="p">:</span> <span class="c"># all results are in</span>
+<span class="gp">... </span>        <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">getJob</span><span class="p">()</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">main_job</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="n">job</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">postprocess</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="n">queue</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">getJob</span><span class="p">()</span><span class="o">.</span><span class="n">queue</span>
+<span class="gp">... </span>    <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="p">(</span><span class="n">job_A</span><span class="p">,</span> <span class="n">job_B</span><span class="p">,</span> <span class="n">job_C</span><span class="p">):</span>
+<span class="gp">... </span>        <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span>
+<span class="gp">... </span>            <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">callback</span><span class="p">,</span> <span class="n">job</span><span class="p">))</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">job</span>
+<span class="gp">...</span>
+</pre></div>
+<p>That may be a bit mind-blowing at first.  The trick to catch here is that,
+because the main_job returns a job, the result of that job will become the
+result of the main_job once the returned (<tt class="docutils literal"><span class="pre">post_process</span></tt>) job is done.</p>
+<p>Now we&#8217;ll put this in and let it cook.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">main_job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">wait_repeatedly</span><span class="p">()</span>
+<span class="gp">... </span><span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">TIME OUT...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">42</span>
+</pre></div>
+</blockquote>
+<p>Once again, ta-da!</p>
+<p>For real-world usage, you&#8217;d also probably want to deal with the possibility of
+one or more of the jobs generating a Failure, among other edge cases.  The
+<tt class="docutils literal"><span class="pre">parallel</span></tt> function introduced above helps you handle this by returning
+jobs, rather than results, so you can analyze what went wrong and try to handle
+it.</p>
+</div>
+</div>
+<div class="section" id="returning-deferreds">
+<h3 id="returning-deferreds">Returning Deferreds<a class="headerlink" href="#returning-deferreds" title="Permalink to this headline">¶</a></h3>
+<p>What if you want to do work that doesn&#8217;t require a ZODB connection?  You
+can also return a Twisted deferred (twisted.internet.defer.Deferred).
+When you then <tt class="docutils literal"><span class="pre">callback</span></tt> the deferred with the eventual result, the
+agent will be responsible for setting that value on the original
+deferred and calling its callbacks.  This can be a useful trick for
+making network calls using Twisted or zc.ngi, for instance.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">imaginaryNetworkCall2</span><span class="p">(</span><span class="n">deferred</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="c"># make a network call...</span>
+<span class="gp">... </span>    <span class="n">deferred</span><span class="o">.</span><span class="n">callback</span><span class="p">(</span><span class="s">&#39;200 OK&#39;</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">twisted.internet.defer</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">threading</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">delegator</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="n">deferred</span> <span class="o">=</span> <span class="n">twisted</span><span class="o">.</span><span class="n">internet</span><span class="o">.</span><span class="n">defer</span><span class="o">.</span><span class="n">Deferred</span><span class="p">()</span>
+<span class="gp">... </span>    <span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="n">target</span><span class="o">=</span><span class="n">imaginaryNetworkCall2</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="p">(</span><span class="n">deferred</span><span class="p">,))</span>
+<span class="gp">... </span>    <span class="n">t</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">deferred</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">delegator</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&#39;200 OK&#39;</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="conclusion">
+<h2 id="conclusion">Conclusion<a class="headerlink" href="#conclusion" title="Permalink to this headline">¶</a></h2>
+<p>This concludes our discussion of zc.async usage. The <a class="reference external" href="README_2.html#configuration-without-zope-3"><em>next section</em></a> shows how to configure zc.async without
+Zope 3 <a class="footnote-reference" href="#stop-usage-reactor" id="id15">[14]</a>.</p>
+<p class="rubric">Footnotes</p>
+<table class="docutils footnote" frame="void" id="usagesetup" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id2">[1]</a></td><td><p class="first">We set up the configuration for our usage examples here.</p>
+<p>You must have two adapter registrations: IConnection to
+ITransactionManager, and IPersistent to IConnection.  We will also
+register IPersistent to ITransactionManager because the adapter is
+designed for it.</p>
+<p>We also need to be able to get data manager partials for functions and
+methods; normal partials for functions and methods; and a data manager for
+a partial. Here are the necessary registrations.</p>
+<p>The dispatcher will look for a UUID utility, so we also need one of these.</p>
+<p>The <tt class="docutils literal"><span class="pre">zc.async.configure.base</span></tt> function performs all of these
+registrations. If you are working with zc.async without ZCML you might want
+to use it or <tt class="docutils literal"><span class="pre">zc.async.configure.minimal</span></tt> as a convenience.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.configure</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">base</span><span class="p">()</span>
+</pre></div>
+<p>Now we&#8217;ll set up the database, and make some policy decisions.  As
+the subsequent <tt class="docutils literal"><span class="pre">configuration</span></tt> sections discuss, some helpers are
+available for you to set this up if you&#8217;d like, though it&#8217;s not too
+onerous to do it by hand.</p>
+<p>We&#8217;ll use a test reactor that we can control.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.testing</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">Reactor</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">start</span><span class="p">()</span> <span class="c"># this monkeypatches datetime.datetime.now</span>
+</pre></div>
+<p>We need to instantiate the dispatcher with a reactor and a DB.  We
+have the reactor, so here is the DB.  We use a FileStorage rather
+than a MappingStorage variant typical in tests and examples because
+we want MVCC.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">ZODB.FileStorage</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">storage</span> <span class="o">=</span> <span class="n">ZODB</span><span class="o">.</span><span class="n">FileStorage</span><span class="o">.</span><span class="n">FileStorage</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="s">&#39;zc_async.fs&#39;</span><span class="p">,</span> <span class="n">create</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">ZODB.DB</span> <span class="kn">import</span> <span class="n">DB</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">db</span> <span class="o">=</span> <span class="n">DB</span><span class="p">(</span><span class="n">storage</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">root</span><span class="p">()</span>
+</pre></div>
+<p>Now let&#8217;s create the mapping of queues, and a single queue.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.queue</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">mapping</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">KEY</span><span class="p">]</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">Queues</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span> <span class="o">=</span> <span class="n">mapping</span><span class="p">[</span><span class="s">&#39;&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">Queue</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<p>Now we can instantiate, activate, and perform some reactor work in order
+to let the dispatcher register with the queue.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.dispatcher</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">Dispatcher</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">reactor</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">activate</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">time_flies</span><span class="p">(</span><span class="mf">1</span><span class="p">)</span>
+<span class="go">1</span>
+</pre></div>
+<p>The UUID is set on the dispatcher.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.component</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">UUID</span> <span class="o">=</span> <span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">getUtility</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IUUID</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">UUID</span> <span class="o">==</span> <span class="n">UUID</span>
+<span class="go">True</span>
+</pre></div>
+<p>Here&#8217;s an agent named &#8216;main&#8217;</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.agent</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">agent</span><span class="o">.</span><span class="n">Agent</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">dispatchers</span><span class="p">[</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">UUID</span><span class="p">][</span><span class="s">&#39;main&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">agent</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span><span class="o">.</span><span class="n">filter</span> <span class="ow">is</span> <span class="bp">None</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span><span class="o">.</span><span class="n">size</span>
+<span class="go">3</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="commit-for-multidatabase" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id3">[2]</a></td><td><p class="first">We commit before we do the next step as a
+good practice, in case the queue is from a different database than
+the root.  See the configuration sections for a discussion about
+why putting the queue in another database might be a good idea.</p>
+<p class="last">Rather than committing the transaction,
+<tt class="docutils literal"><span class="pre">root._p_jar.add(root['demo'])</span></tt> would also accomplish the same
+thing from a multi-database perspective, without a commit.  It was
+not used in the example because the author judged the
+<tt class="docutils literal"><span class="pre">transaction.commit()</span></tt> to be less jarring to the reader.  If you
+are down here reading this footnote, maybe the author was wrong. :-)</p>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="already-passed" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id4">[3]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">send_message</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mf">2006</span><span class="p">,</span> <span class="mf">8</span><span class="p">,</span> <span class="mf">10</span><span class="p">,</span> <span class="mf">15</span><span class="p">,</span> <span class="n">tzinfo</span><span class="o">=</span><span class="n">pytz</span><span class="o">.</span><span class="n">UTC</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">imagine this sent a message to another machine</span>
+</pre></div>
+<p class="last">It&#8217;s worth noting that this situation constitutes a small exception
+in the handling of scheduled calls.  Scheduled calls usually get
+preference when jobs are handed out over normal non-scheduled &#8220;as soon as
+possible&#8221; jobs.  However, setting the begin_after date to an earlier
+time puts the job at the end of the (usually) FIFO queue of non-scheduled
+tasks: it is treated exactly as if the date had not been specified.</p>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="already-passed-timed-out" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id5">[4]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">send_message</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mf">2006</span><span class="p">,</span> <span class="mf">7</span><span class="p">,</span> <span class="mf">21</span><span class="p">,</span> <span class="mf">12</span><span class="p">,</span> <span class="n">tzinfo</span><span class="o">=</span><span class="n">pytz</span><span class="o">.</span><span class="n">UTC</span><span class="p">),</span>
+<span class="gp">... </span>    <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">hours</span><span class="o">=</span><span class="mf">1</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&lt;zc.twist.Failure zc.async.interfaces.TimeoutError&gt;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">sys</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span><span class="o">.</span><span class="n">printTraceback</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="p">)</span> <span class="c"># doctest: +NORMALIZE_WHITESPACE</span>
+<span class="gt">Traceback (most recent call last):</span>
+<span class="nc">Failure</span>: <span class="n-Identifier">zc.async.interfaces.TimeoutError:</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="job" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id6">[5]</a></td><td>The Job class can take arguments and keyword arguments
+for the wrapped callable at call time as well, similar to Python
+2.5&#8217;s <cite>partial</cite>.  This will be important when we use the Job as
+a callback.  For this use case, though, realize that the job
+will be called with no arguments, so you must supply all necessary
+arguments for the callable at creation time.</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="setliveannotation" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id7">[6]</a></td><td>Here&#8217;s the real rule, which is more complex.
+<em>Do not send non-persistent mutables or a persistent.Persistent
+object without a connection, unless you do not refer to it again in
+the current job.</em></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="getliveannotation" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id8">[7]</a></td><td>Here&#8217;s the real rule. <em>To prevent surprising
+errors, do not request an annotation that might be a persistent
+object.</em></td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="stats-1" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id9">[8]</a></td><td><p class="first">The dispatcher has a getStatistics method.  It also shows the
+fact that there is an active task.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">pprint</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">pprint</span><span class="o">.</span><span class="n">pprint</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">getStatistics</span><span class="p">())</span> <span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">{&#39;failed&#39;: 2,</span>
+<span class="go"> &#39;longest active&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;longest failed&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;longest successful&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;shortest active&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;shortest failed&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;shortest successful&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;started&#39;: 12,</span>
+<span class="go"> &#39;statistics end&#39;: datetime.datetime(2006, 8, 10, 15, 44, 22, 211),</span>
+<span class="go"> &#39;statistics start&#39;: datetime.datetime(2006, 8, 10, 15, 56, 47, 211),</span>
+<span class="go"> &#39;successful&#39;: 9,</span>
+<span class="go"> &#39;unknown&#39;: 0}</span>
+</pre></div>
+<p>We can also see the active job with <tt class="docutils literal"><span class="pre">getActiveJobIds</span></tt></p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job_ids</span> <span class="o">=</span> <span class="n">dispatcher</span><span class="o">.</span><span class="n">getActiveJobIds</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">job_ids</span><span class="p">)</span>
+<span class="go">1</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">info</span> <span class="o">=</span> <span class="n">dispatcher</span><span class="o">.</span><span class="n">getJobInfo</span><span class="p">(</span><span class="o">*</span><span class="n">job_ids</span><span class="p">[</span><span class="mf">0</span><span class="p">])</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">pprint</span><span class="o">.</span><span class="n">pprint</span><span class="p">(</span><span class="n">info</span><span class="p">)</span> <span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">{&#39;agent&#39;: &#39;main&#39;,</span>
+<span class="go"> &#39;call&#39;: &quot;&lt;zc.async.job.Job (oid ..., db &#39;unnamed&#39;) ``zc.async.doctest_test.annotateStatus()``&gt;&quot;,</span>
+<span class="go"> &#39;completed&#39;: None,</span>
+<span class="go"> &#39;failed&#39;: False,</span>
+<span class="go"> &#39;poll id&#39;: ...,</span>
+<span class="go"> &#39;queue&#39;: &#39;&#39;,</span>
+<span class="go"> &#39;quota names&#39;: (),</span>
+<span class="go"> &#39;reassigned&#39;: False,</span>
+<span class="go"> &#39;result&#39;: None,</span>
+<span class="go"> &#39;started&#39;: datetime.datetime(...),</span>
+<span class="go"> &#39;thread&#39;: ...}</span>
+<span class="go"> &gt;&gt;&gt; info[&#39;thread&#39;] is not None</span>
+<span class="go"> True</span>
+<span class="go"> &gt;&gt;&gt; info[&#39;poll id&#39;] is not None</span>
+<span class="go"> True</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="stats-2" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id10">[9]</a></td><td><p class="first">Now the task is done, as the stats reflect.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">pprint</span><span class="o">.</span><span class="n">pprint</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">getStatistics</span><span class="p">())</span> <span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">{&#39;failed&#39;: 2,</span>
+<span class="go"> &#39;longest active&#39;: None,</span>
+<span class="go"> &#39;longest failed&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;longest successful&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;shortest active&#39;: None,</span>
+<span class="go"> &#39;shortest failed&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;shortest successful&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;started&#39;: 12,</span>
+<span class="go"> &#39;statistics end&#39;: datetime.datetime(2006, 8, 10, 15, 44, 22, 211),</span>
+<span class="go"> &#39;statistics start&#39;: datetime.datetime(2006, 8, 10, 15, 56, 52, 211),</span>
+<span class="go"> &#39;successful&#39;: 10,</span>
+<span class="go"> &#39;unknown&#39;: 0}</span>
+</pre></div>
+<p>Note that these statistics eventually rotate out. By default, poll info
+will eventually rotate out after about 30 minutes (400 polls), and job info
+will only keep the most recent 200 stats in-memory. To look in history
+beyond these limits, check your logs.</p>
+<p>The <tt class="docutils literal"><span class="pre">getActiveJobIds</span></tt> list is empty now.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">getActiveJobIds</span><span class="p">()</span>
+<span class="go">[]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">info</span> <span class="o">=</span> <span class="n">dispatcher</span><span class="o">.</span><span class="n">getJobInfo</span><span class="p">(</span><span class="o">*</span><span class="n">job_ids</span><span class="p">[</span><span class="mf">0</span><span class="p">])</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">pprint</span><span class="o">.</span><span class="n">pprint</span><span class="p">(</span><span class="n">info</span><span class="p">)</span> <span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">{&#39;agent&#39;: &#39;main&#39;,</span>
+<span class="go"> &#39;call&#39;: &quot;&lt;zc.async.job.Job (oid ..., db &#39;unnamed&#39;) ``zc.async.doctest_test.annotateStatus()``&gt;&quot;,</span>
+<span class="go"> &#39;completed&#39;: datetime.datetime(...),</span>
+<span class="go"> &#39;failed&#39;: False,</span>
+<span class="go"> &#39;poll id&#39;: ...,</span>
+<span class="go"> &#39;queue&#39;: &#39;&#39;,</span>
+<span class="go"> &#39;quota names&#39;: (),</span>
+<span class="go"> &#39;reassigned&#39;: False,</span>
+<span class="go"> &#39;result&#39;: &#39;42&#39;,</span>
+<span class="go"> &#39;started&#39;: datetime.datetime(...),</span>
+<span class="go"> &#39;thread&#39;: ...}</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">info</span><span class="p">[</span><span class="s">&#39;thread&#39;</span><span class="p">]</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">info</span><span class="p">[</span><span class="s">&#39;poll id&#39;</span><span class="p">]</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span>
+<span class="go">True</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="idea-for-collapsing-jobs" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id11">[10]</a></td><td><p class="first">For instance, here is one approach.  Imagine
+you are queueing the job of indexing documents. If the same document has a
+request to index, the job could simply walk the queue and remove (<tt class="docutils literal"><span class="pre">pull</span></tt>)
+similar tasks, perhaps aggregating any necessary data. Since the jobs are
+serial because of a quota, no other worker should be trying to work on
+those jobs.</p>
+<p class="last">Alternatively, you could use a standalone, non-zc.async queue of things to
+do, and have the zc.async job just pull from that queue.  You might use
+zc.queue for this stand-alone queue, or zc.catalogqueue.</p>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="define-longer-wait" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id12">[11]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">wait_repeatedly</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">10</span><span class="p">):</span>
+<span class="gp">... </span>        <span class="n">reactor</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">attempts</span><span class="o">=</span><span class="mf">3</span><span class="p">)</span>
+<span class="gp">... </span>        <span class="k">if</span> <span class="n">job</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">COMPLETED</span><span class="p">:</span>
+<span class="gp">... </span>            <span class="k">break</span>
+<span class="gp">... </span>    <span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="k">assert</span> <span class="bp">False</span><span class="p">,</span> <span class="s">&#39;never completed&#39;</span>
+<span class="gp">...</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="extra-serial-tricks" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id13">[12]</a></td><td><p class="first">The <tt class="docutils literal"><span class="pre">serial</span></tt> helper can accept a partial closure
+for a <tt class="docutils literal"><span class="pre">postprocess</span></tt> argument.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">postprocess</span><span class="p">(</span><span class="n">extra_info</span><span class="p">,</span> <span class="o">*</span><span class="n">jobs</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">extra_info</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">j</span><span class="o">.</span><span class="n">result</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">jobs</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">serial</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">job_zero</span><span class="p">,</span> <span class="n">job_one</span><span class="p">,</span> <span class="n">job_two</span><span class="p">,</span>
+<span class="gp">... </span>    <span class="n">postprocess</span><span class="o">=</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">postprocess</span><span class="p">,</span> <span class="s">&#39;foo&#39;</span><span class="p">)))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">wait_repeatedly</span><span class="p">()</span>
+<span class="gp">... </span><span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">TIME OUT...</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">(&#39;foo&#39;, (0, 1, 2))</span>
+</pre></div>
+<p>The list of jobs can be extended by adding them to the args of the job
+returned by <tt class="docutils literal"><span class="pre">serial</span></tt> under these circumstances:</p>
+<ul class="simple">
+<li>before the job has started,</li>
+<li>by an inner job while it is running, or</li>
+<li>by any callback added to any inner job <em>before</em> that inner job has begun.</li>
+</ul>
+<p>Here&#8217;s an example.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">postprocess</span><span class="p">(</span><span class="o">*</span><span class="n">jobs</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="p">[</span><span class="n">j</span><span class="o">.</span><span class="n">result</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">jobs</span><span class="p">]</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">serial</span><span class="p">(</span><span class="n">postprocess</span><span class="o">=</span><span class="n">postprocess</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">second_job</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="s">&#39;second&#39;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">third_job</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="s">&#39;third&#39;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">schedule_third</span><span class="p">(</span><span class="n">main_job</span><span class="p">,</span> <span class="n">ignored</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">main_job</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">third_job</span><span class="p">))</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">first_job</span><span class="p">(</span><span class="n">main_job</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">j</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">second_job</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="n">main_job</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">j</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="n">j</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">schedule_third</span><span class="p">,</span> <span class="n">main_job</span><span class="p">))</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="s">&#39;first&#39;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">first_job</span><span class="p">,</span> <span class="n">job</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">wait_repeatedly</span><span class="p">()</span>
+<span class="gp">... </span><span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">TIME OUT...</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">[&#39;first&#39;, &#39;second&#39;, &#39;third&#39;]</span>
+</pre></div>
+<p class="last">Be warned, these sort of constructs allow infinite loops!</p>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="extra-parallel-tricks" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id14">[13]</a></td><td><p class="first">The <tt class="docutils literal"><span class="pre">parallel</span></tt> helper can accept a partial closure
+for a <tt class="docutils literal"><span class="pre">postprocess</span></tt> argument.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">postprocess</span><span class="p">(</span><span class="n">extra_info</span><span class="p">,</span> <span class="o">*</span><span class="n">jobs</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">extra_info</span><span class="p">,</span> <span class="nb">sum</span><span class="p">(</span><span class="n">j</span><span class="o">.</span><span class="n">result</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">jobs</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">parallel</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">job_A</span><span class="p">,</span> <span class="n">job_B</span><span class="p">,</span> <span class="n">job_C</span><span class="p">,</span>
+<span class="gp">... </span>    <span class="n">postprocess</span><span class="o">=</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">postprocess</span><span class="p">,</span> <span class="s">&#39;foo&#39;</span><span class="p">)))</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">wait_repeatedly</span><span class="p">()</span>
+<span class="gp">... </span><span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">TIME OUT...</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">(&#39;foo&#39;, 42)</span>
+</pre></div>
+<p>The list of jobs can be extended by adding them to the args of the job
+returned by <tt class="docutils literal"><span class="pre">parallel</span></tt> under these circumstances:</p>
+<ul class="simple">
+<li>before the job has started,</li>
+<li>by an inner job while it is running,</li>
+<li>by any callback added to any inner job <em>before</em> that inner job has begun.</li>
+</ul>
+<p>Here&#8217;s an example.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">postprocess</span><span class="p">(</span><span class="o">*</span><span class="n">jobs</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="p">[</span><span class="n">j</span><span class="o">.</span><span class="n">result</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">jobs</span><span class="p">]</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">parallel</span><span class="p">(</span><span class="n">postprocess</span><span class="o">=</span><span class="n">postprocess</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">second_job</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="s">&#39;second&#39;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">third_job</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="s">&#39;third&#39;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">schedule_third</span><span class="p">(</span><span class="n">main_job</span><span class="p">,</span> <span class="n">ignored</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">main_job</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">third_job</span><span class="p">))</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">first_job</span><span class="p">(</span><span class="n">main_job</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">j</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">second_job</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="n">main_job</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">j</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="n">j</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">schedule_third</span><span class="p">,</span> <span class="n">main_job</span><span class="p">))</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="s">&#39;first&#39;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">first_job</span><span class="p">,</span> <span class="n">job</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">wait_repeatedly</span><span class="p">()</span>
+<span class="gp">... </span><span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">TIME OUT...</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">[&#39;first&#39;, &#39;second&#39;, &#39;third&#39;]</span>
+</pre></div>
+<p class="last">As with <tt class="docutils literal"><span class="pre">serial</span></tt>, be warned, these sort of constructs allow infinite
+loops!</p>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="stop-usage-reactor" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id15">[14]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">threads</span> <span class="o">=</span> <span class="p">[]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">for</span> <span class="n">queue_pools</span> <span class="ow">in</span> <span class="n">dispatcher</span><span class="o">.</span><span class="n">queues</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">for</span> <span class="n">pool</span> <span class="ow">in</span> <span class="n">queue_pools</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
+<span class="gp">... </span>        <span class="n">threads</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">pool</span><span class="o">.</span><span class="n">threads</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_deactivation</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">for</span> <span class="n">thread</span> <span class="ow">in</span> <span class="n">threads</span><span class="p">:</span>
+<span class="gp">... </span>    <span class="n">thread</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="mf">3</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">pprint</span><span class="o">.</span><span class="n">pprint</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">getStatistics</span><span class="p">())</span> <span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">{&#39;failed&#39;: 2,</span>
+<span class="go"> &#39;longest active&#39;: None,</span>
+<span class="go"> &#39;longest failed&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;longest successful&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;shortest active&#39;: None,</span>
+<span class="go"> &#39;shortest failed&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;shortest successful&#39;: (..., &#39;unnamed&#39;),</span>
+<span class="go"> &#39;started&#39;: 54,</span>
+<span class="go"> &#39;statistics end&#39;: datetime.datetime(2006, 8, 10, 15, 44, 22, 211),</span>
+<span class="go"> &#39;statistics start&#39;: datetime.datetime(2006, 8, 10, 16, ...),</span>
+<span class="go"> &#39;successful&#39;: 52,</span>
+<span class="go"> &#39;unknown&#39;: 0}</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+</div>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Table Of Contents</h3>
+            <ul>
+<li><a class="reference external" href="">Usage</a><ul>
+<li><a class="reference external" href="#overview-and-basics">Overview and Basics</a><ul>
+<li><a class="reference external" href="#obtaining-the-queue">Obtaining the queue</a></li>
+<li><a class="reference external" href="#queue-put"><tt class="docutils literal"><span class="pre">queue.put</span></tt></a></li>
+<li><a class="reference external" href="#queue-pull-and-queue-remove"><tt class="docutils literal"><span class="pre">queue.pull</span></tt> and <tt class="docutils literal"><span class="pre">queue.remove</span></tt></a></li>
+<li><a class="reference external" href="#scheduled-calls">Scheduled Calls</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#jobs">Jobs</a><ul>
+<li><a class="reference external" href="#overview">Overview</a></li>
+<li><a class="reference external" href="#results">Results</a></li>
+<li><a class="reference external" href="#closures">Closures</a></li>
+<li><a class="reference external" href="#failures">Failures</a></li>
+<li><a class="reference external" href="#callbacks">Callbacks</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#advanced-techniques-and-tools">Advanced Techniques and Tools</a><ul>
+<li><a class="reference external" href="#zc-async-local">zc.async.local</a></li>
+<li><a class="reference external" href="#job-quotas">Job Quotas</a></li>
+<li><a class="reference external" href="#returning-jobs">Returning Jobs</a><ul>
+<li><a class="reference external" href="#serialized-work">Serialized Work</a></li>
+<li><a class="reference external" href="#parallelized-work">Parallelized Work</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#returning-deferreds">Returning Deferreds</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#conclusion">Conclusion</a></li>
+</ul>
+</li>
+</ul>
+
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="README.html" title="previous chapter">Introduction</a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="README_2.html" title="next chapter">Configuration (without Zope 3)</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/README_1.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README_2.html" title="Configuration (without Zope 3)"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README.html" title="Introduction"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/README_2.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/README_2.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/README_2.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,871 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Configuration (without Zope 3) &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="next" title="Configuration with Zope 3" href="README_3.html" />
+    <link rel="prev" title="Usage" href="README_1.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README_3.html" title="Configuration with Zope 3"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README_1.html" title="Usage"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="id1">
+<span id="configuration-without-zope-3"></span><h1 id="id1"><span id="configuration-without-zope-3"></span>Configuration (without Zope 3)<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h1>
+<p>This section discusses setting up zc.async without Zope 3. Since Zope 3 is
+ill-defined, we will be more specific: this describes setting up zc.async
+without ZCML, without any zope.app packages, and with as few dependencies as
+possible. A casual way of describing the dependencies is &#8220;ZODB, Twisted, and
+zope.component,&#8221; though we directly depend on some smaller packages and
+indirectly on others <a class="footnote-reference" href="#specific-dependencies" id="id2">[1]</a>.</p>
+<p>You may have one or two kinds of configurations for your software using
+zc.async. The simplest approach is to have all processes able both to put items
+in queues, and to perform them with a dispatcher. You can then use on-the-fly
+ZODB configuration to determine what jobs, if any, each process&#8217; dispatcher
+performs. If a dispatcher has no agents in a given queue, as we&#8217;ll discuss
+below, the dispatcher will not perform any job for that queue.</p>
+<p>However, if you want to create some processes that can only put items in a
+queue, and do not have a dispatcher at all, that is easy to do. We&#8217;ll call this
+a &#8220;client&#8221; process, and the full configuration a &#8220;client/server process&#8221;. As
+you might expect, the configuration of a client process is a subset of the
+configuration of the client/server process.</p>
+<p>The <tt class="docutils literal"><span class="pre">zc.async.configure</span></tt> module helps with basic configuration.  The
+<a class="reference external" href="QUICKSTART_1_VIRTUALENV.html#quickstart-with-virtualenv"><em>Quickstart with virtualenv</em></a> shows an example of using this for a very
+:quick start.  The current text uses some of those conveniences, but focuses
+more on understanding the underlying patterns, rather than the conveniences.</p>
+<p>We will first describe setting up a client, non-dispatcher process, in which
+you only can put items in a zc.async queue; and then describe setting up a
+dispatcher client/server process that can be used both to request and to
+perform jobs.</p>
+<div class="section" id="configuring-a-client-process">
+<h2 id="configuring-a-client-process">Configuring a Client Process<a class="headerlink" href="#configuring-a-client-process" title="Permalink to this headline">¶</a></h2>
+<p>Generally, zc.async configuration has four basic parts: component
+registrations, ZODB setup, ZODB configuration, and process configuration.  For
+a client process, we&#8217;ll discuss required component registrations; ZODB
+setup;  minimal ZODB configuration; process configuration; and then circle
+back around for some optional component registrations.</p>
+<div class="section" id="required-component-registrations">
+<h3 id="required-component-registrations">Required Component Registrations<a class="headerlink" href="#required-component-registrations" title="Permalink to this headline">¶</a></h3>
+<p>The required registrations can be installed for you by the
+<tt class="docutils literal"><span class="pre">zc.async.configure.base</span></tt> function. Most other examples in this package,
+such as those in the <a class="reference external" href="README_1.html#usage"><em>Usage</em></a> section, use this in their
+test setup.</p>
+<p>Again, for a quick start, you might just want to use the helper
+<tt class="docutils literal"><span class="pre">zc.async.configure.base</span></tt> function, and move on to the <a class="reference internal" href="#required-zodb-set-up">Required ZODB Set
+Up</a> section below.</p>
+<p>Here, though, we will go over each required registration to briefly explain
+what they are.</p>
+<p>You must have three adapter registrations: IConnection to
+ITransactionManager, IPersistent to IConnection, and IPersistent to
+ITransactionManager.</p>
+<p>The <tt class="docutils literal"><span class="pre">zc.twist</span></tt> package provides all of these adapters.  However,
+zope.app.keyreference also provides a version of the <tt class="docutils literal"><span class="pre">connection</span></tt> adapter
+that is identical or very similar, and that should work fine if you are
+already using that package in your application.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.twist</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.component</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideAdapter</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">twist</span><span class="o">.</span><span class="n">transactionManager</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideAdapter</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">twist</span><span class="o">.</span><span class="n">connection</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">ZODB.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideAdapter</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">twist</span><span class="o">.</span><span class="n">transactionManager</span><span class="p">,</span> <span class="n">adapts</span><span class="o">=</span><span class="p">(</span><span class="n">ZODB</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IConnection</span><span class="p">,))</span>
+</pre></div>
+<p>We also need to be able to adapt functions and methods to jobs.  The
+zc.async.job.Job class is the expected implementation.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">types</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.job</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideAdapter</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">,</span>
+<span class="gp">... </span>    <span class="n">adapts</span><span class="o">=</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">FunctionType</span><span class="p">,),</span>
+<span class="gp">... </span>    <span class="n">provides</span><span class="o">=</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IJob</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideAdapter</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">,</span>
+<span class="gp">... </span>    <span class="n">adapts</span><span class="o">=</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">MethodType</span><span class="p">,),</span>
+<span class="gp">... </span>    <span class="n">provides</span><span class="o">=</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IJob</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideAdapter</span><span class="p">(</span> <span class="c"># optional, rarely used</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">,</span>
+<span class="gp">... </span>    <span class="n">adapts</span><span class="o">=</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">twist</span><span class="o">.</span><span class="n">METHOD_WRAPPER_TYPE</span><span class="p">,),</span>
+<span class="gp">... </span>    <span class="n">provides</span><span class="o">=</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IJob</span><span class="p">)</span>
+</pre></div>
+<p>The queue looks for the UUID utility to set the <tt class="docutils literal"><span class="pre">assignerUUID</span></tt> job attribute,
+and may want to use it to optionally filter jobs during <tt class="docutils literal"><span class="pre">claim</span></tt> in the
+future. Also, the dispatcher will look for a UUID utility if a UUID is not
+specifically provided to its constructor.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">zc.async.instanceuuid</span> <span class="kn">import</span> <span class="n">UUID</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideUtility</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">UUID</span><span class="p">,</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IUUID</span><span class="p">,</span> <span class="s">&#39;&#39;</span><span class="p">)</span>
+</pre></div>
+<p>The UUID we register here is a UUID of the instance, which is expected
+to uniquely identify the process when in production. It is stored in
+the file specified by the <tt class="docutils literal"><span class="pre">ZC_ASYNC_UUID</span></tt> environment variable (or in
+<tt class="docutils literal"><span class="pre">os.join(os.getcwd(),</span> <span class="pre">'uuid.txt')</span></tt> if this is not specified, for easy
+initial experimentation with the package).</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">uuid</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">os</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s">&quot;ZC_ASYNC_UUID&quot;</span><span class="p">])</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">uuid_hex</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">uuid</span> <span class="o">=</span> <span class="n">uuid</span><span class="o">.</span><span class="n">UUID</span><span class="p">(</span><span class="n">uuid_hex</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">UUID</span> <span class="o">==</span> <span class="n">uuid</span>
+<span class="go">True</span>
+</pre></div>
+<p>The uuid.txt file is intended to stay in the instance home as a persistent
+identifier.</p>
+<p>Again, all of the required registrations above can be accomplished quickly with
+<tt class="docutils literal"><span class="pre">zc.async.configure.base</span></tt>.</p>
+</div>
+<div class="section" id="required-zodb-set-up">
+<h3 id="required-zodb-set-up">Required ZODB Set Up<a class="headerlink" href="#required-zodb-set-up" title="Permalink to this headline">¶</a></h3>
+<p>On a basic level, zc.async needs a setup that supports good conflict
+resolution.  Most or all production ZODB storages now have the necessary
+APIs to support MVCC.</p>
+<p>Of course, if you want to run multiple processes, you need ZEO. You should also
+then make sure that your ZEO server installation has all the code that includes
+conflict resolution, such as zc.queue, because, as of this writing, conflict
+resolution happens in the ZEO server, not in clients.</p>
+<p>A more subtle decision is whether to use multiple databases.  The zc.async
+dispatcher can generate a lot of database churn.  It may be wise to put the
+queue in a separate database from your content database(s).</p>
+<p>The downsides to this option include the fact that you must be careful to
+specify to which database objects belong; and that broken cross-database
+references are not handled gracefully in the ZODB as of this writing.</p>
+<p>We will use multiple databases for our example here, because we are trying to
+demonstrate production-quality examples. We will show this with a pure-Python
+approach, rather than the ZConfig approach usually used by Zope. If you know
+ZConfig, that will be a reasonable approach as well; see zope.app.appsetup
+for how Zope uses ZConfig to set up multidatabases.</p>
+<p>In our example, we create two file storages. In production, you might likely
+use ZEO; hooking ClientStorage up instead of FileStorage should be straight
+forward.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">databases</span> <span class="o">=</span> <span class="p">{}</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">ZODB.FileStorage</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">storage</span> <span class="o">=</span> <span class="n">ZODB</span><span class="o">.</span><span class="n">FileStorage</span><span class="o">.</span><span class="n">FileStorage</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="s">&#39;main.fs&#39;</span><span class="p">,</span> <span class="n">create</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">async_storage</span> <span class="o">=</span> <span class="n">ZODB</span><span class="o">.</span><span class="n">FileStorage</span><span class="o">.</span><span class="n">FileStorage</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="s">&#39;async.fs&#39;</span><span class="p">,</span> <span class="n">create</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">ZODB.DB</span> <span class="kn">import</span> <span class="n">DB</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">databases</span><span class="p">[</span><span class="s">&#39;&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">db</span> <span class="o">=</span> <span class="n">DB</span><span class="p">(</span><span class="n">storage</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">databases</span><span class="p">[</span><span class="s">&#39;async&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">async_db</span> <span class="o">=</span> <span class="n">DB</span><span class="p">(</span><span class="n">async_storage</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">async_db</span><span class="o">.</span><span class="n">databases</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">databases</span> <span class="o">=</span> <span class="n">databases</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">db</span><span class="o">.</span><span class="n">database_name</span> <span class="o">=</span> <span class="s">&#39;&#39;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">async_db</span><span class="o">.</span><span class="n">database_name</span> <span class="o">=</span> <span class="s">&#39;async&#39;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">root</span><span class="p">()</span>
+</pre></div>
+</blockquote>
+</div>
+<div class="section" id="zodb-configuration">
+<h3 id="zodb-configuration">ZODB Configuration<a class="headerlink" href="#zodb-configuration" title="Permalink to this headline">¶</a></h3>
+<div class="section" id="a-queue">
+<h4 id="a-queue">A Queue<a class="headerlink" href="#a-queue" title="Permalink to this headline">¶</a></h4>
+<p>All we must have for a client to be able to put jobs in a queue is ... a queue.</p>
+<p>For a quick start, the <tt class="docutils literal"><span class="pre">zc.async.subscribers</span></tt> module provides a subscriber to
+a DatabaseOpened event that does the right dance. See
+<tt class="docutils literal"><span class="pre">multidb_queue_installer</span></tt> and <tt class="docutils literal"><span class="pre">queue_installer</span></tt> in that module, and you can
+see that in use in <a class="reference external" href="README_3.html#configuration-with-zope-3"><em>Configuration with Zope 3</em></a>. For now, though, we&#8217;re taking
+things step by step and explaining what&#8217;s going on.</p>
+<p>Dispatchers look for queues in a mapping off the root of the database in
+a key defined as a constant: zc.async.interfaces.KEY.  This mapping should
+generally be a zc.async.queue.Queues object.</p>
+<p>If we were not using a multi-database for our example, we could simply install
+the queues mapping with this line:
+<tt class="docutils literal"><span class="pre">root[zc.async.interfaces.KEY]</span> <span class="pre">=</span> <span class="pre">zc.async.queue.Queues()</span></tt>.  We will need
+something a bit more baroque.  We will add the queues mapping to the &#8216;async&#8217;
+database, and then make it available in the main database (&#8216;&#8217;) with the proper
+key.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">conn2</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">get_connection</span><span class="p">(</span><span class="s">&#39;async&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.queue</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queues</span> <span class="o">=</span> <span class="n">conn2</span><span class="o">.</span><span class="n">root</span><span class="p">()[</span><span class="s">&#39;mounted_queues&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">Queues</span><span class="p">()</span>
+</pre></div>
+<p>Note that the &#8216;mounted_queues&#8217; key in the async database is arbitrary:
+what we care about is the key in the database that the dispatcher will
+see.</p>
+<p>Now we add the object explicitly to conn2, so that the ZODB will know the
+&#8220;real&#8221; database in which the object lives, even though it will be also
+accessible from the main database.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">conn2</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">queues</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span><span class="p">[</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">KEY</span><span class="p">]</span> <span class="o">=</span> <span class="n">queues</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<p>Now we need to put a queue in the queues collection.  We can have more than
+one, as discussed below, but we suggest a convention of the primary queue
+being available in a key of &#8216;&#8217; (empty string).</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span> <span class="o">=</span> <span class="n">queues</span><span class="p">[</span><span class="s">&#39;&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">Queue</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+</div>
+<div class="section" id="quotas">
+<h4 id="quotas">Quotas<a class="headerlink" href="#quotas" title="Permalink to this headline">¶</a></h4>
+<p>We touched on quotas in the usage section.  Some jobs will need to
+access resources that are shared across processes.  A central data
+structure such as an index in the ZODB is a prime example, but other
+examples might include a network service that only allows a certain
+number of concurrent connections.  These scenarios can be helped by
+quotas.</p>
+<p>Quotas are demonstrated in the usage section.  For configuration, you
+should know these characteristics:</p>
+<ul class="simple">
+<li>you cannot add a job with a quota name that is not defined in the
+queue <a class="footnote-reference" href="#undefined-quota-name" id="id3">[2]</a>;</li>
+<li>you cannot add a quota name to a job in a queue if the quota name is not
+defined in the queue <a class="footnote-reference" href="#no-mutation-to-undefined" id="id4">[3]</a>;</li>
+<li>you can create and remove quotas on the queue <a class="footnote-reference" href="#create-remove-quotas" id="id5">[4]</a>;</li>
+<li>you can remove quotas if pending jobs have their quota names&#8211;the quota name
+is then ignored <a class="footnote-reference" href="#remove-quotas" id="id6">[5]</a>;</li>
+<li>quotas default to a size of 1 <a class="footnote-reference" href="#default-size" id="id7">[6]</a>;</li>
+<li>this can be changed at creation or later <a class="footnote-reference" href="#change-size" id="id8">[7]</a>; and</li>
+<li>decreasing the size of a quota while the old quota size is filled will
+not affect the currently running jobs <a class="footnote-reference" href="#decreasing-affects-future" id="id9">[8]</a>.</li>
+</ul>
+</div>
+<div class="section" id="multiple-queues">
+<h4 id="multiple-queues">Multiple Queues<a class="headerlink" href="#multiple-queues" title="Permalink to this headline">¶</a></h4>
+<p>Since we put our queues in a mapping of them, we can also create multiple
+queues.  This can make some scenarios more convenient and simpler to reason
+about.  For instance, while you might have agents filtering jobs as we
+describe above, it might be simpler to say that you have a queue for one kind
+of job&#8211;say, processing a video file or an audio file&#8211;and a queue for other
+kinds of jobs.  Then it is easy and obvious to set up simple FIFO agents
+as desired for different dispatchers.  The same kind of logic could be
+accomplished with agents, but it is easier to picture the multiple queues.</p>
+<p>Another use case for multiple queues might be for specialized queues, like ones
+that broadcast jobs. You could write a queue subclass that broadcasts copies of
+jobs they get to all dispatchers, aggregating results.  This could be used to
+send &#8220;events&#8221; to all processes, or to gather statistics on certain processes,
+and so on.</p>
+<p>Generally, any time the application wants to be able to assert a kind of job
+rather than letting the agents decide what to do, having separate queues is
+a reasonable tool.</p>
+</div>
+</div>
+<div class="section" id="process-configuration">
+<h3 id="process-configuration">Process Configuration<a class="headerlink" href="#process-configuration" title="Permalink to this headline">¶</a></h3>
+<div class="section" id="daemonization">
+<h4 id="daemonization">Daemonization<a class="headerlink" href="#daemonization" title="Permalink to this headline">¶</a></h4>
+<p>You often want to daemonize your software, so that you can restart it if
+there&#8217;s a problem, keep track of it and monitor it, and so on.  ZDaemon
+(<a class="reference external" href="http://pypi.python.org/pypi/zdaemon">http://pypi.python.org/pypi/zdaemon</a>) and Supervisor (<a class="reference external" href="http://supervisord.org/">http://supervisord.org/</a>)
+are two fairly simple-to-use ways of doing this for both client and
+client/server processes. If your main application can be packaged as a
+setuptools distribution (egg or source release or even development egg) then
+you can have your main application as a zc.async client and your dispatchers
+running a separate zc.async-only main loop that simply includes your main
+application as a dependency, so the necessary software is around. You may have
+to do a bit more configuration on the client/server side to mimic global
+registries such as zope.component registrations and so on between the client
+and the client/servers, but this shouldn&#8217;t be too bad.</p>
+</div>
+<div class="section" id="uuid-file-location">
+<h4 id="uuid-file-location">UUID File Location<a class="headerlink" href="#uuid-file-location" title="Permalink to this headline">¶</a></h4>
+<p>As discussed above, the instanceuuid module will look for an environmental
+variable <tt class="docutils literal"><span class="pre">ZC_ASYNC_UUID</span></tt> to find the file name to use, and failing that will
+use <tt class="docutils literal"><span class="pre">os.join(os.getcwd(),</span> <span class="pre">'uuid.txt')</span></tt>.  It&#8217;s worth noting that daemonization
+tools such as ZDaemon and Supervisor (3 or greater) make setting environment
+values for child processes an easy (and repeatable) configuration file setting.</p>
+</div>
+</div>
+<div class="section" id="optional-component-registrations-for-a-client-process">
+<h3 id="optional-component-registrations-for-a-client-process">Optional Component Registrations for a Client Process<a class="headerlink" href="#optional-component-registrations-for-a-client-process" title="Permalink to this headline">¶</a></h3>
+<p>The only optional component registration potentially valuable for client
+instances that only put jobs in the queue is registering an adapter from
+persistent objects to a queue.  The <tt class="docutils literal"><span class="pre">zc.async.queue.getDefaultQueue</span></tt> adapter
+does this for an adapter to the queue named &#8216;&#8217; (empty string).  Since that&#8217;s
+what we have from the <a class="reference internal" href="#zodb-configuration">ZODB Configuration</a> above section, we&#8217;ll register it.
+Writing your own adapter is trivial, as you can see if you look at the
+implementation of this function.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideAdapter</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">getDefaultQueue</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IQueue</span><span class="p">(</span><span class="n">root</span><span class="p">)</span> <span class="ow">is</span> <span class="n">queue</span>
+<span class="go">True</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="configuring-a-client-server-process">
+<h2 id="configuring-a-client-server-process">Configuring a Client/Server Process<a class="headerlink" href="#configuring-a-client-server-process" title="Permalink to this headline">¶</a></h2>
+<p>Configuring a client/server process&#8211;something that includes a running
+dispatcher&#8211;means doing everything described above, plus a bit more.  You
+need to set up and start a reactor and dispatcher; configure agents as desired
+to get the dispatcher to do some work; and optionally configure logging.</p>
+<p>For a quick start, the <tt class="docutils literal"><span class="pre">zc.async.subscribers</span></tt> module has some conveniences
+to start a threaded reactor and dispatcher, and to install agents.  You might
+want to look at those to get started.  They are also used in the Zope 3
+configuration (README_3).  Meanwhile, this document continues to go
+step-by-step instead, to try and explain the components and configuration.</p>
+<p>Even though it seems reasonable to first start a dispatcher and then set up its
+agents, we&#8217;ll first define a subscriber to create an agent. As we&#8217;ll see below,
+the dispatcher fires an event when it registers with a queue, and another when
+it activates the queue. These events give you the opportunity to register
+subscribers to add one or more agents to a queue, to tell the dispatcher what
+jobs to perform. zc.async.agent.addMainAgentActivationHandler is a reasonable
+starter: it adds a single agent named &#8216;main&#8217; if one does not exist. The agent
+has a simple indiscriminate FIFO policy for the queue. If you want to write
+your own subscriber, look at this, or at the more generic subscriber in the
+<tt class="docutils literal"><span class="pre">zc.async.subscribers</span></tt> module.</p>
+<p>Agents are an important part of the ZODB configuration, and so are described
+more in depth below.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.agent</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideHandler</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">agent</span><span class="o">.</span><span class="n">addMainAgentActivationHandler</span><span class="p">)</span>
+</pre></div>
+<p>This subscriber is registered for the IDispatcherActivated event; another
+approach might use the IDispatcherRegistered event.</p>
+<div class="section" id="starting-the-dispatcher">
+<h3 id="starting-the-dispatcher">Starting the Dispatcher<a class="headerlink" href="#starting-the-dispatcher" title="Permalink to this headline">¶</a></h3>
+<p>Now we can start the reactor, and start the dispatcher.
+In some applications this may be done with an event subscriber to
+DatabaseOpened, as is done in <tt class="docutils literal"><span class="pre">zc.async.subscribers</span></tt>. Here, we will do it
+inline.</p>
+<p>Any object that conforms to the specification of zc.async.interfaces.IReactor
+will be usable by the dispatcher.  For our example, we will use our own instance
+of the Twisted select-based reactor running in a separate thread.  This is
+separate from the Twisted reactor installed in twisted.internet.reactor, and
+so this approach can be used with an application that does not otherwise use
+Twisted (for instance, a Zope application using the &#8220;classic&#8221; zope publisher).</p>
+<p>The testing module also has a reactor on which the <cite>Usage</cite> section relies, if
+you would like to see a minimal contract.</p>
+<p>Configuring the basics is fairly simple, as we&#8217;ll see in a moment.  The
+trickiest part is to handle signals cleanly. It is also optional! The
+dispatcher will eventually figure out that there was not a clean shut down
+before and take care of it. Here, though, essentially as an optimization, we
+install signal handlers in the main thread using <tt class="docutils literal"><span class="pre">reactor._handleSignals</span></tt>.
+<tt class="docutils literal"><span class="pre">reactor._handleSignals</span></tt> may work in some real-world applications, but if
+your application already needs to handle signals you may need a more careful
+approach. Again, see <tt class="docutils literal"><span class="pre">zc.async.subscribers</span></tt> for some options you can explore.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">twisted.internet.selectreactor</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span> <span class="o">=</span> <span class="n">twisted</span><span class="o">.</span><span class="n">internet</span><span class="o">.</span><span class="n">selectreactor</span><span class="o">.</span><span class="n">SelectReactor</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">_handleSignals</span><span class="p">()</span>
+</pre></div>
+<p>Now we are ready to instantiate our dispatcher.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.dispatcher</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">Dispatcher</span><span class="p">(</span><span class="n">db</span><span class="p">,</span> <span class="n">reactor</span><span class="p">)</span>
+</pre></div>
+<p>Notice it has the uuid defined in instanceuuid.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">UUID</span> <span class="o">==</span> <span class="n">UUID</span>
+<span class="go">True</span>
+</pre></div>
+<p>Now we can start the reactor and the dispatcher in a thread.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">threading</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">start</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="n">dispatcher</span><span class="o">.</span><span class="n">activate</span><span class="p">()</span>
+<span class="gp">... </span>    <span class="n">reactor</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">installSignalHandlers</span><span class="o">=</span><span class="mf">0</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">start</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">thread</span><span class="o">.</span><span class="n">setDaemon</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
+</pre></div>
+</blockquote>
+<p>The dispatcher should be starting up now.  Let&#8217;s wait for it to activate.
+We&#8217;re using a test convenience, get_poll, defined in the testing module.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">zc.async.testing</span> <span class="kn">import</span> <span class="n">get_poll</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">poll</span> <span class="o">=</span> <span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">,</span> <span class="mf">0</span><span class="p">)</span>
+</pre></div>
+<p>We&#8217;re off!  The events have been fired for registering and activating the
+dispatcher.  Therefore, our subscriber to add our agent has fired.</p>
+<p>We need to begin our transaction to synchronize our view of the database.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+</pre></div>
+<p>We get the collection of dispatcher agents from the queue, using the UUID.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher_agents</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">dispatchers</span><span class="p">[</span><span class="n">UUID</span><span class="p">]</span>
+</pre></div>
+<p>It has one agent&#8211;the one placed by our subscriber.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher_agents</span><span class="o">.</span><span class="n">keys</span><span class="p">()</span>
+<span class="go">[&#39;main&#39;]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span> <span class="o">=</span> <span class="n">dispatcher_agents</span><span class="p">[</span><span class="s">&#39;main&#39;</span><span class="p">]</span>
+</pre></div>
+<p>Now we have our agent!  But...what is it <a class="footnote-reference" href="#stop-config-reactor" id="id10">[9]</a>?</p>
+</div>
+<div class="section" id="agents">
+<h3 id="agents">Agents<a class="headerlink" href="#agents" title="Permalink to this headline">¶</a></h3>
+<p>Agents are the way you control what a dispatcher&#8217;s worker threads do.  They
+pick the jobs and assign them to their dispatcher when the dispatcher asks.</p>
+<p><em>If a dispatcher does not have any agents in a given queue, it will not perform
+any tasks for that queue.</em></p>
+<p>We currently have an agent that simply asks for the next available FIFO job.
+We are using an agent implementation that allows you to specify a callable to
+filter the job.  That callable is now None.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span><span class="o">.</span><span class="n">filter</span> <span class="ow">is</span> <span class="bp">None</span>
+<span class="go">True</span>
+</pre></div>
+<p>What does a filter do?  A filter takes a job and returns a value evaluated as a
+boolean.  For instance, let&#8217;s say we always wanted a certain number of threads
+available for working on a particular call; for the purpose of example, we&#8217;ll
+use <tt class="docutils literal"><span class="pre">operator.mul</span></tt>, though a more real-world example might be a network call
+or a particular call in your application.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">operator</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">chooseMul</span><span class="p">(</span><span class="n">job</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">job</span><span class="o">.</span><span class="n">callable</span> <span class="o">==</span> <span class="n">operator</span><span class="o">.</span><span class="n">mul</span>
+<span class="gp">...</span>
+</pre></div>
+<p>You might want something more sophisticated, such as preferring operator.mul,
+but if one is not in the queue, it will take any; or doing any other priority
+variations.  To do this, you&#8217;ll want to write your own agent&#8211;possibly
+inheriting from the provided one and overriding <tt class="docutils literal"><span class="pre">_choose</span></tt>.</p>
+<p>Let&#8217;s set up another agent, in addition to the default one, that has
+the <tt class="docutils literal"><span class="pre">chooseMul</span></tt> policy.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">agent2</span> <span class="o">=</span> <span class="n">dispatcher_agents</span><span class="p">[</span><span class="s">&#39;mul&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">agent</span><span class="o">.</span><span class="n">Agent</span><span class="p">(</span><span class="n">chooseMul</span><span class="p">)</span>
+</pre></div>
+<p>Another characteristic of agents is that they specify how many jobs they
+should pick at a time.  The dispatcher actually adjusts the size of the
+ZODB connection pool to accommodate its agents&#8217; size.  The default is 3.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span><span class="o">.</span><span class="n">size</span>
+<span class="go">3</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">agent2</span><span class="o">.</span><span class="n">size</span>
+<span class="go">3</span>
+</pre></div>
+<p>We can change that at creation or later.</p>
+<p>Finally, it&#8217;s worth noting that agents contain the jobs that are currently
+worked on by the dispatcher, on their behalf; and have a <tt class="docutils literal"><span class="pre">completed</span></tt>
+collection of the more recent completed jobs, beginning with the most recently
+completed job.</p>
+</div>
+<div class="section" id="logging-and-monitoring">
+<h3 id="logging-and-monitoring">Logging and Monitoring<a class="headerlink" href="#logging-and-monitoring" title="Permalink to this headline">¶</a></h3>
+<p>Logs are sent to the <tt class="docutils literal"><span class="pre">zc.async.events</span></tt> log for big events, like startup and
+shutdown, and errors.  Poll and job logs are sent to <tt class="docutils literal"><span class="pre">zc.async.trace</span></tt>.
+Configure the standard Python logging module as usual to send these logs where
+you need.  Be sure to auto-rotate the trace logs.</p>
+<p>The package supports monitoring using zc.monitor.  Using this package includes
+only a very few additional dependencies: zc.monitor, simplejson, and zc.ngi. An
+example of setting it up without Zope 3 is in the end of
+<a class="reference external" href="QUICKSTART_1_VIRTUALENV.html#quickstart-with-virtualenv"><em>Quickstart with virtualenv</em></a>.  If you would like to use it, see that
+document, monitor.txt in the package, and our next section:
+<a class="reference external" href="README_3.html#configuration-with-zope-3"><em>Configuration with Zope 3</em></a>.</p>
+<p>Otherwise, if you want to roll your own monitoring, glance at monitor.py and
+monitordb.py&#8211;you&#8217;ll see that you should be able to reuse most of the heavy
+lifting, so it should be pretty easy to hook up the basic data another way.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>
+</pre></div>
+<p class="rubric">Footnotes</p>
+<table class="docutils footnote" frame="void" id="specific-dependencies" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id2">[1]</a></td><td><p class="first">More specifically, as of this writing,
+these are the minimal egg dependencies (including indirect
+dependencies):</p>
+<ul>
+<li><dl class="first docutils">
+<dt>pytz</dt>
+<dd><p class="first last">A Python time zone library</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>rwproperty</dt>
+<dd><p class="first last">A small package of descriptor conveniences</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>uuid</dt>
+<dd><p class="first last">The uuid module included in Python 2.5</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zc.dict</dt>
+<dd><p class="first last">A ZODB-aware dict implementation based on BTrees.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zc.queue</dt>
+<dd><p class="first last">A ZODB-aware queue</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zc.twist</dt>
+<dd><p class="first last">Conveniences for working with Twisted and the ZODB</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>twisted</dt>
+<dd><p class="first last">The Twisted internet library.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>ZConfig</dt>
+<dd><p class="first last">A general configuration package coming from the Zope project with which
+the ZODB tests.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zdaemon</dt>
+<dd><p class="first last">A general daemon tool coming from the Zope project.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>ZODB3</dt>
+<dd><p class="first last">The Zope Object Database.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zope.bforest</dt>
+<dd><p class="first last">Aggregations of multiple BTrees into a single dict-like structure,
+reasonable for rotating data structures, among other purposes.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zope.component</dt>
+<dd><p class="first last">A way to hook together code by contract.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zope.deferredimport</dt>
+<dd><p class="first last">A way to defer imports in Python packages, often to prevent circular
+import problems.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zope.deprecation</dt>
+<dd><p class="first last">A small framework for deprecating features.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zope.event</dt>
+<dd><p class="first last">An exceedingly small event framework that derives its power from
+zope.component.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zope.i18nmessageid</dt>
+<dd><p class="first last">A way to specify strings to be translated.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zope.interface</dt>
+<dd><p class="first last">A way to specify code contracts and other data structures.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zope.proxy</dt>
+<dd><p class="first last">A way to proxy other Python objects.</p>
+</dd>
+</dl>
+</li>
+<li><dl class="first docutils">
+<dt>zope.testing</dt>
+<dd><p class="first last">Testing extensions and helpers.</p>
+</dd>
+</dl>
+</li>
+</ul>
+<p class="last">The next section, <a class="reference external" href="README_3.html#configuration-with-zope-3"><em>Configuration with Zope 3</em></a>, still tries to limit
+dependencies&#8211;we only rely on additional packages zc.z3monitor, simplejson,
+and zope.app.appsetup ourselves&#8211;but as of this writing zope.app.appsetup
+ends up dragging in a large chunk of zope.app.* packages. Hopefully that
+will be refactored in Zope itself, and our full Zope 3 configuration can
+benefit from the reduced indirect dependencies.</p>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="undefined-quota-name" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id3">[2]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">operator</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.job</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">operator</span><span class="o">.</span><span class="n">mul</span><span class="p">,</span> <span class="mf">5</span><span class="p">,</span> <span class="mf">2</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">quota_names</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;content catalog&#39;</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">quota_names</span>
+<span class="go">(&#39;content catalog&#39;,)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="go">ValueError: (&#39;unknown quota name&#39;, &#39;content catalog&#39;)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
+<span class="go">0</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="no-mutation-to-undefined" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id4">[3]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">quota_names</span> <span class="o">=</span> <span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">quota_names</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;content catalog&#39;</span><span class="p">,)</span>
+<span class="gp">...</span>
+<span class="go">ValueError: (&#39;unknown quota name&#39;, &#39;content catalog&#39;)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">quota_names</span>
+<span class="go">()</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="create-remove-quotas" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id5">[4]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="p">)</span>
+<span class="go">[]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s">&#39;testing&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="p">)</span>
+<span class="go">[&#39;testing&#39;]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s">&#39;testing&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="p">)</span>
+<span class="go">[]</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="remove-quotas" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id6">[5]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s">&#39;content catalog&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">quota_names</span> <span class="o">=</span> <span class="p">(</span><span class="s">&#39;content catalog&#39;</span><span class="p">,)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="s">&#39;content catalog&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">quota_names</span>
+<span class="go">(&#39;content catalog&#39;,)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
+<span class="go">0</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="default-size" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id7">[6]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s">&#39;content catalog&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="p">[</span><span class="s">&#39;content catalog&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">size</span>
+<span class="go">1</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="change-size" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id8">[7]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="p">[</span><span class="s">&#39;content catalog&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="mf">2</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="p">[</span><span class="s">&#39;content catalog&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">size</span>
+<span class="go">2</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s">&#39;frobnitz account&#39;</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mf">3</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="p">[</span><span class="s">&#39;frobnitz account&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">size</span>
+<span class="go">3</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="decreasing-affects-future" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id9">[8]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">operator</span><span class="o">.</span><span class="n">mul</span><span class="p">,</span> <span class="mf">5</span><span class="p">,</span> <span class="mf">2</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">operator</span><span class="o">.</span><span class="n">mul</span><span class="p">,</span> <span class="mf">5</span><span class="p">,</span> <span class="mf">2</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job3</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">operator</span><span class="o">.</span><span class="n">mul</span><span class="p">,</span> <span class="mf">5</span><span class="p">,</span> <span class="mf">2</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span><span class="o">.</span><span class="n">quota_names</span> <span class="o">=</span> <span class="n">job2</span><span class="o">.</span><span class="n">quota_names</span> <span class="o">=</span> <span class="n">job3</span><span class="o">.</span><span class="n">quota_names</span> <span class="o">=</span> <span class="p">(</span>
+<span class="gp">... </span>    <span class="s">&#39;content catalog&#39;</span><span class="p">,)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">job1</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">job2</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job3</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">job3</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">()</span>
+<span class="go">None</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">quota</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">quotas</span><span class="p">[</span><span class="s">&#39;content catalog&#39;</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">quota</span><span class="p">)</span>
+<span class="go">2</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">quota</span><span class="p">)</span> <span class="o">==</span> <span class="p">[</span><span class="n">job1</span><span class="p">,</span> <span class="n">job2</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">quota</span><span class="o">.</span><span class="n">filled</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">quota</span><span class="o">.</span><span class="n">size</span> <span class="o">=</span> <span class="mf">1</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">quota</span><span class="o">.</span><span class="n">filled</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">()</span>
+<span class="go">None</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job1</span><span class="p">()</span>
+<span class="go">10</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">()</span>
+<span class="go">None</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">quota</span><span class="p">)</span>
+<span class="go">1</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">quota</span><span class="p">)</span> <span class="o">==</span> <span class="p">[</span><span class="n">job2</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job2</span><span class="p">()</span>
+<span class="go">10</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job3</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">quota</span><span class="p">)</span> <span class="o">==</span> <span class="p">[</span><span class="n">job3</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">quota</span><span class="p">)</span>
+<span class="go">1</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job3</span><span class="p">()</span>
+<span class="go">10</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">()</span>
+<span class="go">None</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
+<span class="go">0</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">quota</span><span class="o">.</span><span class="n">clean</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">quota</span><span class="p">)</span>
+<span class="go">0</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">quota</span><span class="o">.</span><span class="n">filled</span>
+<span class="go">False</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="stop-config-reactor" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id10">[9]</a></td><td><p class="first">We don&#8217;t want the live dispatcher for our demos,
+actually.  See dispatcher.txt to see the live dispatcher actually in use.
+So, here we&#8217;ll stop the &#8220;real&#8221; reactor and switch to a testing one.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">callFromThread</span><span class="p">(</span><span class="n">reactor</span><span class="o">.</span><span class="n">stop</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">thread</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="mf">3</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">assert</span> <span class="ow">not</span> <span class="n">dispatcher</span><span class="o">.</span><span class="n">activated</span><span class="p">,</span> <span class="s">&#39;dispatcher did not deactivate&#39;</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.testing</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">Reactor</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">_reactor</span> <span class="o">=</span> <span class="n">reactor</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">activate</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">reactor</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+</div>
+</div>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Table Of Contents</h3>
+            <ul>
+<li><a class="reference external" href="">Configuration (without Zope 3)</a><ul>
+<li><a class="reference external" href="#configuring-a-client-process">Configuring a Client Process</a><ul>
+<li><a class="reference external" href="#required-component-registrations">Required Component Registrations</a></li>
+<li><a class="reference external" href="#required-zodb-set-up">Required ZODB Set Up</a></li>
+<li><a class="reference external" href="#zodb-configuration">ZODB Configuration</a><ul>
+<li><a class="reference external" href="#a-queue">A Queue</a></li>
+<li><a class="reference external" href="#quotas">Quotas</a></li>
+<li><a class="reference external" href="#multiple-queues">Multiple Queues</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#process-configuration">Process Configuration</a><ul>
+<li><a class="reference external" href="#daemonization">Daemonization</a></li>
+<li><a class="reference external" href="#uuid-file-location">UUID File Location</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#optional-component-registrations-for-a-client-process">Optional Component Registrations for a Client Process</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#configuring-a-client-server-process">Configuring a Client/Server Process</a><ul>
+<li><a class="reference external" href="#starting-the-dispatcher">Starting the Dispatcher</a></li>
+<li><a class="reference external" href="#agents">Agents</a></li>
+<li><a class="reference external" href="#logging-and-monitoring">Logging and Monitoring</a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="README_1.html" title="previous chapter">Usage</a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="README_3.html" title="next chapter">Configuration with Zope 3</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/README_2.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README_3.html" title="Configuration with Zope 3"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README_1.html" title="Usage"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/README_3.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/README_3.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/README_3.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,168 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Configuration with Zope 3 &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="next" title="Shared Single Database Set Up" href="README_3a.html" />
+    <link rel="prev" title="Configuration (without Zope 3)" href="README_2.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README_3a.html" title="Shared Single Database Set Up"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README_2.html" title="Configuration (without Zope 3)"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="id1">
+<span id="configuration-with-zope-3"></span><h1 id="id1"><span id="configuration-with-zope-3"></span>Configuration with Zope 3<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h1>
+<p>Our last main section can be the shortest yet, both because we&#8217;ve already
+introduced all of the main concepts, and because we will be leveraging
+conveniences to automate much of the configuration shown in the section
+discussing configuration without Zope 3.</p>
+<div class="section" id="client-set-up">
+<h2 id="client-set-up">Client Set Up<a class="headerlink" href="#client-set-up" title="Permalink to this headline">¶</a></h2>
+<p>If you want to set up a client alone, without a dispatcher, include the egg in
+your setup.py, include the configure.zcml in your applications zcml, make sure
+you share the database in which the queues will be held, and make sure that
+either the zope.app.keyreference.persistent.connectionOfPersistent adapter is
+registered, or zc.twist.connection.</p>
+<p>That should be it.</p>
+</div>
+<div class="section" id="client-server-set-up">
+<h2 id="client-server-set-up">Client/Server Set Up<a class="headerlink" href="#client-server-set-up" title="Permalink to this headline">¶</a></h2>
+<p>For a client/server combination, use zcml that is something like the
+basic_dispatcher_policy.zcml, make sure you have access to the database with
+the queues, configure logging and monitoring as desired, configure the
+<tt class="docutils literal"><span class="pre">ZC_ASYNC_UUID</span></tt> environmental variable in zdaemon.conf if you are in
+production, and start up! Getting started is really pretty easy. You can even
+start a dispatcher-only version by not starting any servers in zcml.</p>
+<p>In comparison to the non-Zope 3 usage, an important difference in your setup.py
+is that, if you want the full set up described below, including zc.z3monitor,
+you&#8217;ll need to specify &#8220;zc.async [z3]&#8221; as the desired package in your
+<tt class="docutils literal"><span class="pre">install_requires</span></tt>, as opposed to just &#8220;zc.async&#8221; <a class="footnote-reference" href="#extras-require" id="id2">[1]</a>.</p>
+<p>We&#8217;ll look at this by making a zope.conf-alike and a site.zcml-alike.  We&#8217;ll
+need a place to put some files, so we&#8217;ll use a temporary directory.  This, and
+the comments in the files that we set up, are the primary differences between
+our examples and a real set up.</p>
+<p>We&#8217;ll do this in two versions.  The first version uses a single database, as
+you might do to get started quickly, or for a small site.  The second version
+has one database for the main application, and one database for the async data,
+as will be more appropriate for typical production usage.</p>
+<ul>
+<li><a class="reference external" href="README_3a.html">Shared Single Database Set Up</a></li>
+</ul>
+<ul>
+<li><a class="reference external" href="README_3b.html">Two Database Set Up</a></li>
+</ul>
+<p class="rubric">Footnotes</p>
+<table class="docutils footnote" frame="void" id="extras-require" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id2">[1]</a></td><td>The &#8220;[z3]&#8221; is an &#8220;extra&#8221;, defined in zc.async&#8217;s setup.py
+in <tt class="docutils literal"><span class="pre">extras_require</span></tt>. It pulls along zc.z3monitor and simplejson in
+addition to the packages described in the
+<a class="reference external" href="README_2.html#configuration-without-zope-3"><em>Configuration (without Zope 3)</em></a> section. Unfortunately, zc.z3monitor
+depends on zope.app.appsetup, which as of this writing ends up depending
+indirectly on many, many packages, some as far flung as zope.app.rotterdam.</td></tr>
+</tbody>
+</table>
+</div>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Table Of Contents</h3>
+            <ul>
+<li><a class="reference external" href="">Configuration with Zope 3</a><ul>
+<li><a class="reference external" href="#client-set-up">Client Set Up</a></li>
+<li><a class="reference external" href="#client-server-set-up">Client/Server Set Up</a><ul>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="README_2.html" title="previous chapter">Configuration (without Zope 3)</a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="README_3a.html" title="next chapter">Shared Single Database Set Up</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/README_3.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README_3a.html" title="Shared Single Database Set Up"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README_2.html" title="Configuration (without Zope 3)"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/README_3a.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/README_3a.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/README_3a.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,345 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Shared Single Database Set Up &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="up" title="Configuration with Zope 3" href="README_3.html" />
+    <link rel="next" title="Two Database Set Up" href="README_3b.html" />
+    <link rel="prev" title="Configuration with Zope 3" href="README_3.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README_3b.html" title="Two Database Set Up"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README_3.html" title="Configuration with Zope 3"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+          <li><a href="README_3.html" accesskey="U">Configuration with Zope 3</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="shared-single-database-set-up">
+<h1 id="shared-single-database-set-up">Shared Single Database Set Up<a class="headerlink" href="#shared-single-database-set-up" title="Permalink to this headline">¶</a></h1>
+<p>As described above, using a shared single database will probably be the
+quickest way to get started.  Large-scale production usage will probably prefer
+to use the <a class="reference external" href="README_3b.html#two-database-set-up"><em>Two Database Set Up</em></a> described later.</p>
+<p>So, without further ado, here is the text of our zope.conf-alike, and of our
+site.zcml-alike <a class="footnote-reference" href="#get-vals" id="id1">[1]</a>.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zope_conf</span> <span class="o">=</span> <span class="s">&quot;&quot;&quot;</span>
+<span class="gp">... </span><span class="s">site-definition %(site_zcml_file)s</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;zodb main&gt;</span>
+<span class="gp">... </span><span class="s">  &lt;filestorage&gt;</span>
+<span class="gp">... </span><span class="s">    create true</span>
+<span class="gp">... </span><span class="s">    path %(main_storage_path)s</span>
+<span class="gp">... </span><span class="s">  &lt;/filestorage&gt;</span>
+<span class="gp">... </span><span class="s">&lt;/zodb&gt;</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;product-config zc.z3monitor&gt;</span>
+<span class="gp">... </span><span class="s">  port %(monitor_port)s</span>
+<span class="gp">... </span><span class="s">&lt;/product-config&gt;</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;logger&gt;</span>
+<span class="gp">... </span><span class="s">  level debug</span>
+<span class="gp">... </span><span class="s">  name zc.async</span>
+<span class="gp">... </span><span class="s">  propagate no</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">  &lt;logfile&gt;</span>
+<span class="gp">... </span><span class="s">    path %(async_event_log)s</span>
+<span class="gp">... </span><span class="s">  &lt;/logfile&gt;</span>
+<span class="gp">... </span><span class="s">&lt;/logger&gt;</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;logger&gt;</span>
+<span class="gp">... </span><span class="s">  level debug</span>
+<span class="gp">... </span><span class="s">  name zc.async.trace</span>
+<span class="gp">... </span><span class="s">  propagate no</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">  &lt;logfile&gt;</span>
+<span class="gp">... </span><span class="s">    path %(async_trace_log)s</span>
+<span class="gp">... </span><span class="s">  &lt;/logfile&gt;</span>
+<span class="gp">... </span><span class="s">&lt;/logger&gt;</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;eventlog&gt;</span>
+<span class="gp">... </span><span class="s">  &lt;logfile&gt;</span>
+<span class="gp">... </span><span class="s">    formatter zope.exceptions.log.Formatter</span>
+<span class="gp">... </span><span class="s">    path STDOUT</span>
+<span class="gp">... </span><span class="s">  &lt;/logfile&gt;</span>
+<span class="gp">... </span><span class="s">  &lt;logfile&gt;</span>
+<span class="gp">... </span><span class="s">    formatter zope.exceptions.log.Formatter</span>
+<span class="gp">... </span><span class="s">    path %(event_log)s</span>
+<span class="gp">... </span><span class="s">  &lt;/logfile&gt;</span>
+<span class="gp">... </span><span class="s">&lt;/eventlog&gt;</span>
+<span class="gp">... </span><span class="s">&quot;&quot;&quot;</span> <span class="o">%</span> <span class="p">{</span><span class="s">&#39;site_zcml_file&#39;</span><span class="p">:</span> <span class="n">site_zcml_file</span><span class="p">,</span>
+<span class="gp">... </span>       <span class="s">&#39;main_storage_path&#39;</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;main.fs&#39;</span><span class="p">),</span>
+<span class="gp">... </span>       <span class="s">&#39;async_storage_path&#39;</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;async.fs&#39;</span><span class="p">),</span>
+<span class="gp">... </span>       <span class="s">&#39;monitor_port&#39;</span><span class="p">:</span> <span class="n">monitor_port</span><span class="p">,</span>
+<span class="gp">... </span>       <span class="s">&#39;event_log&#39;</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;z3.log&#39;</span><span class="p">),</span>
+<span class="gp">... </span>       <span class="s">&#39;async_event_log&#39;</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;async.log&#39;</span><span class="p">),</span>
+<span class="gp">... </span>       <span class="s">&#39;async_trace_log&#39;</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;async_trace.log&#39;</span><span class="p">),}</span>
+<span class="gp">...</span>
+</pre></div>
+<p>In a non-trivial production system, you will also probably want to replace
+the file storage with a &lt;zeoclient&gt; stanza.</p>
+<p>Also note that an open monitor port should be behind a firewall, of course.</p>
+<p>We&#8217;ll assume that zdaemon.conf has been set up to put ZC_ASYNC_UUID in the
+proper place too.  It would have looked something like this in the
+zdaemon.conf:</p>
+<pre>&lt;environment&gt;
+  ZC_ASYNC_UUID /path/to/uuid.txt
+&lt;/environment&gt;</pre>
+<p>(Other tools, such as supervisor, also can work, of course; their spellings are
+different and are &#8220;left as an exercise to the reader&#8221; at the moment.)</p>
+<p>We&#8217;ll do that by hand:</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s">&#39;ZC_ASYNC_UUID&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;uuid.txt&#39;</span><span class="p">)</span>
+</pre></div>
+<p>Now let&#8217;s define our site-zcml-alike.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">site_zcml</span> <span class="o">=</span> <span class="s">&quot;&quot;&quot;</span>
+<span class="gp">... </span><span class="s">&lt;configure xmlns=&#39;http://namespaces.zope.org/zope&#39;</span>
+<span class="gp">... </span><span class="s">           xmlns:meta=&quot;http://namespaces.zope.org/meta&quot;</span>
+<span class="gp">... </span><span class="s">           &gt;</span>
+<span class="gp">... </span><span class="s">&lt;include package=&quot;zope.component&quot; file=&quot;meta.zcml&quot; /&gt;</span>
+<span class="gp">... </span><span class="s">&lt;include package=&quot;zope.component&quot; /&gt;</span>
+<span class="gp">... </span><span class="s">&lt;include package=&quot;zc.z3monitor&quot; /&gt;</span>
+<span class="gp">... </span><span class="s">&lt;include package=&quot;zc.async&quot; file=&quot;basic_dispatcher_policy.zcml&quot; /&gt;</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;!-- this is usually handled in Zope applications by the</span>
+<span class="gp">... </span><span class="s">     zope.app.keyreference.persistent.connectionOfPersistent adapter --&gt;</span>
+<span class="gp">... </span><span class="s">&lt;adapter factory=&quot;zc.twist.connection&quot; /&gt;</span>
+<span class="gp">... </span><span class="s">&lt;/configure&gt;</span>
+<span class="gp">... </span><span class="s">&quot;&quot;&quot;</span>
+</pre></div>
+<p>Now we&#8217;re done.</p>
+<p>If we process these files, and wait for a poll, we&#8217;ve got a working
+set up <a class="footnote-reference" href="#process" id="id2">[2]</a>.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.dispatcher</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">pprint</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">pprint</span><span class="o">.</span><span class="n">pprint</span><span class="p">(</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">,</span> <span class="mf">0</span><span class="p">))</span>
+<span class="go">{&#39;&#39;: {&#39;main&#39;: {&#39;active jobs&#39;: [],</span>
+<span class="go">               &#39;error&#39;: None,</span>
+<span class="go">               &#39;len&#39;: 0,</span>
+<span class="go">               &#39;new jobs&#39;: [],</span>
+<span class="go">               &#39;size&#39;: 3}}}</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">True</span>
+</pre></div>
+<p>We can ask for a job to be performed, and get the result.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">root</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IQueue</span><span class="p">(</span><span class="n">root</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">operator</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.job</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">operator</span><span class="o">.</span><span class="n">mul</span><span class="p">,</span> <span class="mf">21</span><span class="p">,</span> <span class="mf">2</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">wait_for_result</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">42</span>
+</pre></div>
+<p>We can connect to the monitor server with telnet.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">telnetlib</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">tn</span> <span class="o">=</span> <span class="n">telnetlib</span><span class="o">.</span><span class="n">Telnet</span><span class="p">(</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="n">monitor_port</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">tn</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">&#39;async status</span><span class="se">\n</span><span class="s">&#39;</span><span class="p">)</span> <span class="c"># immediately disconnects</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">tn</span><span class="o">.</span><span class="n">read_all</span><span class="p">()</span> <span class="c"># doctest: +ELLIPSIS +NORMALIZE_WHITESPACE</span>
+<span class="go">{</span>
+<span class="go">    &quot;poll interval&quot;: {</span>
+<span class="go">        &quot;seconds&quot;: ...</span>
+<span class="go">    },</span>
+<span class="go">    &quot;status&quot;: &quot;RUNNING&quot;,</span>
+<span class="go">    &quot;time since last poll&quot;: {</span>
+<span class="go">        &quot;seconds&quot;: ...</span>
+<span class="go">    },</span>
+<span class="go">    &quot;uptime&quot;: {</span>
+<span class="go">        &quot;seconds&quot;: ...</span>
+<span class="go">    },</span>
+<span class="go">    &quot;uuid&quot;: &quot;...&quot;</span>
+<span class="go">}</span>
+<span class="go">&lt;BLANKLINE&gt;</span>
+</pre></div>
+<p>Now we&#8217;ll &#8220;shut down&#8221; with a CTRL-C, or SIGINT, and clean up.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">signal</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">if</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">os</span><span class="p">,</span> <span class="s">&#39;getpid&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span> <span class="c"># UNIXEN, not Windows</span>
+<span class="gp">... </span>    <span class="n">pid</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getpid</span><span class="p">()</span>
+<span class="gp">... </span>    <span class="k">try</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="n">os</span><span class="o">.</span><span class="n">kill</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="n">signal</span><span class="o">.</span><span class="n">SIGINT</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="k">if</span> <span class="n">dispatcher</span><span class="o">.</span><span class="n">activated</span><span class="p">:</span>
+<span class="gp">... </span>            <span class="k">assert</span> <span class="bp">False</span><span class="p">,</span> <span class="s">&#39;dispatcher did not deactivate&#39;</span>
+<span class="gp">... </span>    <span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="k">print</span> <span class="s">&quot;failed to send SIGINT, or something&quot;</span>
+<span class="gp">... </span><span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>    <span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">callFromThread</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">stop</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">30</span><span class="p">):</span>
+<span class="gp">... </span>        <span class="k">if</span> <span class="ow">not</span> <span class="n">dispatcher</span><span class="o">.</span><span class="n">activated</span><span class="p">:</span>
+<span class="gp">... </span>            <span class="k">break</span>
+<span class="gp">... </span>        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="k">assert</span> <span class="bp">False</span><span class="p">,</span> <span class="s">&#39;dispatcher did not deactivate&#39;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span> <span class="c"># sync</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.component</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">uuid</span> <span class="o">=</span> <span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">getUtility</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IUUID</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">dispatchers</span><span class="p">[</span><span class="n">uuid</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">da</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">False</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">db</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">shutil</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">shutil</span><span class="o">.</span><span class="n">rmtree</span><span class="p">(</span><span class="nb">dir</span><span class="p">)</span>
+</pre></div>
+</blockquote>
+<p>These instructions are very similar to the <a class="reference external" href="README_3b.html#two-database-set-up"><em>Two Database Set Up</em></a>.</p>
+<p class="rubric">Footnotes</p>
+<table class="docutils footnote" frame="void" id="get-vals" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id1">[1]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">errno</span><span class="o">,</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">random</span><span class="o">,</span> <span class="nn">socket</span><span class="o">,</span> <span class="nn">tempfile</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">dir</span> <span class="o">=</span> <span class="n">tempfile</span><span class="o">.</span><span class="n">mkdtemp</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">site_zcml_file</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;site.zcml&#39;</span><span class="p">)</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">s</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">20</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">monitor_port</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mf">20000</span><span class="p">,</span> <span class="mf">49151</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="k">try</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="n">s</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="n">monitor_port</span><span class="p">))</span>
+<span class="gp">... </span>    <span class="k">except</span> <span class="n">socket</span><span class="o">.</span><span class="n">error</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">args</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span> <span class="o">==</span> <span class="n">errno</span><span class="o">.</span><span class="n">EADDRINUSE</span><span class="p">:</span>
+<span class="gp">... </span>            <span class="k">pass</span>
+<span class="gp">... </span>        <span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>            <span class="k">raise</span>
+<span class="gp">... </span>    <span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="n">s</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+<span class="gp">... </span>        <span class="k">break</span>
+<span class="gp">... </span><span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>    <span class="k">assert</span> <span class="bp">False</span><span class="p">,</span> <span class="s">&#39;could not find available port&#39;</span>
+<span class="gp">... </span>    <span class="n">monitor_port</span> <span class="o">=</span> <span class="bp">None</span>
+<span class="gp">...</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="process" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id2">[2]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zope_conf_file</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;zope.conf&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">zope_conf_file</span><span class="p">,</span> <span class="s">&#39;w&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">zope_conf</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">site_zcml_file</span><span class="p">,</span> <span class="s">&#39;w&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">site_zcml</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zdaemon.zdoptions</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.app.appsetup</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">options</span> <span class="o">=</span> <span class="n">zdaemon</span><span class="o">.</span><span class="n">zdoptions</span><span class="o">.</span><span class="n">ZDOptions</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">options</span><span class="o">.</span><span class="n">schemadir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">appsetup</span><span class="o">.</span><span class="n">__file__</span><span class="p">)),</span>
+<span class="gp">... </span>    <span class="s">&#39;schema&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">options</span><span class="o">.</span><span class="n">realize</span><span class="p">([</span><span class="s">&#39;-C&#39;</span><span class="p">,</span> <span class="n">zope_conf_file</span><span class="p">])</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">config</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">configroot</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.app.appsetup.product</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">appsetup</span><span class="o">.</span><span class="n">product</span><span class="o">.</span><span class="n">setProductConfigurations</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">config</span><span class="o">.</span><span class="n">product_config</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">ignore</span> <span class="o">=</span> <span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">appsetup</span><span class="o">.</span><span class="n">config</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">site_definition</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.app.appsetup.appsetup</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">db</span> <span class="o">=</span> <span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">appsetup</span><span class="o">.</span><span class="n">appsetup</span><span class="o">.</span><span class="n">multi_database</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">databases</span><span class="p">)[</span><span class="mf">0</span><span class="p">][</span><span class="mf">0</span><span class="p">]</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.event</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">notify</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">DatabaseOpened</span><span class="p">(</span><span class="n">db</span><span class="p">))</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">zc.async.testing</span> <span class="kn">import</span> <span class="n">get_poll</span><span class="p">,</span> <span class="n">wait_for_result</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="README_3.html" title="previous chapter">Configuration with Zope 3</a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="README_3b.html" title="next chapter">Two Database Set Up</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/README_3a.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="README_3b.html" title="Two Database Set Up"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README_3.html" title="Configuration with Zope 3"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+          <li><a href="README_3.html" accesskey="U">Configuration with Zope 3</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/README_3b.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/README_3b.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/README_3b.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,312 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Two Database Set Up &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="up" title="Configuration with Zope 3" href="README_3.html" />
+    <link rel="next" title="Tips and Tricks" href="tips.html" />
+    <link rel="prev" title="Shared Single Database Set Up" href="README_3a.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="tips.html" title="Tips and Tricks"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README_3a.html" title="Shared Single Database Set Up"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+          <li><a href="README_3.html" accesskey="U">Configuration with Zope 3</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="id1">
+<span id="two-database-set-up"></span><h1 id="id1"><span id="two-database-set-up"></span>Two Database Set Up<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h1>
+<p>Even though it is a bit more trouble to set up, large-scale production usage
+will probably prefer to use this approach, over the shared single database
+described above.</p>
+<p>For our zope.conf, we only need one additional stanza to the one seen above:</p>
+<pre>&lt;zodb async&gt;
+  &lt;filestorage&gt;
+    create true
+    path REPLACE_THIS_WITH_PATH_TO_STORAGE
+  &lt;/filestorage&gt;
+&lt;/zodb&gt;</pre>
+<p>(You would replace &#8220;REPLACE_THIS_WITH_PATH_TO_STORAGE&#8221; with the path to the
+storage file.)</p>
+<p>As before, you will probably prefer to use ZEO rather than FileStorage in
+production.</p>
+<p>The zdaemon.conf instructions are the same: set the ZC_ASYNC_UUID environment
+variable properly in the zdaemon.conf file.</p>
+<p>For our site.zcml, the only difference is that we use the
+multidb_dispatcher_policy.zcml file rather than the
+basic_dispatcher_policy.zcml file.</p>
+<p>If you want to change policy, change &#8220;multidb_dispatcher_policy.zcml&#8221; to
+&#8220;dispatcher.zcml&#8221; in the example above and register your replacement bits for
+the policy in &#8220;multidb_dispatcher_policy.zcml&#8221;.  You&#8217;ll see that most of that
+comes from code in subscribers.py, which can be adjusted easily.</p>
+<p>If we process the files described above, and wait for a poll, we&#8217;ve got a
+working set up <a class="footnote-reference" href="#process-multi" id="id2">[1]</a>.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.dispatcher</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">pprint</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">pprint</span><span class="o">.</span><span class="n">pprint</span><span class="p">(</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">,</span> <span class="mf">0</span><span class="p">))</span>
+<span class="go">{&#39;&#39;: {&#39;main&#39;: {&#39;active jobs&#39;: [],</span>
+<span class="go">               &#39;error&#39;: None,</span>
+<span class="go">               &#39;len&#39;: 0,</span>
+<span class="go">               &#39;new jobs&#39;: [],</span>
+<span class="go">               &#39;size&#39;: 3}}}</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">True</span>
+</pre></div>
+<p>As before, we can ask for a job to be performed, and get the result.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">root</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IQueue</span><span class="p">(</span><span class="n">root</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">operator</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.job</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">job</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">operator</span><span class="o">.</span><span class="n">mul</span><span class="p">,</span> <span class="mf">21</span><span class="p">,</span> <span class="mf">2</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">wait_for_result</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">42</span>
+</pre></div>
+<p>Hopefully zc.async will be an easy-to-configure, easy-to-use, and useful tool
+for you! Good luck! <a class="footnote-reference" href="#shutdown" id="id3">[2]</a></p>
+<p class="rubric">Footnotes</p>
+<table class="docutils footnote" frame="void" id="process-multi" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id2">[1]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">errno</span><span class="o">,</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">random</span><span class="o">,</span> <span class="nn">socket</span><span class="o">,</span> <span class="nn">tempfile</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">dir</span> <span class="o">=</span> <span class="n">tempfile</span><span class="o">.</span><span class="n">mkdtemp</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">site_zcml_file</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;site.zcml&#39;</span><span class="p">)</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">s</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">20</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">monitor_port</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mf">20000</span><span class="p">,</span> <span class="mf">49151</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="k">try</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="n">s</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="s">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="n">monitor_port</span><span class="p">))</span>
+<span class="gp">... </span>    <span class="k">except</span> <span class="n">socket</span><span class="o">.</span><span class="n">error</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">args</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span> <span class="o">==</span> <span class="n">errno</span><span class="o">.</span><span class="n">EADDRINUSE</span><span class="p">:</span>
+<span class="gp">... </span>            <span class="k">pass</span>
+<span class="gp">... </span>        <span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>            <span class="k">raise</span>
+<span class="gp">... </span>    <span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="n">s</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+<span class="gp">... </span>        <span class="k">break</span>
+<span class="gp">... </span><span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>    <span class="k">assert</span> <span class="bp">False</span><span class="p">,</span> <span class="s">&#39;could not find available port&#39;</span>
+<span class="gp">... </span>    <span class="n">monitor_port</span> <span class="o">=</span> <span class="bp">None</span>
+<span class="gp">...</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zope_conf</span> <span class="o">=</span> <span class="s">&quot;&quot;&quot;</span>
+<span class="gp">... </span><span class="s">site-definition %(site_zcml_file)s</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;zodb main&gt;</span>
+<span class="gp">... </span><span class="s">  &lt;filestorage&gt;</span>
+<span class="gp">... </span><span class="s">    create true</span>
+<span class="gp">... </span><span class="s">    path %(main_storage_path)s</span>
+<span class="gp">... </span><span class="s">  &lt;/filestorage&gt;</span>
+<span class="gp">... </span><span class="s">&lt;/zodb&gt;</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;zodb async&gt;</span>
+<span class="gp">... </span><span class="s">  &lt;filestorage&gt;</span>
+<span class="gp">... </span><span class="s">    create true</span>
+<span class="gp">... </span><span class="s">    path %(async_storage_path)s</span>
+<span class="gp">... </span><span class="s">  &lt;/filestorage&gt;</span>
+<span class="gp">... </span><span class="s">&lt;/zodb&gt;</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;product-config zc.z3monitor&gt;</span>
+<span class="gp">... </span><span class="s">  port %(monitor_port)s</span>
+<span class="gp">... </span><span class="s">&lt;/product-config&gt;</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;logger&gt;</span>
+<span class="gp">... </span><span class="s">  level debug</span>
+<span class="gp">... </span><span class="s">  name zc.async</span>
+<span class="gp">... </span><span class="s">  propagate no</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">  &lt;logfile&gt;</span>
+<span class="gp">... </span><span class="s">    path %(async_event_log)s</span>
+<span class="gp">... </span><span class="s">  &lt;/logfile&gt;</span>
+<span class="gp">... </span><span class="s">&lt;/logger&gt;</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;logger&gt;</span>
+<span class="gp">... </span><span class="s">  level debug</span>
+<span class="gp">... </span><span class="s">  name zc.async.trace</span>
+<span class="gp">... </span><span class="s">  propagate no</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">  &lt;logfile&gt;</span>
+<span class="gp">... </span><span class="s">    path %(async_trace_log)s</span>
+<span class="gp">... </span><span class="s">  &lt;/logfile&gt;</span>
+<span class="gp">... </span><span class="s">&lt;/logger&gt;</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;eventlog&gt;</span>
+<span class="gp">... </span><span class="s">  &lt;logfile&gt;</span>
+<span class="gp">... </span><span class="s">    formatter zope.exceptions.log.Formatter</span>
+<span class="gp">... </span><span class="s">    path STDOUT</span>
+<span class="gp">... </span><span class="s">  &lt;/logfile&gt;</span>
+<span class="gp">... </span><span class="s">  &lt;logfile&gt;</span>
+<span class="gp">... </span><span class="s">    formatter zope.exceptions.log.Formatter</span>
+<span class="gp">... </span><span class="s">    path %(event_log)s</span>
+<span class="gp">... </span><span class="s">  &lt;/logfile&gt;</span>
+<span class="gp">... </span><span class="s">&lt;/eventlog&gt;</span>
+<span class="gp">... </span><span class="s">&quot;&quot;&quot;</span> <span class="o">%</span> <span class="p">{</span><span class="s">&#39;site_zcml_file&#39;</span><span class="p">:</span> <span class="n">site_zcml_file</span><span class="p">,</span>
+<span class="gp">... </span>       <span class="s">&#39;main_storage_path&#39;</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;main.fs&#39;</span><span class="p">),</span>
+<span class="gp">... </span>       <span class="s">&#39;async_storage_path&#39;</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;async.fs&#39;</span><span class="p">),</span>
+<span class="gp">... </span>       <span class="s">&#39;monitor_port&#39;</span><span class="p">:</span> <span class="n">monitor_port</span><span class="p">,</span>
+<span class="gp">... </span>       <span class="s">&#39;event_log&#39;</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;z3.log&#39;</span><span class="p">),</span>
+<span class="gp">... </span>       <span class="s">&#39;async_event_log&#39;</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;async.log&#39;</span><span class="p">),</span>
+<span class="gp">... </span>       <span class="s">&#39;async_trace_log&#39;</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;async_trace.log&#39;</span><span class="p">),}</span>
+<span class="gp">...</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s">&#39;ZC_ASYNC_UUID&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;uuid.txt&#39;</span><span class="p">)</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">site_zcml</span> <span class="o">=</span> <span class="s">&quot;&quot;&quot;</span>
+<span class="gp">... </span><span class="s">&lt;configure xmlns=&#39;http://namespaces.zope.org/zope&#39;</span>
+<span class="gp">... </span><span class="s">           xmlns:meta=&quot;http://namespaces.zope.org/meta&quot;</span>
+<span class="gp">... </span><span class="s">           &gt;</span>
+<span class="gp">... </span><span class="s">&lt;include package=&quot;zope.component&quot; file=&quot;meta.zcml&quot; /&gt;</span>
+<span class="gp">... </span><span class="s">&lt;include package=&quot;zope.component&quot; /&gt;</span>
+<span class="gp">... </span><span class="s">&lt;include package=&quot;zc.z3monitor&quot; /&gt;</span>
+<span class="gp">... </span><span class="s">&lt;include package=&quot;zc.async&quot; file=&quot;multidb_dispatcher_policy.zcml&quot; /&gt;</span>
+<span class="gp">...</span><span class="s"></span>
+<span class="gp">... </span><span class="s">&lt;!-- this is usually handled in Zope applications by the</span>
+<span class="gp">... </span><span class="s">     zope.app.keyreference.persistent.connectionOfPersistent adapter --&gt;</span>
+<span class="gp">... </span><span class="s">&lt;adapter factory=&quot;zc.twist.connection&quot; /&gt;</span>
+<span class="gp">... </span><span class="s">&lt;/configure&gt;</span>
+<span class="gp">... </span><span class="s">&quot;&quot;&quot;</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zope_conf_file</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s">&#39;zope.conf&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">zope_conf_file</span><span class="p">,</span> <span class="s">&#39;w&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">zope_conf</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">site_zcml_file</span><span class="p">,</span> <span class="s">&#39;w&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">site_zcml</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zdaemon.zdoptions</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.app.appsetup</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">options</span> <span class="o">=</span> <span class="n">zdaemon</span><span class="o">.</span><span class="n">zdoptions</span><span class="o">.</span><span class="n">ZDOptions</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">options</span><span class="o">.</span><span class="n">schemadir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">abspath</span><span class="p">(</span><span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">appsetup</span><span class="o">.</span><span class="n">__file__</span><span class="p">)),</span>
+<span class="gp">... </span>    <span class="s">&#39;schema&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">options</span><span class="o">.</span><span class="n">realize</span><span class="p">([</span><span class="s">&#39;-C&#39;</span><span class="p">,</span> <span class="n">zope_conf_file</span><span class="p">])</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">config</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">configroot</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.app.appsetup.product</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">appsetup</span><span class="o">.</span><span class="n">product</span><span class="o">.</span><span class="n">setProductConfigurations</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">config</span><span class="o">.</span><span class="n">product_config</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">ignore</span> <span class="o">=</span> <span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">appsetup</span><span class="o">.</span><span class="n">config</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">site_definition</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.app.appsetup.appsetup</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">db</span> <span class="o">=</span> <span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">appsetup</span><span class="o">.</span><span class="n">appsetup</span><span class="o">.</span><span class="n">multi_database</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">databases</span><span class="p">)[</span><span class="mf">0</span><span class="p">][</span><span class="mf">0</span><span class="p">]</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.event</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">notify</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">DatabaseOpened</span><span class="p">(</span><span class="n">db</span><span class="p">))</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">zc.async.testing</span> <span class="kn">import</span> <span class="n">get_poll</span><span class="p">,</span> <span class="n">wait_for_result</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="shutdown" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id3">[2]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.dispatcher</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">callFromThread</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">stop</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">thread</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="mf">3</span><span class="p">)</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">db</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">db</span><span class="o">.</span><span class="n">databases</span><span class="p">[</span><span class="s">&#39;async&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">shutil</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">shutil</span><span class="o">.</span><span class="n">rmtree</span><span class="p">(</span><span class="nb">dir</span><span class="p">)</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="README_3a.html" title="previous chapter">Shared Single Database Set Up</a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="tips.html" title="next chapter">Tips and Tricks</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/README_3b.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="tips.html" title="Tips and Tricks"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README_3a.html" title="Shared Single Database Set Up"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+          <li><a href="README_3.html" accesskey="U">Configuration with Zope 3</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/_sources/CHANGES.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/CHANGES.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/CHANGES.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,355 @@
+=======
+Changes
+=======
+
+1.5.0 (2008-09-21)
+==================
+
+- Documentation improvements.  Converted documentation into Sphinx system.
+
+- Made "other" commit errors for the ``RetryCommonForever`` retry policy have
+  an incremental backoff.  By default, this starts at 0 seconds, and increments
+  by a second to a maximum of 60 seconds.
+
+- Work around a memory leak in zope.i18nmessageid
+  (https://bugs.launchpad.net/zope3/+bug/257657).  The change should be
+  backward-compatible.  It also will produce slightly smaller pickles for Jobs,
+  but that was really not a particular goal.
+
+- Added zc.async.partial.Partial for backward compatibility purposes.
+
+- Fix support for Twisted installed reactor.
+
+- Fix retry behavior for parallel and serial jobs
+
+- Tweaked the uuid.txt to mention zdaemon/supervisor rather than Zope 3.
+
+- Fixed some bugs in egg creation.
+
+- Changed quotas to not use a container that has conflict resolution, since
+  these values should be a strict maximum.
+
+- We only want to claim a job if we are activated.   Make the agent check the
+  ``activated`` and ``dead`` attributes of the parent dispatcher before
+  claiming.
+
+- When activating, also clean out jobs from the dispatcher's agents, just as
+  with deactivating.  This should protect from unusual race conditions in
+  which the dispatcher got a job after being deactivated.
+
+- Change dispatcher to ping before claiming jobs.
+
+- when a ping reactivates a dispatcher, use new method ``reactivate`` rather
+  than ``activate``.  This fires a new ``DispatcherReactivated`` event.
+
+- It's still theoretically possible (for instance, with a
+  badly-behaved long commit that causes a sibling to believe that the
+  process is dead) that an async worker process would be working on a
+  job that it shouldn't be.  For instance, the job has been taken away,
+  and is another process' responsibility now.  Now, whenever a
+  process is about to start any work (especially a retry), it should
+  double-check that the job is registered as being performed by itself.
+  If not, the process should abort the transaction, make an error
+  log, and give up on the job.  Write conflict errors on the job should
+  protect us from the edge cases in this story.
+
+- The dispatcher's ``getActiveJobs`` method now actually tells you information
+  about what's going on in the threads at this instant, rather than what's
+  going on in the database.  The poll's ``active jobs`` keys continues to
+  report what was true *in the database* as of *the last poll*.  This change
+  also affects the ``async jobs`` monitor command.
+
+- The dispatcher method ``getJobInfo`` (and the monitor command ``async job``)
+  now returns the name of the queue for the job, the name of the agent for the
+  job, and whether the job has been, or was reassigned.
+
+- zc.async events inherit from 'zc.component.interfaces.IObjectEvent' instead
+  of a zc.async specific IObjectEvent (thanks to Satchit Haridas).
+
+- Added new monitoring and introspection tools: the ``asyncdb`` zc.monitor
+  command (and, for Python, the code in monitordb.py).  This code provides
+  easy spellings to examine the database's view of what is happening in
+  zc.async.  Because it is the database, it also has a much longer historical
+  view than the ``async`` tools.  The best way to learn about these tools is
+  to read the extensive documentation provided within zc.monitor by
+  using ``asyncdb help`` and ``asyncdb help <TOOL NAME>``.
+
+- Added new preferred way of filtering agent choices: the new ``filter``
+  attribute.  Using filters, rather than "choosers," allows several ``asyncdb``
+  tools to filter pending jobs based on what an agent is willing to do.  It
+  also is a smaller contract, and so a filter requires less code than a chooser
+  in the common case.  On the other hand, using a filter alone doesn't allow
+  the agent to try to *prefer* certain tasks.
+
+- Deprecated agent.chooseFirst.  It is no longer necesary, since an agent
+  without a chooser and with a filter of None has the same behavior.  It is
+  retained for legacy databases.
+
+- Moved deprecated legacy code to new ``legacy`` module.
+
+- Tried to be significantly reduce the chance of spurious timing errors in the
+  tests, at the expense of causing the tests to take longer to run.
+
+- monitoring support depends on the new zc.monitor package, which is not Zope
+  specific.  This means non-Zope 3 apps can take advantage of the monitoring
+  support.  To use, use the [monitor] target; this only adds simplejson,
+  zc.ngi, and zc.monitor to the basic dependencies.
+
+- Make ftesting try to join worker threads, in addition to polling thread,
+  to try to eliminate intermittent test-runner warnings in ftests that a
+  thread is left behind.  If the threads do not end, inform the user what jobs
+  are not letting go.  (thanks to Patrick Strawderman)
+
+1.4.1 (2008-07-30)
+==================
+
+- The new ``serial`` and ``parallel`` helpers did not allow the
+  ``postprocess`` argument to be a partial closure, and were being naughty.
+  Fixed.
+
+- Added tests and demos for advanced features of ``serial`` and ``parallel``.
+
+- More tweaks to the new Quickstart S5 document.
+
+1.4.0 (2008-07-30)
+==================
+
+- Mentioned in ftesting.txt that Zope 3 users should uses zope.app.testing
+  3.4.2 or newer.  Also added a summary section at the beginning of that file.
+
+- Added logging of critical messages to __stdout__ for ``ftesting.setUp``.
+  This can help discovering problems in callback transactions.  This uses a new
+  helper function , ``print_logs``, in zc.async.testing, which is primarily
+  intended to be used for quick and dirty debugging
+
+- Changed testing.wait_for_result and testing.wait_for_annotation to ignore
+  ReadConflictErrors, so they can be used more reliably in tests that use
+  MappingStorage, and other storages without MVCC.
+
+- Support <type 'builtin_function_or_method'> for adaptation to Job.
+
+- Add warning about long commits to tips and tricks.
+
+- After complaining about a polling dispatcher that is deactivated not really
+  being dead in the logs, reactivate.
+
+- No longer use intermediate job to implement the success/failure addCallbacks
+  behavior.  Introduce an ICallbackProxy that can be used for this kind of
+  behavior instead.  This change was driven by two desires.
+
+  - Don't log the intermediate result.  It makes logs harder to read with
+    unnecessary duplications of pertinent data hidden within unimportant
+    differences in the log entries.
+
+  - Don't unnecessarily remember errors in success/failure callbacks.  This can
+    cause unnecessary failures in unusual situations.
+
+  The callback proxy accepts callbacks, which are added to the selected job
+  (success or failure) when the job is selected.
+
+  This change introduces some hopefully trivial incompatibilities, which
+  basically come down to the callback being a proxy, not a real job. Use the
+  convenience properties ``success`` and ``failure`` on the proxy to look at
+  the respective jobs. After the proxy is evaluated, the ``job`` attribute
+  will hold the job that was actually run. ``status`` and ``result`` are
+  conveniences to get the status and result of the selected job.
+
+- Add ``parallel`` and ``serial`` convenience functions to zc.async.job to make
+  it trivial to schedule and process decomposed jobs.
+
+- Add ``start`` convenience function to zc.async.configure to make it trivial
+  to start up a common-case configuration of a zc.async dispatcher.
+
+- No longer use protected attributes of callbacks in ``resumeCallbacks``.
+
+- The "local" code is now moved out from the dispatcher module to
+  threadlocal.  This is to recognize that the local code is now modified
+  outside of the dispatcher module, as described in the next bullet.
+
+- Jobs, when called, are responsible for setting the "local" job value.  This
+  means that zc.async.local.getJob() always returns the currently running job,
+  whether it is a top-level job (as before) or a callback (now).
+
+- Start on S5 QuickStart presentation (see QUICKSTART_1_VIRTUALENV.txt in
+  package).
+
+1.3 (2008-07-04)
+================
+
+- added "Tips and Tricks" and incorporated into the PyPI page.
+
+- added ``setUp`` and ``tearDown`` hooks to Job class so that code can run
+  before and after the main job's code.  The output of ``setUp`` is passed as
+  an argument to ``tearDown`` so that one can pass state to the other, if
+  needed. ``setUp`` is run immediately before the actual job call.
+  ``tearDown`` runs after the transaction is committed, or after it was aborted
+  if there was a failure.   A retry requested by a retry policy causes the
+  methods to be run again.  A failure in ``setUp`` is considered to be a
+  failure in the job, as far as the retryPolicy is concerned (i.e., the job
+  calls the retry policy's ``jobError`` method).  If ``setUp`` fails, the job
+  is not called, bit ``tearDown`` is.  ``tearDown`` will fail with a critical
+  log message, but then processing will continue.
+
+- using the new ``setUp`` and ``tearDown`` hooks, added a Zope 3-specific Job
+  subclass (see zc.async.z3.Job) that remembers the zope.app.component site and
+  interaction participants when instantiated. These can be mutated. Then, when
+  the job is run, the ``setUp`` sets up the site and a security interaction
+  with the old participants, and then the ``tearDown`` tears it all down after
+  the transaction has committed.
+
+- changed retry policy logs to "WARNING" level, from "INFO" level.
+
+- changed many dispatcher errors to "CRITICAL" level from "ERROR" level.
+
+- added "CRITICAL" level logs for "other" commit retries on the
+  RetryCommonForever retry policy.
+
+- added ``remove`` method on queue.
+
+- added helpers for setting up and tearing down Zope 3 functional tests
+  (ftesting.py), and a discussion of how to write Zope 3 functional tests with
+  layers (zope.app.testing.functional) in ftesting.txt.
+
+- remove obsolete retry approach for success/failure callbacks
+  (``completeStartedJobArguments``): it is now handled by retry policies.
+
+- remove odd full-path self-references within the utils module.
+
+- renamed ``zc.async.utils.try_transaction_five_times`` to
+  ``zc.async.utils.try_five_times``.
+
+- doc improvements and fixes (thanks to Zvezdan Petkovic and Gintautas
+  Miliauskas).
+
+- the ``z3`` "extra" distutils target now explicitly depends on zope.security,
+  zope.app.security, and zope.app.component.  This almost certainly does not
+  increase the practical dependencies of the ``z3`` extras, but it does reflect
+  new direct dependencies of the z3-specific modules in the package.
+
+1.2 (2008-06-20)
+================
+
+- made the log for finding an activated agent report the pertinent queue's oid
+  as an unpacked integer, rather than the packed string blob. Use
+  ``ZODB.utils.p64`` to convert back to an oid that the ZODB will recognize.
+
+- Bugfix: in failing a job, the job thought it was in its old agent, and the
+  ``fail`` call failed. This is now tested by the first example in new doctest
+  ``catastrophes.txt``.
+
+- jobs no longer default to a ``begin_by`` value of one hour after the
+  ``begin_after``.  The default now is no limit.
+
+- Made dispatcher much more robust to transaction errors and ZEO
+  ClientDisconnected errors.
+
+- Jobs now use an IRetryPolicy to decide what to do on failure within a job,
+  within the commit of the result, and if the job is interrupted.  This allows
+  support of transactional jobs, transactional jobs that critically must be
+  run to completion, and non-transactional jobs such as communicating with an
+  external service.
+
+- The default retry policy supports retries for ClientDisconnected errors,
+  transaction errors, and interruptions.
+
+- ``job.txt`` has been expanded significantly to show error handling and the
+  use of retry policies. New file ``catastrophes.txt`` shows handling of other
+  catastrophes, such as interruptions to polling.
+
+- job errors now go in the main zc.async.event log rather than in the
+  zc.async.trace log.  Successes continue to go in the trace log.
+
+- callback failures go to the main log as a CRITICAL error, by default.
+
+- ``handleInterrupt`` is the new protocol on jobs to inform them that they were
+  active in a dispatcher that is now dead. They either fail or reschedule,
+  depending on the associated IRetryPolicy for the job. If they reschedule,
+  this should either be a datetime or timedelta. The job calls the agent's
+  ``reschedule`` method. If the timedelta is empty or negative, or the datetime
+  is earlier than now, the job is put back in the queue with a new ``putBack``
+  method on the queue. This is intended to be the opposite of ``claim``. Jobs
+  put in the queue with ``putBack`` will be pulled out before any others.
+
+- convert to using zope.minmax rather than locally defined ``Atom``.
+
+- Fix (and simplify) last_ping code so as to reduce unnecessarily writing the
+  state of the parent DispatcherAgents collection to the database whenever the
+  atom changed.
+
+- Depends on new release of zc.twist (1.3)
+
+- Switched dispatcher's in-memory storage of job and poll information to be per
+  job or per poll, respectively, rather than per time period, so as to try and
+  make memory usage more predictable (for instance, whether a dispatcher is
+  whipping through lots of jobs quickly, or doing work more slowly).
+
+1.1.1 (2008-05-14)
+==================
+
+- more README tweaks.
+
+- converted all reports from the dispatcher, including the monitor output,
+  to use "unpacked" integer oids.  This addresses a problem that simplejson
+  was having in trying to interpret the packed string blobs as unicode, and
+  then making zc.ngi fall over.  To get the object, then, you'll need to
+  use ``ZODB.utils.p64``, like this:
+  ``connection.get(ZODB.utils.p64(INTEGER_OID))``, where ``INTEGER_OID``
+  indicates the integer oid of the object you want to examine.
+
+- added several more tests for the monitor code.
+
+- made the ``async jobs`` monitor command be "up to the minute".  Before, it
+  included all of the new and active jobs from the previous poll; now, it
+  also filters out those that have since completed.
+
+- The ``async job`` command was broken, as revealed by a new monitor test.
+  Fixed, which also means we need a new version of zope.bforest (1.2) for a new
+  feature there.
+
+1.1 (2008-04-24)
+================
+
+- Fired events when the IQueues and IQueue objects are installed by the
+  QueueInstaller (thanks to Fred Drake).
+
+- Dispatchers make agent threads keep their connections, so each connection's
+  object cache use is optimized if the agent regularly requests jobs with
+  the same objects.
+
+- README improved (thanks to Benji York and Sebastian Ware).
+
+- Callbacks are logged at start in the trace log.
+
+- All job results (including callbacks) are logged, including verbose
+  tracebacks if the callback generated a failure.
+
+- Had the ThreadedDispatcherInstaller subscriber stash the thread on the
+  dispatcher, so you can shut down tests like this:
+
+  >>> import zc.async.dispatcher
+  >>> dispatcher = zc.async.dispatcher.get()
+  >>> dispatcher.reactor.callFromThread(dispatcher.reactor.stop)
+  >>> dispatcher.thread.join(3)
+
+- Added ``getQueue`` to zc.async.local as a convenience (it does what you
+  could already do: ``zc.async.local.getJob().queue``).
+
+- Clarified that ``IQueue.pull`` is the approved way of removing scheduled jobs
+  from a queue in interfaces and README.
+
+- reports in the logs of a job's success or failure come before callbacks are
+  started.
+
+- Added a section showing how the basic_dispatcher_policy.zcml worked, which
+  then pushed the former README_3 examples into README_3b.
+
+- Put ZPL everywhere I was supposed to.
+
+- Moved a number of helpful testing functions out of footnotes and into
+  zc.async.testing, both so that zc.async tests don't have to redefine them
+  and client packages can reuse them.
+
+1.0 (2008-04-09)
+================
+
+Initial release.


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/QUICKSTART_1_VIRTUALENV.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/QUICKSTART_1_VIRTUALENV.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/QUICKSTART_1_VIRTUALENV.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,1024 @@
+.. _quickstart-with-virtualenv:
+
+==============================
+Quickstart with ``virtualenv``
+==============================
+
+Prerequisites
+=============
+
+------------
+Installation
+------------
+
+To start, install |virtualenv|_ and create a virtual environment for our
+experiments.
+
+.. sidebar:: ``virtualenv`` and ``zc.buildout``
+
+   I prefer |zc.buildout|_ for production deployments, but |virtualenv|_ is
+   very nice for quick experimentation.  The other quick-start uses
+   |zc.buildout|_.
+
+::
+
+    $ easy_install virtualenv
+    $ virtualenv quickstart
+
+Install |async|_ in the virtual environment.
+
+::
+
+    $ cd quickstart/
+    $ ./bin/easy_install zc.async
+
+.. |zc.buildout| replace:: ``zc.buildout``
+
+.. _`zc.buildout`: http://pypi.python.org/pypi/zc.buildout
+
+.. |virtualenv| replace:: ``virtualenv``
+
+.. _virtualenv: http://pypi.python.org/pypi/virtualenv
+
+.. |async| replace:: ``zc.async``
+
+.. _`async`: http://pypi.python.org/pypi/zc.async
+
+------------
+Dependencies
+------------
+
+.. sidebar:: Example Dependencies
+
+   Here's an example listing of the site-packages brought in by a run of this
+   quick-start.
+   
+   ::
+   
+       $ ls lib/python2.5/site-packages/
+       Twisted-8.1.0-py2.5-macosx-10.5-i386.egg
+       ZConfig-2.5.1-py2.5.egg
+       ZODB3-3.8.1b5-py2.5-macosx-10.5-i386.egg
+       easy-install.pth
+       pytz-2008c-py2.5.egg
+       rwproperty-1.0-py2.5.egg
+       setuptools-0.6c8-py2.5.egg
+       setuptools.pth
+       uuid-1.30-py2.5.egg
+       zc.async-1.4.0-py2.5.egg
+       zc.dict-1.2.1-py2.5.egg
+       zc.queue-1.1-py2.5.egg
+       zc.twist-1.3-py2.5-macosx-10.5-i386.egg
+       zdaemon-2.0.2-py2.5.egg
+       zope.bforest-1.2-py2.5.egg
+       zope.component-3.4.0-py2.5.egg
+       zope.deferredimport-3.4.0-py2.5.egg
+       zope.deprecation-3.4.0-py2.5.egg
+       zope.event-3.4.0-py2.5.egg
+       zope.i18nmessageid-3.4.3-py2.5-macosx-10.5-i386.egg
+       zope.interface-3.4.1-py2.5-macosx-10.5-i386.egg
+       zope.minmax-1.1.0-py2.5.egg
+       zope.proxy-3.4.1-py2.5-macosx-10.5-i386.egg
+       zope.testing-3.6.0-py2.5.egg
+
+This installed several packages.
+
+- the ZODB_, an object database from the Zope project;
+
+- Twisted_, a framework for networked applications;
+
+- the component architecture from the Zope project;
+
+- and a few smaller packages.
+
+All of these, and zc.async, are distributed under BSD-like licenses such as
+LGPL and ZPL_.
+
+.. _ZODB: http://pypi.python.org/pypi/ZODB3
+
+.. _Twisted: http://pypi.python.org/pypi/Twisted
+
+.. _ZPL: http://en.wikipedia.org/wiki/Zope_Public_License
+
+----------
+ZEO Server
+----------
+
+zc.async relies on a distributed ZODB technology called ZEO ("Zope Enterprise
+Objects") to distribute work. ZEO has a central database server to which client
+processes connect.
+
+Let's start the ZEO Server::
+
+    $ ./bin/runzeo -a 9999 -f test.fs &
+
+That starts a database server, accessible on port 9999 of your local machine,
+saving the data in the test.fs file.
+
+
+Starting |async|
+================
+
+--------
+A Client
+--------
+
+Now let's start a Python with a client connection to the database server.
+
+Start up ``bin/python`` (not your system python, but the one in virtualenv's
+``quickstart/bin``)::
+
+    $ ./bin/python
+
+This will be our single client process.
+
+You might have many, each connecting to the main database server, and each able
+to perform and/or request |async| jobs.
+
+-------------------
+Database Connection
+-------------------
+
+Connect to the database.
+
+::
+
+    import ZEO.ClientStorage
+    import ZODB
+    storage = ZEO.ClientStorage.ClientStorage(
+        ('127.0.0.1', 9999))
+    db = ZODB.DB(storage)
+
+.. When run as a doctest, this uses a simple FileStorage, rather than a
+   ClientStorage.
+
+    >>> import ZODB.FileStorage
+    >>> storage = ZODB.FileStorage.FileStorage(
+    ...     'zc_async.fs', create=True)
+    >>> from ZODB.DB import DB
+    >>> db = DB(storage)
+
+-------------
+Start |async|
+-------------
+
+Basics
+------
+
+Now we do some basic configuration.  This first bit installs some default
+adapters.  You might not ever have to worry too much about them.
+
+    >>> import zc.async.configure
+    >>> zc.async.configure.base()
+
+Policy
+------
+
+This second part is policy, and if you ever put zc.async in production, you'll
+want to understand what's going on here.
+
+    >>> zc.async.configure.start(db, poll_interval=1)
+
+Now the system has a ``dispatcher`` polling for jobs every second.  As we'll
+see below, a ``dispatcher`` polls for jobs in one or more queues, to assign
+them to worker threads.
+
+Using |async|
+=============
+
+---------
+The Queue
+---------
+
+The ``start`` function also installed a queue.  To get zc.async to do work, you
+put a job in a queue, and commit the transaction.
+
+First, let's get the queue that we have installed.  We need to open a
+connection to the database.  Then we get the queue.
+
+    >>> conn = db.open()
+    >>> import zc.async.interfaces
+    >>> q = zc.async.interfaces.IQueue(conn)
+
+-----
+A Job
+-----
+
+.. sidebar:: A Silly Example
+
+    This is a silly example. Imagine instead that this was some really
+    long-running job. Maybe you have lots of these jobs coming in, and you need
+    to have many machines to claim jobs and perform them, so that you can
+    scale. Maybe this job divides itself up into parallel or serial jobs, and
+    this parent job isn't done until all the children jobs run to completion.
+
+    Or maybe this is a silly example.
+..
+
+Let's put a job in our queue.  This silly example will return the current time.
+
+    >>> import time
+    >>> j = q.put(time.time)
+
+It's not done yet.
+
+    >>> j.result
+    >>> j.status
+    u'pending-status'
+
+-------------
+A Transaction
+-------------
+
+We have to commit the transaction for the dispatcher to see the job.
+
+    >>> import transaction
+    >>> transaction.commit()
+
+--------
+A Result
+--------
+
+Now wait a second and then try this.  "transaction.begin" will sync up our
+database with database changes made elsewhere.
+
+.. This lets us "wait a second".
+
+    >>> import zc.async.testing
+    >>> res = zc.async.testing.wait_for_result(j)
+
+..
+
+    >>> _ = transaction.begin()
+    >>> j.result
+    1216179006.856108
+    >>> j.status
+    u'completed-status'
+
+--------------------------
+Another Job (And Closures)
+--------------------------
+
+You can also make closures.  The Job class accepts arguments similarly to
+the Python 2.5 :func:`functools.partial`: ``Job(func, \*args,
+\*\*keywords)``. This instantiates a new callable (a Job instance) with
+partial application of the given arguments and keywords.  You can then
+pass the job instance to the
+:meth:`~zc.async.interfaces.IQueue.put` method.
+
+Generating RSA keys is actually a reasonable real-world use case for
+something like this.
+
+::
+
+    import subprocess
+    j = q.put(zc.async.job.Job(
+        subprocess.call,
+        ['openssl', 'genrsa', '-out',
+         'key.pem', '1024']))
+    transaction.commit()
+
+We need to begin the transaction to see the result...
+
+::
+
+    j.result
+    _ = transaction.begin()
+    j.result
+
+...which in this case is simply ``0``, indicating a successful UNIX
+process.
+
+::
+
+    0
+
+We can open the file to show the result.
+
+::
+
+    subprocess.call(['cat', 'key.pem'])
+
+This will show you the key, which should look something like this::
+
+    -----BEGIN RSA PRIVATE KEY-----
+    MIICXgIBAAKBgQCYAZW+HjDGJhRHnUlZZWqhrGOxU2K/RhssmcMs0JLnWI2cWmZ+
+    ...
+    CEcz6ZbO8zm4AEGI/dqLicZh3bhunhflAovW6WxbNKLENQ==
+    -----END RSA PRIVATE KEY-----
+    0
+
+Running Your Own Code
+=====================
+
+------------------------
+A Monte Carlo Simulation
+------------------------
+
+.. sidebar:: Another Silly Example
+
+   Monte Carlo simulations are generally better suited to other tasks in
+   real-world use; pi is typically calculated by `far more sophisticated
+   means`_.
+
+We've now seen some simple examples from the standard library.  But how do you
+get your own work done?
+
+Let's say we want to implement an old chestnut of a problem: use a `Monte
+Carlo simulation`_ (that is, "throwing darts and analyzing the results") to
+`calculate pi`_.  This will use |async| much like the map-reduce approach
+of `Hadoop`_: we will want to distribute the work of running the simulation to
+multiple machines so the result can be done faster.
+
+.. _`Monte Carlo simulation`: http://en.wikipedia.org/wiki/Monte_Carlo_method
+
+.. _`Hadoop`: http://hadoop.apache.org/core/
+
+.. _`calculate pi`: http://math.fullerton.edu/mathews/n2003/MonteCarloPiMod.html
+
+.. _`far more sophisticated means`: http://en.wikipedia.org/wiki/Computing_Ï€
+
+---------------------------------
+Picklable Callables and Arguments
+---------------------------------
+
+You want a job to have a reference to your own callable, so the job will get
+the work you define performed.
+
+This reference, of the job to your callable, will need to be persisted in the
+database.
+
+.. sidebar:: ZODB Persistence Rules
+   
+   We don't need these now.  But if you are really curious about ZODB
+   persistence rules, here's a start.
+   
+   - Anything pickleable can be persisted.  Module global functions can be
+     pickled (by name), for instance, and will come in handy for our examples.
+   
+   - Custom classes should typically inherit from persistent.Persistent.
+     Instances of persistent.Persistent subclasses are each stored as a single
+     record in the database, and references to them are handled efficiently.
+   
+   - Subclasses of persistent.Persistent produce instances that recognize when
+     their *direct* attributes have been written, and inform the database that
+     they are dirty, for the next committed transaction.  Standard,
+     non-persistent-aware mutables such as lists, sets and dicts do *not* know
+     when they have been changed, and are best avoided, or at least not
+     mutated. A variety of persistent-aware replacements for these types are
+     available.
+   
+   - Use the transaction module to commit and abort transactions in the ZODB.
+   
+   More advanced concerns include the following.
+   
+   - An optimistic implementation of MVCC_ in the ZODB means that you should be
+     careful reading (not writing) one persistent.Persistent instance (record)
+     and writing another persistent.Persistent instance with a value that
+     depends on the first, read value within a transaction.
+   
+   - Concurrent transactions can produce write conflict errors.  Transactions
+     are often automatically retried in |async| and other ZODB-based systems if
+     conflict errors are encountered.  If this is not desired for a given
+     |async| job, it should be given a ``zc.async.job.NeverRetry`` retry
+     policy, as discussed elsewhere in this documentation.
+   
+   For ZODB documentation see http://www.zope.org/Wikis/ZODB/guide/zodb.html
+
+Because zc.async uses the ZODB for its persistence mechanism, the ZODB's
+persistence rules are in effect.
+
+Luckily, these are fairly simple.
+
+For now, we'll stay as simple as it gets: if you use *module global functions*
+and *immutables*, and share software across instances, you'll be fine.
+
+ZODB allows a lot more, if you're willing to follow a few more rules, but that
+one rule will get us moving for this quick-start.
+
+.. _MVCC: http://en.wikipedia.org/wiki/Multiversion_concurrency_control
+
+-------------
+Process UUIDs
+-------------
+
+Exit the Python interpreter (control-D).  Look around in the directory
+(``ls``): you should see a few database files, the key.pem you created, and a
+new file: ``uuid.txt``.  It should look something like this::
+
+    $ cat uuid.txt
+    afd1e0d0-52e1-11dd-879b-0017f2c49bdd
+    ------------------------------------------------------------------------
+    The value above (and this file) is created and used by the zc.async
+    package. It is intended to uniquely identify this software instance when
+    it is used to start a zc.async dispatcher.  This allows multiple
+    dispatchers, each in its own software instance, to connect to a single
+    database to do work.
+    
+    In order to decide where to look for this file (or to create it, if
+    necessary), the module looks in ``os.environ['ZC_ASYNC_UUID']`` for a
+    file name.
+    
+    If you are using zdaemon (http://pypi.python.org/pypi/zdaemon) to
+    daemonize your process, you can set this in a zdaemon environment section
+    of your zdaemon.conf. Supervisor (http://supervisord.org/) also provides
+    this functionality. Other similar tools probably do as well.
+    
+    If the ``ZC_ASYNC_UUID`` is not found in the environment, it will use
+    ``os.path.join(os.getgwd(), 'uuid.txt')`` as the file name.
+    
+    To get a new identifier for this software instance, delete this file,
+    restart Python, and import zc.async.instanceuuid.  This file will be
+    recreated with a new value.
+
+That text is intended to be self-explanatory, so hopefully it made sense to
+you.  We'll handle these UUIDs more explicitly in a moment.
+
+-----------
+Make a File
+-----------
+
+Make a new Python file.  Let's call it ``pi.py``.
+Save this file in ``lib/python2.5/site-packages/``.
+
+Use the following for the file content.
+
+.. sidebar:: Code Walkthrough
+
+    This code walkthrough focuses on the elements needed to use |async|,
+    rather than the calculation of pi.  Numerous explanations of this Monte
+    Carlo simulation to calculate pi are on the internet.  I liked `this one`_.
+
+    We begin with some imports.
+
+    The ``generate_sample`` code will be run on multiple processes to produce
+    samples for our Monte Carlo function simulation.  It is ignorant of
+    |async|.
+    
+    Once the samples are all done, we'll reduce the results with
+    ``process_samples``.  It will return an approximation of pi, with
+    accuracy strongly influenced by the total size of the aggregated samples,
+    assuming even distribution of the random numbers.  As you'll see
+    soon, we'll be using a |async| convenience function for
+    :func:`~zc.async.job.parallel` jobs that gives all of the completed
+    jobs that have been running in parallel to this, the postprocessing
+    call. Therefore, ``process_samples`` gets the ``result`` of
+    ``generate_sample`` off of each job. Other than that, this function
+    is ignorant of |async|.
+    
+    The last code block should look similar to our previous example of starting
+    up a dispatcher, except this one uses the main, installed Twisted reactor,
+    rather than a threaded instance.  It creates the database, configures
+    zc.async, and starts the reactor.  We use a short poll_interval so we can
+    have a perceptually snappy response in this quick start.
+
+.. literalinclude:: pi1.py
+   :linenos:
+
+.. We'll need these defined when we run this as a test.
+
+    >>> import random
+    >>> import math
+    >>> import types
+    >>> import sys
+
+    >>> def generate_sample(size=100000):
+    ...     count = 0
+    ...     for i in range(size):
+    ...         if math.hypot(random.random(), random.random()) < 1:
+    ...             count += 1
+    ...     return count, size
+    ... 
+    >>> def process_samples(*sample_jobs):
+    ...     count = 0 
+    ...     size = 0
+    ...     for j in sample_jobs:
+    ...         count += j.result[0]
+    ...         size += j.result[1]
+    ...     return 4.0 * count / size
+    ...
+    >>> _pi = types.ModuleType('pi')
+    >>> sys.modules['pi'] = _pi
+    >>> _pi.generate_sample = generate_sample
+    >>> _pi.process_samples = process_samples
+
+.. _`this one`: http://math.fullerton.edu/mathews/n2003/MonteCarloPiMod.html
+
+--------------------------------
+Our First Monte Carlo Experiment
+--------------------------------
+
+We'll need the ZEO server running.  If you've gone through this file from the
+start, it should still be running.  If not, use this::
+
+    $ ./bin/runzeo -a 9999 -f test.fs &
+
+Now, for our first experiment with the Monte Carlo simulation, we'll start a
+single worker process.
+
+Enter this in the terminal::
+
+    $ ./bin/python lib/python2.5/site-packages/pi.py &
+
+That will start our worker process.  Now let's start an interpreter.
+
+::
+
+    $ ./bin/python
+
+Now get a database and a connection, as we've seen before.  We'll also set up
+the base zc.async configuration.
+
+::
+
+    import ZEO.ClientStorage
+    import ZODB
+    storage = ZEO.ClientStorage.ClientStorage(
+        ('127.0.0.1', 9999))
+    db = ZODB.DB(storage)
+    conn = db.open()
+    import zc.async.configure
+    zc.async.configure.base()
+
+We don't have any adapters installed, so ``zc.async.interfaces.IQueue(conn)``
+won't work.  This will though, and still looks pretty good::
+
+    >>> import zc.async.queue
+    >>> q = zc.async.queue.getDefaultQueue(conn)
+
+Now we can start some jobs in :func:`~zc.async.job.parallel`.
+
+::
+
+    >>> import pi
+    >>> import zc.async.job
+    >>> j = q.put(zc.async.job.parallel(
+    ...     pi.generate_sample, pi.generate_sample, pi.generate_sample,
+    ...     postprocess=pi.process_samples))
+    >>> import transaction
+    >>> transaction.commit()
+
+Wait a few seconds.  If the result is empty (None), begin the transaction again
+and check the result again.  Eventually, these next two lines should give you a
+result: an approximation of pi.
+
+.. This lets us "wait a second".
+
+    >>> zc.async.testing.wait_for_result(j) # doctest: +ELLIPSIS
+    3.1...
+
+..
+
+::
+
+    _ = transaction.begin()
+    j.result
+
+For one run, I got ``3.1386666666666665``.  Cool.
+
+--------
+Closures
+--------
+
+We've already seen Jobs used as closures in the openssl example.  You can also
+use them to pass a different ``size`` argument to ``generate_sample``.
+
+Let's try it.
+
+    >>> j = q.put(zc.async.job.parallel(
+    ...     zc.async.job.Job(pi.generate_sample, 1000000),
+    ...     zc.async.job.Job(pi.generate_sample, size=1000000),
+    ...     postprocess=pi.process_samples))
+    >>> transaction.commit()
+
+Wait a bit, again.  Retry these two lines until you get a result.  It should
+be well under a minute on most machines.
+
+.. This lets us "wait a second".
+
+    >>> zc.async.testing.wait_for_result(j, seconds=20) # doctest: +ELLIPSIS
+    3.1...
+
+..
+
+::
+
+    _ = transaction.begin()
+    j.result
+
+My run got ``3.1434359999999999``.  Cool.
+
+-------------
+Configuration
+-------------
+
+Unfortunately, the parallel runs we've done so far would actually make the
+calculation go slower than if we did it in a single function call!  That's
+because we ran it in a single Python worker process, and the overhead of
+threads just makes the jobs a bit less efficient.  Threads help for some uses
+of |async|, but not this one.
+
+Let's assume you are running these experiments on a machine with two processor
+cores.  We should actually start two worker processes then, one for each core.
+We'll need to make sure each worker process has its own OID.
+
+Moreover, we need to configure each process to only take one
+``generate_sample`` job at a time.  Let's adjust our code to do that.
+
+Agents
+------
+
+A worker process regularly polls the database for new jobs.  The software
+component that polls is called a ``dispatcher``. Dispatchers look in the
+database to ask their personal ``agent`` (or agents) to determine what
+they should get their threads to do.  Think of the ``agent`` as a
+"`talent agent`_" or a "booking agent" for the process.
+
+By default, using the zc.async.configure helpers, each dispatcher is given a
+single agent that will choose the first job in the queue, and that wants to run
+no more than three jobs at a time.
+
+For our Monte Carlo job, we'll give each process an additional agent. The
+new agent will only accept up to one instance of a ``generate_sample``
+job at a time. 
+
+We will also reconfigure the existing agent to accept up to three of
+anything *except* ``generate_sample``.
+
+We'll do that in our file.  Here's the revised version.  The only changes
+are the imports, the three new functions, and setting up
+``install_agent`` in the ``if __name__ == '__main__':`` block.
+
+.. sidebar:: Code Walkthrough for Changes
+
+   We import three new modules from |async|, :mod:zc.async.queue,
+   :mod:zc.async.instanceuuid, and :mod:zc.async.agent.  The
+   :class:`~zc.async.interfaces.IAgent`
+   implementation (:class:`zc.async.agent.Agent`) uses a function called a
+   :attr:`~zc.async.agent.Agent.chooser` to determine its
+   policy for choosing agents.  We define two chooser functions, one for
+   each agent: ``choose_generate_sample`` and ``choose_another``.  Then
+   we have a function ``install_agent`` to set up the old agent to use
+   ``choose_another``, and create a new agent of ``size=1`` with the
+   ``choose_generate_sample`` chooser.  We make sure this function is
+   installed to run when the Twisted reactor starts. That's it.
+   
+   It's important to note that the references to these functions are
+   persistent, and by name.  If you change the location or name of these
+   functions, you will need to keep the old names and locations around,
+   at least for long enough to switch your agents to use the new names. 
+   This is a general pattern for module globals--functions and
+   classes--to which the ZODB has direct references or instances.
+   
+   We could have accomplished the same basic policy changes with several
+   different agent configurations. For instance, we could have written a
+   chooser that looked through its agents's current jobs to verify that
+   it did not have another ``generate_sample``.
+   
+   The only trick to this kind of agent configuration is that you always
+   want to have at least some catch-all dispatchers who are willing to do
+   just about any jobs (what our ``choose_another`` choose accomplishes).
+   This supports jobs that the |async| system sometimes needs to run for
+   exceptional circumstances.
+
+.. literalinclude:: pi2.py
+   :linenos:
+
+.. _`talent agent`: http://en.wikipedia.org/wiki/Talent_agent
+
+-------------
+Demonstration
+-------------
+
+Now let's start up our workers, and see how they work.  We're going to
+have two workers now, and they will each need separate UUIDs.  A really
+simple approach will be to make two separate working directories for the
+two worker processes.  (We also could use the environmental variable,
+``ZC_ASYNC_UUID``, described in the `Process UUIDs`_ section above.)
+
+::
+
+    $ mkdir worker1
+    $ mv uuid.txt worker1
+    $ cd worker1
+    $ ../bin/python ../lib/python2.5/site-packages/pi.py &
+    $ cd ..
+    $ mkdir worker2
+    $ cd worker2
+    $ ../bin/python ../lib/python2.5/site-packages/pi.py &
+
+Now we'll start the Python process in which we will test our code.  We'll move
+to the main directory, but as long as we don't start another worker, it doesn't
+really matter.
+
+::
+
+    $ cd ..
+    $ ./bin/python
+
+And now, our test.
+
+::
+
+    import ZEO.ClientStorage
+    import ZODB
+    storage = ZEO.ClientStorage.ClientStorage(
+        ('127.0.0.1', 9999))
+    db = ZODB.DB(storage)
+    conn = db.open()
+    import zc.async.configure
+    zc.async.configure.base()
+    import pi
+    import zc.async.job
+    import zc.async.queue
+    q = zc.async.queue.getDefaultQueue(conn)
+    j = q.put(zc.async.job.parallel(
+        zc.async.job.Job(pi.generate_sample, 5000000),
+        zc.async.job.Job(pi.generate_sample, size=5000000),
+        postprocess=pi.process_samples))
+    import transaction
+    transaction.commit()
+
+Wait a few seconds and then try these lines.
+
+::
+
+    _ = transaction.begin()
+    j.result
+
+If the result is empty (None), repeat those two lines again (thatæ is, begin
+the transaction again and check the result again).  Eventually, these lines
+should give you a result: an approximation of pi.
+
+Just to prove to ourselves that we saved some time, let's do a comparison test:
+the same number of samples, but not in parallel.
+
+::
+
+    j2 = q.put(zc.async.job.parallel(
+        zc.async.job.Job(pi.generate_sample, 10000000),
+        postprocess=pi.process_samples))
+    transaction.commit()
+    _ = transaction.begin()
+    j2.result
+
+Once both jobs are complete, compare their run-time.
+
+::
+
+    j.active_end - j.active_start
+    j2.active_end - j2.active_start
+
+On my machine, even in this simple, short example, running in parallel
+with a job per processor/core took 7.8 seconds, while running all in one
+process took 13.4 seconds.
+
+Monitoring
+==========
+
+Soon, you may want to be able to monitor or introspect what's going on in your
+zc.async work.  The package provides several tools to do that.  We'll take a
+look at a few here.
+
+We will be turning on a monitor port.  This port should be protected behind a
+firewall, like your ZEO ports.
+
+If you like the functionality that we describe here but would prefer to expose
+it in a different manner, note that most of the Python functions in
+``monitor.py`` and ``monitordb.py`` power the zc.async commands in the monitor
+port, and can be used without the monitor itself.
+
+To enable the monitoring port, we need to install some extra dependencies for
+zc.async: "[monitor]".  Exit the Python interpreter an make sure you are in the
+top "quickstart" directory, and then enter this command:
+
+    $ ./bin/easy_install zc.async[monitor]
+
+If you take a glance at the output, you'll see we've only added a few
+dependencies: ``simplejson``, ``zc.ngi``, and ``zc.monitor``.
+
+Now let's turn on the port and the zc.async commands.
+
+At the top of the ``pi.py`` file, add some imports::
+
+    import os
+    import zope.component
+    import zc.monitor
+    import zc.monitor.interfaces
+    import zc.async.monitor
+    import zc.async.monitordb
+
+Then down in the ``if __name__ == '__main__':`` block, add these lines at the
+top.
+
+::
+
+    monitor_port = os.environ.get('MONITOR_PORT')
+    if monitor_port:
+        for f in (zc.monitor.interactive, zc.monitor.quit, zc.monitor.help,
+                  zc.async.monitor.async, zc.async.monitordb.asyncdb):
+            zope.component.provideUtility(
+                f, zc.monitor.interfaces.IMonitorPlugin, f.__name__)
+        zc.monitor.start(int(monitor_port))
+
+The file should look like this now.
+
+.. literalinclude:: pi3.py
+   :linenos:
+
+Now stop and restart your two worker instances, this time providing two
+different ports in the environment for each worker.  Here's one way to do it.
+First we'll shut down the previous instances.  If you used the lines above to
+start them before, type ``fg`` and RETURN, and then CTRL-C to stop worker 2;
+and then do the same thing (``fg`` and RETURN, and then CTRL-C) to stop worker
+1.
+
+::
+
+    $ cd worker1
+    $ MONITOR_PORT=9991 ../bin/python ../lib/python2.5/site-packages/pi.py &
+    $ cd ../worker2
+    $ MONITOR_PORT=9992 ../bin/python ../lib/python2.5/site-packages/pi.py &
+
+Now you can open connections to these two ports, 9991 and 9992, and query the
+worker (using the ``async`` command, primarily) and the state of zc.async in
+the database itself (the ``asyncdb`` command).
+
+The easiest way to experiment with this is using telnet.  Try this.
+
+::
+
+    $ telnet 127.0.0.1 9991
+
+You should see something like this::
+
+    Trying 127.0.0.1...
+    Connected to localhost.
+    Escape character is '^]'.
+
+Now you can experiment with commands.  Try this (followed by a RETURN)::
+
+    help
+
+You should see something like this::
+
+    Supported commands:
+      async -- Monitor zc.async activity in this process.
+      asyncdb -- Monitor and introspect zc.async activity in the database.
+      help -- Get help about server commands
+      interactive -- Turn on monitor's interactive mode
+      quit -- Quit the monitor
+    Connection closed by foreign host.
+    $
+
+Hm, it dumped us straight back to the shell!  ``zc.monitor`` behaves that way
+tp be friendly to automated monitoring processes using the port.  We can use
+the ``interactive`` command to make things a bit more pleasant for ourselves.
+
+Reuse the telnet command shown above, or maybe connect to 9992 (``telnet
+127.0.0.1 9992``) to see that you can.  This time, type the ``interactive``
+command first.  You should see this reply::
+
+    Interactive mode on.  Use "quit" To exit.
+
+Now experiment!  Try some of these commands to see what you get.
+
+::
+
+    help async
+    
+    async help
+
+    async help status
+    
+    async status
+    
+    async help poll
+    
+    async poll
+    
+    async UUID
+    
+    async utcnow
+    
+    help asyncdb
+
+    asyncdb help
+    
+    asyncdb help UUIDs
+    
+    asyncdb UUIDs
+    
+    asyncdb lastcompleted
+    
+    asyncdb count completed
+    
+    asyncdb count completed uuid:THIS
+
+That last command shows how many jobs this worker has completed for as long as
+the database has records, which by default means between seven and eight days. 
+The help for ``asyncdb count``, which is available from ``asyncdb help count``,
+is not short, but tells you the many options available.
+
+When you are done, use the ``quit`` command to exit the telnet connection to
+the monitor port.
+
+Another important tool is logging.  The ``zc.async.event`` logger gets
+important events--pay special attention to critical events!  ``zc.async.trace``
+lets you follow along with every detail, if so desired.
+
+These monitoring and introspection tools, combined with logging, provide
+powerful tools to keep track of your zc.async work.
+
+Other Configuration
+===================
+
+We're at the end of this quickstart.  To close, here's a quick survey of some
+other configuration opportunities available that we haven't seen here.
+
+- Callbacks are a very important per-job configuration.  You can add them to
+  a job, to be run unconditionally, conditionally if the result is an
+  instance of a ``twisted.python.failure.Failure``, or conditionally if
+  the result is not a ``Failure``.  See
+  :meth:`~zc.async.interfaces.IJob.addCallback`,
+  :meth:`~zc.async.interfaces.IJob.addCallbacks`, and
+  :attr:`~zc.async.interfaces.IJob.callbacks`
+
+.. note::
+
+   Unlike Twisted callbacks, all callbacks for the same job get the same
+   result; if you would like to chain results, the callbacks themselves
+   are Jobs, so attach a callback to your callback.
+
+- You can request that a job :attr:`~zc.async.interfaces.IJob.begin_after`
+  a given timezone-aware datetime.  If not given, this defaults to now, for the
+  purposes of calculating the effective
+  :attr:`~zc.async.interfaces.IJob.begin_by` datetime, described below.
+
+- You can specify that a job should :attr:`~zc.async.interfaces.IJob.begin_by`
+  a given duration (datetime.timedelta) *after* the jobs's
+  :attr:`~zc.async.interfaces.IJob.begin_after` value.  When the queue
+  :gets
+  ready to offer the job for an agent to choose, if the effective
+  ``begin_by`` value has passed, the queue will instead offer a call to
+  the job's
+  :meth:`~zc.async.interfaces.IJob.fail` method.
+
+.. note::
+
+   There is no built-in way to stop a running job, short of stopping the
+   process.  This can be approximated by use in your job of
+   :func:`~zc.async.local.getLiveAnnotation` to poll for stop requests; or the
+   brave can write some C to use PyThreadState_SetAsyncExc_.
+
+- You can set up quotas for certain arbitrary quota names that you define.
+  This is a limit: no more than the given quota can run at once, total,
+  across all workers.  This can let you decrease the chance of conflict
+  errors for long-running jobs that write to the same data structures.  See
+  :attr:`~zc.async.interfaces.IQueue.quotas`,
+  :class:`~zc.async.interfaces.IQuotas`, and
+  :attr:`~zc.async.interfaces.IJob.quota_names`.
+
+- Retry policies determine how jobs should be retried if they raise an
+  uncaught exception while running, if their commit raises an error, or if
+  they are interrupted.  They can be configured per job, or as defaults for
+  callback and non-callback jobs.  See
+  :class:`~zc.async.interfaces.IRetryPolicy`,
+  :attr:`~zc.async.interfaces.IJob.retry_policy_factory`,
+  :meth:`~zc.async.interfaces.IJob.getRetryPolicy`; the default retry policies
+  :class:`~zc.async.job.RetryCommonFourTimes`,
+  :class:`~zc.async.job.RetryCommonForever` and
+  :class:`~zc.async.job.NeverRetry`; and the ``retry_policy_factory`` argument
+  to :meth:`~zc.async.interfaces.IJob.addCallback`,
+  :meth:`~zc.async.interfaces.IJob.addCallbacks`, and
+  :meth:`~zc.async.interfaces.IQueue.put`.
+
+- The :attr:`~zc.async.interfaces.IJob.failure_log_level` determines at what
+  level Failure results for a given job should be logged.  This is usually
+  logging.ERROR, and logging.CRITICAL for callbacks.  This can be set directly
+  on the job, or applied via the ``failure_log_level`` argument to
+  :meth:`~zc.async.interfaces.IJob.addCallback`,
+  :meth:`~zc.async.interfaces.IJob.addCallbacks`, and
+  :meth:`~zc.async.interfaces.IQueue.put`.
+
+- Custom job subclasses can take advantage of
+  :meth:`~zc.async.interfaces.IJob.setUp` and
+  :meth:`~zc.async.interfaces.IJob.tearDown` hooks to set up and tear down
+  state.  An example is the Zope 3-specific :class:`~zc.async.z3.Job`.
+
+- A custom queue might support broadcasting jobs across dispatchers, or
+  targeting specific dispatchers.  These features may be developed for |async|
+  itself at a later date.
+
+There are many other topics to discuss--testing, debugging, and Zope 3
+integration, for instance--but this is a quick start, so we'll end here.
+
+.. _PyThreadState_SetAsyncExc: http://docs.python.org/api/threads.html
+
+.. Now we are going to stop the reactor.
+
+    >>> import zc.async.dispatcher
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> reactor = dispatcher.reactor
+    >>> reactor.callFromThread(reactor.stop)
+    >>> dispatcher.thread.join(3)


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/QUICKSTART_1_VIRTUALENV.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/QUICKSTART_2_GROK.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/QUICKSTART_2_GROK.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/QUICKSTART_2_GROK.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,192 @@
+=====================
+Quickstart with Grok_
+=====================
+
+Goals
+=====
+
+In this quickstart, we will use zc.async to make a small web application that
+is a Python Package Index (PyPI, http://pypi.python.org/) helper portal.  We'll
+call it "My PyPI," to be cute.
+
+*My PyPI* will let you subscribe to changes of specific packages, rather than
+the entire package index; and will let you associate external web pages with
+packages for you and others to see and search on.  
+
+We'll make a number of "toy app" decisions to keep the story quick, but it
+should be a good example for how to leverage zc.async.
+
+Also for simplicity, we'll assume that we are making several instances on the
+same machine, such as you might do with a few processors at your disposal.  To
+get the true advantage of high availability in production, you'd want at least
+two boxes, with a deployment of a ZEO server (or equivalent, for RelStorage),
+some kind of redundancy for your database (ZRS, or slony for RelStorage plus
+PostgreSQL) and instructions for each box on how to connect to the ZEO primary.
+
+This quickstart is more complex than the :ref:`quickstart-with-virtualenv`.  I
+suggest you read that through before this one.  
+
+- That previous quickstart introduces |async| through the Python interpreter
+  for a very casual and quick start.  
+
+- It also is more "pure-Python" with very little understanding needed of
+  additional frameworks to follow and use the examples.
+
+This quickstart instead uses the following somewhat "heavier" technologies.
+
+- |zc.buildout|_ is a way of producing repeatable software build-outs, for
+  development and conceivably for deployment.  It is written in Python, but is
+  not Python-specific, and it has found use as a ``make`` replacement for many
+  projects.
+
+- Grok_ is a web framework emerging from "Zope 3" technologies.  From their
+  website:
+  
+    Grok is a web application framework for Python developers. It is aimed at
+    both beginners and very experienced web developers. Grok has an emphasis on
+    agile development. Grok is easy and powerful.
+
+This guide, then, takes a somewhat slower definition of "quick" for its
+"quickstart", in exchange for more guidance and care with a view towards
+production-readiness.
+
+.. _Grok: http://grok.zope.org/
+
+.. |async| replace:: ``zc.async``
+
+.. _`async`: http://pypi.python.org/pypi/zc.async
+
+.. |zc.buildout| replace:: ``zc.buildout``
+
+.. _`zc.buildout`: http://pypi.python.org/pypi/zc.buildout
+
+Prerequisites
+=============
+
+.. sidebar:: Building Python 2.4.5 on OS X Leopard
+
+   Unfortunately, building a clean, standalone workable Python 2.4.5 on OS X is
+   not obvious.  This is what I recommend, if you are working on that platform.
+   
+   First you need macports.  Go to http://www.macports.org/ and download the
+   newest version.  It doesn't seem to set up the manual path correctly, so
+   after the installation add this to your ``~/.profile`` (or in a similar
+   place)::
+   
+    export MANPATH=/opt/local/man:$MANPATH
+   
+   You'll need a new terminal session (or other shell magic if you know it) for
+   these changes to take effect.  The easiest thing to do is close the shell
+   you are working in and open a new one.
+
+   Download a source distribution of Python 2.4.5.  You may have your own
+   approach as to where to put things, but I go with this pattern: ``~/src``
+   holds expanded source trees, ``~/opt`` holds our local Python, and I develop
+   in ``~/dev``.
+   
+   We will want readline and need zlib from macports.
+
+   ::
+
+    $ sudo port -c install readline
+    $ sudo port -c install zlib
+   
+   Now we'll do the usual dance, with a couple of ugly extra steps.
+
+   **Note: replace ``/Users/gary/opt/py`` in the below with your own desired
+   location!**
+
+   ::
+
+    $ MACOSX_DEPLOYMENT_TARGET=10.5 ./configure \
+      --prefix=/Users/gary/opt/py \
+      LDFLAGS=-L/opt/local/lib \
+      OPT=-I/opt/local/include
+    $ make
+    $ make install
+
+   Now, given my ``--prefix``, I'll find my python in
+   ``/Users/gary/opt/py/bin/python``.
+
+As of this writing, Grok requires Python 2.4.  Moreover, for more repeatable
+installations, many developers strongly recommend using a "clean", non-system
+Python, to reduce the probability of unnecessary or spurious problems (in your
+software *or* in your system!).  Therefore, consider building your own Python
+2.4 for your development.
+
+We'll also expect that your Python has |easy_install|_.  If it doesn't, you can
+just download `ez_setup.py`_ and then run it with your local, development
+Python (e.g., ``~/opt/py/bin/python ez_setup.py``). This will install the
+easy_install command for your development Python in the same bin directory as
+your Python (e.g., ``~/opt/py/bin/easy_install``).
+
+.. |easy_install| replace:: ``easy_install``
+
+.. _`easy_install`: http://peak.telecommunity.com/DevCenter/EasyInstall
+
+.. _`ez_setup.py`: http://peak.telecommunity.com/dist/ez_setup.py
+
+grokproject_
+============
+
+.. sidebar:: |zc.buildout| Conveniences
+
+   You may want to consider the following conveniences if you are building many
+   projects with |zc.buildout|.  They make zc.buildout keep two shared
+   collections across all of your zc.buildout projects.  This can significantly
+   speed up the time to buildout new applications.  One shared collection is a
+   download cache of source distributions and eggs.  The other is an egg cache
+   only, for both the downloaded eggs and the eggs generated on your machine.
+   
+   In your home directory, make a ``.buildout`` directory.  In that directory,
+   make two sub-directories, ``eggs`` and ``download-cache``.  Also in
+   ``.buildout``, create a file named ``default.cfg`` with the following
+   content, where ``/Users/gary`` is replaced with the path to your home
+   directory::
+
+    [buildout]
+    eggs-directory=/Users/gary/.buildout/eggs
+    download-cache=/Users/gary/.buildout/download-cache
+
+   There are many other possible settings to make here (for instance, we could
+   specify the clean Python you built here), but these are all I
+   currently bother with.
+
+   It is also worth mentioning that, as of this writing, setuptools builds eggs
+   in such a way as to confuse the Python debugger.  If you use the Python
+   debugger and discover that you want to see the lines in an egg and can't,
+   the following line (or something like it) will help for non-zipped eggs::
+   
+    find ~/.buildout/eggs-aside/ -name '*.pyc' -exec rm {} \;
+
+Grok has a pleasantly convenient way to start a project.  It is called
+grokproject_.  Use your local Python's ``easy_install`` to install it.  For
+instance, I might type ``~/opt/py/bin/easy_install grokproject``.
+
+After it runs, it should have installed the ``grokproject`` command in the same
+bin directory as your local Python (e.g., ``~/opt/py/bin/grokproject``).
+
+.. _grokproject: http://pypi.python.org/pypi/grokproject
+
+Skeleton
+========
+
+Now we will use grokproject_ to make a skeleton of our package.  Let's
+call the project "mypypi".  Go to a directory in which you want to develop
+our package.  Then use the newly installed ``grokproject`` command to create
+
+XXX
+
+- include zc.async in setup.py; mention versions.cfg
+
+- set up ZEO
+
+- set up multiple instances
+
+- zope.app.testing = 3.4.1 -> 3.4.2
+
+- set up interpreter
+
+- set up z3monitor
+
+- make separate debug instance


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/QUICKSTART_2_GROK.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/README.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/README.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/README.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,244 @@
+============
+Introduction
+============
+
+Goals
+=====
+
+The zc.async package provides a way to schedule jobs to be performed
+out-of-band from your current thread.  The job might be done in another thread
+or another process, possibly on another machine.  Here are some example core
+use cases.
+
+- You want to let users do something that requires a lot of system
+  resources from your application, such as creating a large PDF.  Naively
+  done, six or seven simultaneous PDF requests will consume your
+  application thread pool and could make your application unresponsive to
+  any other users.
+
+- You want to let users spider a web site; communicate with a credit card
+  company; query a large, slow LDAP database on another machine; or do
+  some other action that generates network requests from the server.
+  System resources might not be a problem, but, again, if something goes
+  wrong, several requests could make your application unresponsive.
+
+- Perhaps because of resource contention, you want to serialize work
+  that can be done asynchronously, such as updating a single data structure
+  like a catalog index.
+
+- You want to decompose and parallelize a single job across many machines so
+  it can be finished faster.
+
+- You have an application job that you discover is taking longer than users can
+  handle, even after you optimize it.  You want a quick fix to move the work
+  out-of-band.
+
+Many of these core use cases involve end-users being able to start potentially
+expensive processes, on demand.  Basic scheduled tasks are also provided by this
+package, though recurrence must be something you arrange.
+
+History
+=======
+
+This is a second-generation design.  The first generation was `zasync`,
+a mission-critical and successful Zope 2 product in use for a number of
+high-volume Zope 2 installations.  [#async_history]_ It's worthwhile noting
+that zc.async has absolutely no backwards compatibility with zasync and
+zc.async does not require Zope (although it can be used in conjunction with
+it).
+
+Design Overview
+===============
+
+---------------
+Overview: Usage
+---------------
+
+Looking at the design from the perspective of regular usage, your code obtains
+a ``queue``, which is a place to register jobs to be performed asynchronously.
+
+Your application calls ``put`` on the queue to register a job.  The job must be
+a pickleable, callable object.  A global function, a callable persistent
+object, a method of a persistent object, or a special zc.async.job.Job object
+(discussed later) are all examples of suitable objects.  The job by default is
+registered to be performed as soon as possible, but can be registered to be
+called at a certain time.
+
+The ``put`` call will return a zc.async.job.Job object.  This object represents
+both the callable and its deferred result.  It has information about the job
+requested, the current state of the job, and the result of performing the job.
+
+An example spelling for registering a job might be ``self.pending_result =
+queue.put(self.performSpider)``.  The returned object can be stored and polled
+to see when the job is complete; or the job can be configured to do additional
+work when it completes (such as storing the result in a data structure).
+
+-------------------
+Overview: Mechanism
+-------------------
+
+Multiple processes, typically spread across multiple machines, can
+connect to the queue and claim and perform work.  As with other
+collections of processes that share pickled objects, these processes
+generally should share the same software (though some variations on this
+constraint should be possible).
+
+A process that should claim and perform work, in addition to a database
+connection and the necessary software, needs a ``dispatcher`` with a
+``reactor`` to provide a heartbeat.  The dispatcher will rely on one or more
+persistent ``agents`` in the queue (in the database) to determine which jobs
+it should perform.
+
+A ``dispatcher`` is in charge of dispatching queued work for a given
+process to worker threads.  It works with one or more queues and a
+single reactor.  It has a universally unique identifier (UUID), which is
+usually an identifier of the application instance in which it is
+running.  The dispatcher starts jobs in dedicated threads.
+
+A ``reactor`` is something that can provide an eternal loop, or heartbeat,
+to power the dispatcher.  It can be the main twisted reactor (in the
+main thread); another instance of a twisted reactor (in a child thread);
+or any object that implements a small subset of the twisted reactor
+interface (see discussion in dispatcher.txt, and example testing reactor in
+testing.py, used below).
+
+An ``agent`` is a persistent object in a queue that is associated with a
+dispatcher and is responsible for picking jobs and keeping track of
+them. Zero or more agents within a queue can be associated with a
+dispatcher.  Each agent for a given dispatcher in a given queue is
+identified uniquely with a name [#identifying_agent]_.
+
+Generally, these work together as follows.  The reactor calls the
+dispatcher. The dispatcher tries to find the mapping of queues in the
+database root under a key of ``zc.async`` (see constant
+zc.async.interfaces.KEY).  If it finds the mapping, it iterates
+over the queues (the mapping's values) and asks each queue for the
+agents associated with the dispatcher's UUID.  The dispatcher then is
+responsible for seeing what jobs its agents want to do from the queue,
+and providing threads and connections for the work to be done.  The
+dispatcher then asks the reactor to call itself again in a few seconds.
+
+.. rubric:: Footnotes
+
+.. [#async_history] The first generation, ``zasync``, had the following goals:
+
+    - be scalable, so that another process or machine could do the asynchronous
+      work;
+
+    - support lengthy jobs outside of the ZODB;
+
+    - support lengthy jobs inside the ZODB;
+
+    - be recoverable, so that crashes would not lose work;
+
+    - be discoverable, so that logs and web interfaces give a view into the
+      work being done asynchronously;
+
+    - be easily extendible, to do new jobs; and
+
+    - support graceful job expiration and cancellation.
+
+    It met its goals well in some areas and adequately in others.
+
+    Based on experience with the first generation, this second generation
+    identifies several areas of improvement from the first design, and adds
+    several goals.
+
+    - Improvements
+
+      * More carefully delineate the roles of the comprising components.
+
+        The zc.async design has three main components, as divided by their
+        roles: persistent deferreds, now called jobs; job queues (the original
+        zasync's "asynchronous call manager"); and dispatchers (the original
+        zasync ZEO client). The zasync 1.x design blurred the lines between the
+        three components such that the component parts could only be replaced
+        with difficulty, if at all. A goal for the 2.x design is to clearly
+        define the role for each of three components such that, for instance, a
+        user of a queue does not need to know about the dispatcher or the
+        agents.
+
+      * Improve scalability of asynchronous workers.
+
+        The 1.x line was initially designed for a single asynchronous worker,
+        which could be put on another machine thanks to ZEO. Tarek Ziade of
+        Nuxeo wrote zasyncdispatcher, which allowed multiple asynchronous
+        workers to accept work, allowing multiple processes and multiple
+        machines to divide and conquer. It worked around the limitations of the
+        original zasync design to provide even more scalability. However, it
+        was forced to divide up work well before a given worker looks at the
+        queue.
+
+        While dividing work earlier allows guesses and heuristics a chance to
+        predict what worker might be more free in the future, a more reliable
+        approach is to let the worker gauge whether it should take a job at the
+        time the job is taken. Perhaps the worker will choose based on the
+        worker's load, or other concurrent jobs in the process, or other
+        details. A goal for the 2.x line is to more directly support this type
+        of scalability.
+
+      * Improve scalability of registering jobs.
+
+        The 1.x line initially wasn't concerned about very many concurrent
+        asynchronous requests. When this situation was encountered, it caused
+        ConflictErrors between the worker process reading the deferred queue
+        and the code that was adding the deferreds. Thanks to Nuxeo, this
+        problem was addressed in the 1.x line. A goal for the new version is to
+        include and improve upon the 1.x solution.
+
+      * Make it even simpler to provide new jobs.
+
+        In the first version, `plugins` performed jobs. They had a specific API
+        and they had to be configured. A goal for the new version is to require
+        no specific API for jobs, and to not require any configuration.
+
+      * Improve report information, especially through the web.
+
+        The component that the first version of zasync provided to do the
+        asynchronous work, the zasync client, provided very verbose logs of the
+        jobs done, but they were hard to read and also did not have a through-
+        the-web parallel. Two goals for the new version are to improve the
+        usefulness of the filesystem logs and to include more complete
+        visibility of the status of the provided asynchronous clients.
+
+      * Make it easier to configure and start, especially for small
+        deployments.
+
+        A significant barrier to experimentation and deployment of the 1.x line
+        was the difficulty in configuration. The 1.x line relied on ZConfig for
+        zasync client configuration, demanding non-extensible
+        similar-yet-subtly-different .conf files like the Zope conf files. The
+        2.x line provides code that Zope 3 can configure to run in the same
+        process as a standard Zope 3 application. This means that development
+        instances can start a zasync quickly and easily. It also means that
+        processes can be reallocated on the fly during production use, so that
+        a machine being used as a zasync process can quickly be converted to a
+        web server, if needed, and vice versa.
+
+    - New goals
+
+      * Support intermediate return calls so that jobs can report back how they
+        are doing.
+
+        A frequent request from users of zasync 1.x was the ability for a long-
+        running asynchronous process to report back progress to the original
+        requester. The 2.x line addresses this with three changes:
+
+        + jobs are annotatable;
+
+        + jobs should not be modified in an asynchronous worker that does work
+          (though they may be read);
+
+        + jobs can request another job in a synchronous process that annotates
+          the job with progress status or other information.
+
+        Because of relatively recent changes in ZODB--multi version concurrency
+        control--this simple pattern should not generate conflict errors.
+
+      * Support time-delayed calls.
+
+        Retries and other use cases make time-delayed deferred calls desirable.
+        The new design supports these sort of calls.
+
+.. [#identifying_agent] The combination of a queue name plus a
+    dispatcher UUID plus an agent name uniquely identifies an agent.


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/README_1.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/README_1.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/README_1.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,1195 @@
+.. _usage:
+
+=====
+Usage
+=====
+
+Overview and Basics
+===================
+
+The basic usage of zc.async does not depend on a particular configuration
+of the back-end mechanism for getting the jobs done.  Moreover, on some
+teams, it will be the responsibility of one person or group to configure
+zc.async, but a service available to the code of all team members.  Therefore,
+we begin our detailed discussion with regular usage, assuming configuration
+has already happened.  Subsequent sections discuss configuring zc.async
+with and without Zope 3.
+
+So, let's assume we have a queue with dispatchers, reactors and agents all
+waiting to fulfill jobs placed into the queue.  We start with a connection
+object, ``conn``, and some convenience functions introduced along the way that
+help us simulate time passing and work being done [#usageSetUp]_.
+
+-------------------
+Obtaining the queue
+-------------------
+
+First, how do we get the queue?  Your installation may have some
+conveniences.  For instance, the Zope 3 configuration described below
+makes it possible to get the primary queue with an adaptation call like
+``zc.async.interfaces.IQueue(a_persistent_object_with_db_connection)``.
+
+But failing that, queues are always expected to be in a zc.async.queue.Queues
+mapping found off the ZODB root in a key defined by the constant
+zc.async.interfaces.KEY.
+
+    >>> import zc.async.interfaces
+    >>> zc.async.interfaces.KEY
+    'zc.async'
+    >>> root = conn.root()
+    >>> queues = root[zc.async.interfaces.KEY]
+    >>> import zc.async.queue
+    >>> isinstance(queues, zc.async.queue.Queues)
+    True
+
+As the name implies, ``queues`` is a collection of queues. As discussed later,
+it's possible to have multiple queues, as a tool to distribute and control
+work. We will assume a convention of a queue being available in the '' (empty
+string).
+
+    >>> queues.keys()
+    ['']
+    >>> queue = queues['']
+
+-------------
+``queue.put``
+-------------
+
+Now we want to actually get some work done.  The simplest case is simple
+to perform: pass a persistable callable to the queue's ``put`` method and
+commit the transaction.
+
+    >>> def send_message():
+    ...     print "imagine this sent a message to another machine"
+    >>> job = queue.put(send_message)
+    >>> import transaction
+    >>> transaction.commit()
+
+Note that this won't really work in an interactive session: the callable needs
+to be picklable, as discussed above, so ``send_message`` would need to be
+a module global, for instance.
+
+The ``put`` returned a job.  Now we need to wait for the job to be
+performed.  We would normally do this by really waiting.  For our
+examples, we will use a helper method on the testing reactor to ``wait_for``
+the job to be completed.
+
+    >>> reactor.wait_for(job)
+    imagine this sent a message to another machine
+
+We also could have used the method of a persistent object.  Here's another
+quick example.
+
+First we define a simple persistent.Persistent subclass and put an instance of
+it in the database [#commit_for_multidatabase]_.
+
+    >>> import persistent
+    >>> class Demo(persistent.Persistent):
+    ...     counter = 0
+    ...     def increase(self, value=1):
+    ...         self.counter += value
+    ...
+    >>> root['demo'] = Demo()
+    >>> transaction.commit()
+
+Now we can put the ``demo.increase`` method in the queue.
+
+    >>> root['demo'].counter
+    0
+    >>> job = queue.put(root['demo'].increase)
+    >>> transaction.commit()
+
+    >>> reactor.wait_for(job)
+    >>> root['demo'].counter
+    1
+
+The method was called, and the persistent object modified!
+
+To reiterate, only pickleable callables such as global functions and the
+methods of persistent objects can be used. This rules out, for instance,
+lambdas and other functions created dynamically. As we'll see below, the job
+instance can help us out there somewhat by offering closure-like features.
+
+-----------------------------------
+``queue.pull`` and ``queue.remove``
+-----------------------------------
+
+If you put a job into a queue and it hasn't been claimed yet and you want to
+cancel the job, ``pull`` or ``remove`` it from the queue.
+
+The ``pull`` method removes the first job, or takes an integer index.
+
+    >>> len(queue)
+    0
+    >>> job1 = queue.put(send_message)
+    >>> job2 = queue.put(send_message)
+    >>> len(queue)
+    2
+    >>> job1 is queue.pull()
+    True
+    >>> list(queue) == [job2]
+    True
+    >>> job1 is queue.put(job1)
+    True
+    >>> list(queue) == [job2, job1]
+    True
+    >>> job1 is queue.pull(-1)
+    True
+    >>> job2 is queue.pull()
+    True
+    >>> len(queue)
+    0
+
+The ``remove`` method removes the specific given job.
+
+    >>> job1 = queue.put(send_message)
+    >>> job2 = queue.put(send_message)
+    >>> len(queue)
+    2
+    >>> queue.remove(job1)
+    >>> list(queue) == [job2]
+    True
+    >>> job1 is queue.put(job1)
+    True
+    >>> list(queue) == [job2, job1]
+    True
+    >>> queue.remove(job1)
+    >>> list(queue) == [job2]
+    True
+    >>> queue.remove(job2)
+    >>> len(queue)
+    0
+
+---------------
+Scheduled Calls
+---------------
+
+When using ``put``, you can also pass a datetime.datetime to schedule a call. A
+datetime without a timezone is considered to be in the UTC timezone.
+
+    >>> t = transaction.begin()
+    >>> import datetime
+    >>> import pytz
+    >>> datetime.datetime.now(pytz.UTC)
+    datetime.datetime(2006, 8, 10, 15, 44, 33, 211, tzinfo=<UTC>)
+    >>> job = queue.put(
+    ...     send_message, begin_after=datetime.datetime(
+    ...         2006, 8, 10, 15, 56, tzinfo=pytz.UTC))
+    >>> job.begin_after
+    datetime.datetime(2006, 8, 10, 15, 56, tzinfo=<UTC>)
+    >>> transaction.commit()
+    >>> reactor.wait_for(job, attempts=2) # +5 virtual seconds
+    TIME OUT
+    >>> reactor.wait_for(job, attempts=2) # +5 virtual seconds
+    TIME OUT
+    >>> datetime.datetime.now(pytz.UTC)
+    datetime.datetime(2006, 8, 10, 15, 44, 43, 211, tzinfo=<UTC>)
+
+    >>> zc.async.testing.set_now(datetime.datetime(
+    ...     2006, 8, 10, 15, 56, tzinfo=pytz.UTC))
+    >>> reactor.wait_for(job)
+    imagine this sent a message to another machine
+    >>> datetime.datetime.now(pytz.UTC) >= job.begin_after
+    True
+
+If you set a time that has already passed, it will be run as if it had
+been set to run as soon as possible [#already_passed]_...unless the job
+has already timed out, in which case the job fails with an
+abort [#already_passed_timed_out]_.
+
+The queue's ``put`` method is the essential API. ``pull`` is used rarely. Other
+methods are used to introspect, but are not needed for basic usage.
+
+But what is that result of the ``put`` call in the examples above?  A
+job?  What do you do with that?
+
+Jobs
+====
+
+--------
+Overview
+--------
+
+The result of a call to ``put`` returns an ``IJob``. The job represents the
+pending result. This object has a lot of functionality that's explored in other
+documents in this package, and demonstrated a bit below, but here's a summary.
+
+- You can introspect, and even modify, the call and its arguments.
+
+- You can specify that the job should be run serially with others of a given
+  identifier.
+
+- You can specify other calls that should be made on the basis of the result of
+  this call.
+
+- You can persist a reference to it, and periodically (after syncing your
+  connection with the database, which happens whenever you begin or commit a
+  transaction) check its ``status`` to see if it is equal to
+  ``zc.async.interfaces.COMPLETED``. When it is, the call has run to completion,
+  either to success or an exception.
+
+- You can look at the result of the call (once ``COMPLETED``). It might be the
+  result you expect, or a ``zc.twist.Failure``, a subclass of
+  ``twisted.python.failure.Failure``, which is a way to safely communicate
+  exceptions across connections and machines and processes.
+
+-------
+Results
+-------
+
+So here's a simple story.  What if you want to get a result back from a
+call?  Look at the job.result after the call is ``COMPLETED``.
+
+    >>> def imaginaryNetworkCall():
+    ...     # let's imagine this makes a network call...
+    ...     return "200 OK"
+    ...
+    >>> job = queue.put(imaginaryNetworkCall)
+    >>> print job.result
+    None
+    >>> job.status == zc.async.interfaces.PENDING
+    True
+    >>> transaction.commit()
+    >>> reactor.wait_for(job)
+    >>> t = transaction.begin()
+    >>> job.result
+    '200 OK'
+    >>> job.status == zc.async.interfaces.COMPLETED
+    True
+
+--------
+Closures
+--------
+
+What's more, you can pass a Job to the ``put`` call.  This means that you
+aren't constrained to simply having simple non-argument calls performed
+asynchronously, but you can pass a job with a call, arguments, and
+keyword arguments--effectively, a kind of closure.  Here's a quick example.
+We'll use the demo object, and its increase method, that we introduced
+above, but this time we'll include some arguments [#job]_.
+
+With positional arguments:
+
+    >>> t = transaction.begin()
+    >>> job = queue.put(
+    ...     zc.async.job.Job(root['demo'].increase, 5))
+    >>> transaction.commit()
+    >>> reactor.wait_for(job)
+    >>> t = transaction.begin()
+    >>> root['demo'].counter
+    6
+
+With keyword arguments (``value``):
+
+    >>> job = queue.put(
+    ...     zc.async.job.Job(root['demo'].increase, value=10))
+    >>> transaction.commit()
+    >>> reactor.wait_for(job)
+    >>> t = transaction.begin()
+    >>> root['demo'].counter
+    16
+
+Note that arguments to these jobs can be any persistable object.
+
+--------
+Failures
+--------
+
+What happens if a call raises an exception?  The return value is a Failure.
+
+    >>> def I_am_a_bad_bad_function():
+    ...     return foo + bar
+    ...
+    >>> job = queue.put(I_am_a_bad_bad_function)
+    >>> transaction.commit()
+    >>> reactor.wait_for(job)
+    >>> t = transaction.begin()
+    >>> job.result
+    <zc.twist.Failure exceptions.NameError>
+
+Failures can provide useful information such as tracebacks.
+
+    >>> print job.result.getTraceback()
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    exceptions.NameError: global name 'foo' is not defined
+    <BLANKLINE>
+
+---------
+Callbacks
+---------
+
+You can register callbacks to handle the result of a job, whether a
+Failure or another result.
+
+Note that, unlike callbacks on a Twisted deferred, these callbacks do not
+change the result of the original job. Since callbacks are jobs, you can chain
+results, but generally callbacks for the same job all get the same result as
+input.
+
+Also note that, during execution of a callback, there is no guarantee that
+the callback will be processed on the same machine as the main call.  Also,
+some of the ``local`` functions, discussed below, will not work as desired.
+
+Here's a simple example of reacting to a success.
+
+    >>> def I_scribble_on_strings(string):
+    ...     return string + ": SCRIBBLED"
+    ...
+    >>> job = queue.put(imaginaryNetworkCall)
+    >>> callback = job.addCallback(I_scribble_on_strings)
+    >>> transaction.commit()
+    >>> reactor.wait_for(job)
+    >>> job.result
+    '200 OK'
+    >>> callback.result
+    '200 OK: SCRIBBLED'
+
+Here's a more complex example of handling a Failure, and then chaining
+a subsequent callback.
+
+    >>> def I_handle_NameErrors(failure):
+    ...     failure.trap(NameError) # see twisted.python.failure.Failure docs
+    ...     return 'I handled a name error'
+    ...
+    >>> job = queue.put(I_am_a_bad_bad_function)
+    >>> callback1 = job.addCallbacks(failure=I_handle_NameErrors)
+    >>> callback2 = callback1.addCallback(I_scribble_on_strings)
+    >>> transaction.commit()
+    >>> reactor.wait_for(job)
+    >>> job.result
+    <zc.twist.Failure exceptions.NameError>
+    >>> callback1.result
+    'I handled a name error'
+    >>> callback2.result
+    'I handled a name error: SCRIBBLED'
+
+Advanced Techniques and Tools
+=============================
+
+**Important**
+
+The job and its functionality described above are the core zc.async tools.
+
+The following are advanced techniques and tools of various complexities. You
+can use zc.async very productively without ever understanding or using them. If
+the following do not make sense to you now, please just move on for now.
+
+--------------
+zc.async.local
+--------------
+
+Jobs always run their callables in a thread, within the context of a
+connection to the ZODB. The callables have access to five special
+thread-local functions if they need them for special uses.  These are
+available off of zc.async.local.
+
+``zc.async.local.getJob()``
+    The ``getJob`` function can be used to examine the job, to get
+    a connection off of ``_p_jar``, to get the queue into which the job
+    was put, or other uses.
+
+``zc.async.local.getQueue()``
+    The ``getQueue`` function can be used to examine the queue, to put another
+    task into the queue, or other uses. It is sugar for
+    ``zc.async.local.getJob().queue``.
+
+``zc.async.local.setLiveAnnotation(name, value, job=None)``
+    The ``setLiveAnnotation`` tells the agent to set an annotation on a job,
+    by default the current job, *in another connection*.  This makes it
+    possible to send messages about progress or for coordination while in the
+    middle of other work.
+
+    As a simple rule, only send immutable objects like strings or
+    numbers as values [#setLiveAnnotation]_.
+
+``zc.async.local.getLiveAnnotation(name, default=None, timeout=0, poll=1, job=None)``
+    The ``getLiveAnnotation`` tells the agent to get an annotation for a job,
+    by default the current job, *from another connection*.  This makes it
+    possible to send messages about progress or for coordination while in the
+    middle of other work.
+
+    As a simple rule, only ask for annotation values that will be
+    immutable objects like strings or numbers [#getLiveAnnotation]_.
+
+    If the ``timeout`` argument is set to a positive float or int, the function
+    will wait at least that number of seconds until an annotation of the
+    given name is available. Otherwise, it will return the ``default`` if the
+    name is not present in the annotations. The ``poll`` argument specifies
+    approximately how often to poll for the annotation, in seconds (to be more
+    precise, a subsequent poll will be min(poll, remaining seconds until
+    timeout) seconds away).
+
+``zc.async.local.getReactor()``
+    The ``getReactor`` function returns the job's dispatcher's reactor.  The
+    ``getLiveAnnotation`` and ``setLiveAnnotation`` functions use this,
+    along with the zc.twist package, to work their magic; if you are feeling
+    adventurous, you can do the same.
+
+``zc.async.local.getDispatcher()``
+    The ``getDispatcher`` function returns the job's dispatcher.  This might
+    be used to analyze its non-persistent poll data structure, for instance
+    (described later in configuration discussions).
+
+Let's give three of those a whirl. We will write a function that examines the
+job's state while it is being called, and sets the state in an annotation, then
+waits for our flag to finish.
+
+    >>> def annotateStatus():
+    ...     zc.async.local.setLiveAnnotation(
+    ...         'zc.async.test.status',
+    ...         zc.async.local.getJob().status)
+    ...     zc.async.local.getLiveAnnotation(
+    ...         'zc.async.test.flag', timeout=5)
+    ...     return 42
+    ...
+    >>> job = queue.put(annotateStatus)
+    >>> transaction.commit()
+    >>> import time
+    >>> def wait_for_annotation(job, key):
+    ...     reactor.time_flies(dispatcher.poll_interval) # starts thread
+    ...     for i in range(10):
+    ...         while reactor.time_passes():
+    ...             pass
+    ...         transaction.begin()
+    ...         if key in job.annotations:
+    ...             break
+    ...         time.sleep(0.1)
+    ...     else:
+    ...         print 'Timed out' + repr(dict(job.annotations))
+    ...
+    >>> wait_for_annotation(job, 'zc.async.test.status')
+    >>> job.annotations['zc.async.test.status'] == (
+    ...     zc.async.interfaces.ACTIVE)
+    True
+    >>> job.status == zc.async.interfaces.ACTIVE
+    True
+
+[#stats_1]_
+
+    >>> job.annotations['zc.async.test.flag'] = True
+    >>> transaction.commit()
+    >>> reactor.wait_for(job)
+    >>> job.result
+    42
+
+[#stats_2]_ ``getReactor`` and ``getDispatcher`` are for advanced use
+cases and are not explored further here.
+
+----------
+Job Quotas
+----------
+
+One class of asynchronous jobs are ideally serialized.  For instance,
+you may want to reduce or eliminate the chance of conflict errors when
+updating a text index.  One way to do this kind of serialization is to
+use the ``quota_names`` attribute of the job.
+
+For example, let's first show two non-serialized jobs running at the
+same time, and then two serialized jobs created at the same time.
+The first part of the example does not use queue_names, to show a contrast.
+
+For our parallel jobs, we'll do something that would create a deadlock
+if they were serial.  Notice that we are mutating the job arguments after
+creation to accomplish this, which is supported.
+
+    >>> def waitForParallel(other):
+    ...     zc.async.local.setLiveAnnotation(
+    ...         'zc.async.test.flag', True)
+    ...     zc.async.local.getLiveAnnotation(
+    ...         'zc.async.test.flag', job=other, timeout=0.4, poll=0)
+    ...
+    >>> job1 = queue.put(waitForParallel)
+    >>> job2 = queue.put(waitForParallel)
+    >>> job1.args.append(job2)
+    >>> job2.args.append(job1)
+    >>> transaction.commit()
+    >>> reactor.wait_for(job1, job2)
+    >>> job1.status == zc.async.interfaces.COMPLETED
+    True
+    >>> job2.status == zc.async.interfaces.COMPLETED
+    True
+    >>> job1.result is job2.result is None
+    True
+
+On the other hand, for our serial jobs, we'll do something that would fail
+if it were parallel.  We'll rely on ``quota_names``.
+
+Quotas verge on configuration, which is not what this section is about,
+because they must be configured on the queue.  However, they also affect
+usage, so we show them here.
+
+    >>> def pause(other):
+    ...     zc.async.local.setLiveAnnotation(
+    ...         'zc.async.test.flag', True)
+    ...     res = zc.async.local.getLiveAnnotation(
+    ...         'zc.async.test.flag', timeout=0.4, poll=0.1, job=other)
+    ...
+    >>> job1 = queue.put(pause)
+    >>> job2 = queue.put(imaginaryNetworkCall)
+
+You can't put a name in ``quota_names`` unless the quota has been created
+in the queue.
+
+    >>> job1.quota_names = ('test',)
+    Traceback (most recent call last):
+    ...
+    ValueError: ('unknown quota name', 'test')
+    >>> queue.quotas.create('test')
+    >>> job1.quota_names = ('test',)
+    >>> job2.quota_names = ('test',)
+
+Now we can see the two jobs being performed serially.
+
+    >>> job1.args.append(job2)
+    >>> transaction.commit()
+    >>> reactor.time_flies(dispatcher.poll_interval)
+    1
+    >>> for i in range(10):
+    ...     t = transaction.begin()
+    ...     if job1.status == zc.async.interfaces.ACTIVE:
+    ...         break
+    ...     time.sleep(0.1)
+    ... else:
+    ...     print 'TIME OUT'
+    ...
+    >>> job2.status == zc.async.interfaces.PENDING
+    True
+    >>> job2.annotations['zc.async.test.flag'] = False
+    >>> transaction.commit()
+    >>> reactor.wait_for(job1)
+    >>> reactor.wait_for(job2)
+    >>> print job1.result
+    None
+    >>> print job2.result
+    200 OK
+
+Quotas can be configured for limits greater than one at a time, if desired.
+This may be valuable when a needed resource is only available in limited
+numbers at a time.
+
+Note that, while quotas are valuable tools for doing serialized work such as
+updating a text index, other optimization features sometimes useful for this
+sort of task, such as collapsing similar jobs, are not provided directly by
+this package. This functionality could be trivially built on top of zc.async,
+however [#idea_for_collapsing_jobs]_.
+
+--------------
+Returning Jobs
+--------------
+
+Our examples so far have done work directly.  What if the job wants to
+orchestrate other work?  One way this can be done is to return another
+job.  The result of the inner job will be the result of the first
+job once the inner job is finished.  This approach can be used to
+break up the work of long running processes; to be more cooperative to
+other jobs; and to make parts of a job that can be parallelized available
+to more workers.
+
+Serialized Work
+---------------
+
+First, consider a serialized example.  This simple pattern is one approach.
+
+    >>> def second_job(value):
+    ...     # imagine a lot of work goes on...
+    ...     return value * 2
+    ...
+    >>> def first_job():
+    ...     # imagine a lot of work goes on...
+    ...     intermediate_value = 21
+    ...     queue = zc.async.local.getJob().queue
+    ...     return queue.put(zc.async.job.Job(
+    ...         second_job, intermediate_value))
+    ...
+    >>> job = queue.put(first_job)
+    >>> transaction.commit()
+    >>> reactor.wait_for(job, attempts=3)
+    TIME OUT
+    >>> len(agent)
+    1
+    >>> reactor.wait_for(job, attempts=3)
+    >>> job.result
+    42
+
+The job is now out of the agent.
+
+    >>> len(agent)
+    0
+
+The second_job could also have returned a job, allowing for additional
+legs.  Once the last job returns a real result, it will cascade through the
+past jobs back up to the original one.
+
+A different approach could have used callbacks.  Using callbacks can be
+somewhat more complicated to follow, but can allow for a cleaner
+separation of code: dividing code that does work from code that orchestrates
+the jobs. The ``serial`` helper function in the job module uses this pattern.
+Here's a quick example of the helper function [#define_longer_wait]_.
+
+    >>> def job_zero():
+    ...     return 0
+    ...
+    >>> def job_one():
+    ...     return 1
+    ...
+    >>> def job_two():
+    ...     return 2
+    ...
+    >>> def postprocess(zero, one, two):
+    ...     return zero.result, one.result, two.result
+    ...
+    >>> job = queue.put(zc.async.job.serial(job_zero, job_one, job_two,
+    ...                                     postprocess=postprocess))
+    >>> transaction.commit()
+
+    >>> wait_repeatedly()
+    ... # doctest: +ELLIPSIS
+    TIME OUT...
+
+    >>> job.result
+    (0, 1, 2)
+
+[#extra_serial_tricks]_
+
+The ``parallel`` example we use below follows a similar pattern.
+
+Parallelized Work
+-----------------
+
+Now how can we set up parallel jobs?  There are other good ways, but we
+can describe one way that avoids potential problems with the
+current-as-of-this-writing (ZODB 3.8 and trunk) default optimistic MVCC
+serialization behavior in the ZODB.  The solution uses callbacks, which
+also allows us to cleanly divide the "work" code from the synchronization
+code, as described in the previous paragraph.
+
+First, we'll define the jobs that do work.  ``job_A``, ``job_B``, and
+``job_C`` will be jobs that can be done in parallel, and
+``postprocess`` will be a function that assembles the job results for a
+final result.
+
+    >>> def job_A():
+    ...     # imaginary work...
+    ...     return 7
+    ...
+    >>> def job_B():
+    ...     # imaginary work...
+    ...     return 14
+    ...
+    >>> def job_C():
+    ...     # imaginary work...
+    ...     return 21
+    ...
+    >>> def postprocess(*jobs):
+    ...     # this callable represents one that needs to wait for the
+    ...     # parallel jobs to be done before it can process them and return
+    ...     # the final result
+    ...     return sum(job.result for job in jobs)
+    ...
+
+This can be handled by a convenience function, ``parallel``, that will arrange
+everything for you.
+
+    >>> job = queue.put(zc.async.job.parallel(
+    ...     job_A, job_B, job_C, postprocess=postprocess))
+    >>> transaction.commit()
+
+Now we just wait for the result.
+
+    >>> wait_repeatedly()
+    ... # doctest: +ELLIPSIS
+    TIME OUT...
+
+    >>> job.result
+    42
+
+Ta-da! [#extra_parallel_tricks]_
+
+Now, how did this work?  Let's look at a simple implementation directly.  We'll
+use a slightly different postprocess, that expects results directly rather than
+the jobs.
+
+    >>> def postprocess(*results):
+    ...     # this callable represents one that needs to wait for the
+    ...     # parallel jobs to be done before it can process them and return
+    ...     # the final result
+    ...     return sum(results)
+    ...
+
+This code works with jobs to get everything done. Note, in the callback
+function, that mutating the same object we are checking (job.args) is the way
+we are enforcing necessary serializability with MVCC turned on.
+
+    >>> def callback(job, result):
+    ...     job.args.append(result)
+    ...     if len(job.args) == 3: # all results are in
+    ...         zc.async.local.getJob().queue.put(job)
+    ...
+    >>> def main_job():
+    ...     job = zc.async.job.Job(postprocess)
+    ...     queue = zc.async.local.getJob().queue
+    ...     for j in (job_A, job_B, job_C):
+    ...         queue.put(j).addCallback(
+    ...             zc.async.job.Job(callback, job))
+    ...     return job
+    ...
+
+That may be a bit mind-blowing at first.  The trick to catch here is that,
+because the main_job returns a job, the result of that job will become the
+result of the main_job once the returned (``post_process``) job is done.
+
+Now we'll put this in and let it cook.
+
+    >>> job = queue.put(main_job)
+    >>> transaction.commit()
+
+    >>> wait_repeatedly()
+    ... # doctest: +ELLIPSIS
+    TIME OUT...
+    >>> job.result
+    42
+
+Once again, ta-da!
+
+For real-world usage, you'd also probably want to deal with the possibility of
+one or more of the jobs generating a Failure, among other edge cases.  The
+``parallel`` function introduced above helps you handle this by returning
+jobs, rather than results, so you can analyze what went wrong and try to handle
+it.
+
+-------------------
+Returning Deferreds
+-------------------
+
+What if you want to do work that doesn't require a ZODB connection?  You
+can also return a Twisted deferred (twisted.internet.defer.Deferred).
+When you then ``callback`` the deferred with the eventual result, the
+agent will be responsible for setting that value on the original
+deferred and calling its callbacks.  This can be a useful trick for
+making network calls using Twisted or zc.ngi, for instance.
+
+    >>> def imaginaryNetworkCall2(deferred):
+    ...     # make a network call...
+    ...     deferred.callback('200 OK')
+    ...
+    >>> import twisted.internet.defer
+    >>> import threading
+    >>> def delegator():
+    ...     deferred = twisted.internet.defer.Deferred()
+    ...     t = threading.Thread(
+    ...         target=imaginaryNetworkCall2, args=(deferred,))
+    ...     t.run()
+    ...     return deferred
+    ...
+    >>> job = queue.put(delegator)
+    >>> transaction.commit()
+    >>> reactor.wait_for(job)
+    >>> job.result
+    '200 OK'
+
+Conclusion
+==========
+
+This concludes our discussion of zc.async usage. The :ref:`next section
+<configuration-without-zope-3>` shows how to configure zc.async without
+Zope 3 [#stop_usage_reactor]_.
+
+.. _`next section`: :ref:`configuration-without-zope-3`
+
+.. rubric:: Footnotes
+
+.. [#usageSetUp] We set up the configuration for our usage examples here.
+
+    You must have two adapter registrations: IConnection to
+    ITransactionManager, and IPersistent to IConnection.  We will also
+    register IPersistent to ITransactionManager because the adapter is
+    designed for it.
+
+    We also need to be able to get data manager partials for functions and
+    methods; normal partials for functions and methods; and a data manager for
+    a partial. Here are the necessary registrations.
+
+    The dispatcher will look for a UUID utility, so we also need one of these.
+
+    The ``zc.async.configure.base`` function performs all of these
+    registrations. If you are working with zc.async without ZCML you might want
+    to use it or ``zc.async.configure.minimal`` as a convenience.
+
+    >>> import zc.async.configure
+    >>> zc.async.configure.base()
+
+    Now we'll set up the database, and make some policy decisions.  As
+    the subsequent ``configuration`` sections discuss, some helpers are
+    available for you to set this up if you'd like, though it's not too
+    onerous to do it by hand.
+
+    We'll use a test reactor that we can control.
+
+    >>> import zc.async.testing
+    >>> reactor = zc.async.testing.Reactor()
+    >>> reactor.start() # this monkeypatches datetime.datetime.now
+
+    We need to instantiate the dispatcher with a reactor and a DB.  We
+    have the reactor, so here is the DB.  We use a FileStorage rather
+    than a MappingStorage variant typical in tests and examples because
+    we want MVCC.
+
+    >>> import ZODB.FileStorage
+    >>> storage = ZODB.FileStorage.FileStorage(
+    ...     'zc_async.fs', create=True)
+    >>> from ZODB.DB import DB
+    >>> db = DB(storage)
+    >>> conn = db.open()
+    >>> root = conn.root()
+
+    Now let's create the mapping of queues, and a single queue.
+
+    >>> import zc.async.queue
+    >>> import zc.async.interfaces
+    >>> mapping = root[zc.async.interfaces.KEY] = zc.async.queue.Queues()
+    >>> queue = mapping[''] = zc.async.queue.Queue()
+    >>> import transaction
+    >>> transaction.commit()
+
+    Now we can instantiate, activate, and perform some reactor work in order
+    to let the dispatcher register with the queue.
+
+    >>> import zc.async.dispatcher
+    >>> dispatcher = zc.async.dispatcher.Dispatcher(db, reactor)
+    >>> dispatcher.activate()
+    >>> reactor.time_flies(1)
+    1
+
+    The UUID is set on the dispatcher.
+
+    >>> import zope.component
+    >>> import zc.async.interfaces
+    >>> UUID = zope.component.getUtility(zc.async.interfaces.IUUID)
+    >>> dispatcher.UUID == UUID
+    True
+
+    Here's an agent named 'main'
+
+    >>> import zc.async.agent
+    >>> agent = zc.async.agent.Agent()
+    >>> queue.dispatchers[dispatcher.UUID]['main'] = agent
+    >>> agent.filter is None
+    True
+    >>> agent.size
+    3
+    >>> transaction.commit()
+
+.. [#commit_for_multidatabase] We commit before we do the next step as a
+    good practice, in case the queue is from a different database than
+    the root.  See the configuration sections for a discussion about
+    why putting the queue in another database might be a good idea.
+
+    Rather than committing the transaction,
+    ``root._p_jar.add(root['demo'])`` would also accomplish the same
+    thing from a multi-database perspective, without a commit.  It was
+    not used in the example because the author judged the
+    ``transaction.commit()`` to be less jarring to the reader.  If you
+    are down here reading this footnote, maybe the author was wrong. :-)
+
+.. [#already_passed]
+
+    >>> t = transaction.begin()
+    >>> job = queue.put(
+    ...     send_message, datetime.datetime(2006, 8, 10, 15, tzinfo=pytz.UTC))
+    >>> transaction.commit()
+    >>> reactor.wait_for(job)
+    imagine this sent a message to another machine
+
+    It's worth noting that this situation constitutes a small exception
+    in the handling of scheduled calls.  Scheduled calls usually get
+    preference when jobs are handed out over normal non-scheduled "as soon as
+    possible" jobs.  However, setting the begin_after date to an earlier
+    time puts the job at the end of the (usually) FIFO queue of non-scheduled
+    tasks: it is treated exactly as if the date had not been specified.
+
+.. [#already_passed_timed_out]
+
+    >>> t = transaction.begin()
+    >>> job = queue.put(
+    ...     send_message, datetime.datetime(2006, 7, 21, 12, tzinfo=pytz.UTC),
+    ...     datetime.timedelta(hours=1))
+    >>> transaction.commit()
+    >>> reactor.wait_for(job)
+    >>> job.result
+    <zc.twist.Failure zc.async.interfaces.TimeoutError>
+    >>> import sys
+    >>> job.result.printTraceback(sys.stdout) # doctest: +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    Failure: zc.async.interfaces.TimeoutError:
+
+.. [#job] The Job class can take arguments and keyword arguments
+    for the wrapped callable at call time as well, similar to Python
+    2.5's `partial`.  This will be important when we use the Job as
+    a callback.  For this use case, though, realize that the job
+    will be called with no arguments, so you must supply all necessary
+    arguments for the callable at creation time.
+
+.. [#setLiveAnnotation]  Here's the real rule, which is more complex.
+    *Do not send non-persistent mutables or a persistent.Persistent
+    object without a connection, unless you do not refer to it again in
+    the current job.*
+
+.. [#getLiveAnnotation] Here's the real rule. *To prevent surprising
+    errors, do not request an annotation that might be a persistent
+    object.*
+
+.. [#stats_1] The dispatcher has a getStatistics method.  It also shows the
+    fact that there is an active task.
+
+    >>> import pprint
+    >>> pprint.pprint(dispatcher.getStatistics()) # doctest: +ELLIPSIS
+    {'failed': 2,
+     'longest active': (..., 'unnamed'),
+     'longest failed': (..., 'unnamed'),
+     'longest successful': (..., 'unnamed'),
+     'shortest active': (..., 'unnamed'),
+     'shortest failed': (..., 'unnamed'),
+     'shortest successful': (..., 'unnamed'),
+     'started': 12,
+     'statistics end': datetime.datetime(2006, 8, 10, 15, 44, 22, 211),
+     'statistics start': datetime.datetime(2006, 8, 10, 15, 56, 47, 211),
+     'successful': 9,
+     'unknown': 0}
+
+    We can also see the active job with ``getActiveJobIds``
+
+    >>> job_ids = dispatcher.getActiveJobIds()
+    >>> len(job_ids)
+    1
+    >>> info = dispatcher.getJobInfo(*job_ids[0])
+    >>> pprint.pprint(info) # doctest: +ELLIPSIS
+    {'agent': 'main',
+     'call': "<zc.async.job.Job (oid ..., db 'unnamed') ``zc.async.doctest_test.annotateStatus()``>",
+     'completed': None,
+     'failed': False,
+     'poll id': ...,
+     'queue': '',
+     'quota names': (),
+     'reassigned': False,
+     'result': None,
+     'started': datetime.datetime(...),
+     'thread': ...}
+     >>> info['thread'] is not None
+     True
+     >>> info['poll id'] is not None
+     True
+
+
+.. [#stats_2] Now the task is done, as the stats reflect.
+
+    >>> pprint.pprint(dispatcher.getStatistics()) # doctest: +ELLIPSIS
+    {'failed': 2,
+     'longest active': None,
+     'longest failed': (..., 'unnamed'),
+     'longest successful': (..., 'unnamed'),
+     'shortest active': None,
+     'shortest failed': (..., 'unnamed'),
+     'shortest successful': (..., 'unnamed'),
+     'started': 12,
+     'statistics end': datetime.datetime(2006, 8, 10, 15, 44, 22, 211),
+     'statistics start': datetime.datetime(2006, 8, 10, 15, 56, 52, 211),
+     'successful': 10,
+     'unknown': 0}
+
+    Note that these statistics eventually rotate out. By default, poll info
+    will eventually rotate out after about 30 minutes (400 polls), and job info
+    will only keep the most recent 200 stats in-memory. To look in history
+    beyond these limits, check your logs.
+
+    The ``getActiveJobIds`` list is empty now.
+
+    >>> dispatcher.getActiveJobIds()
+    []
+    >>> info = dispatcher.getJobInfo(*job_ids[0])
+    >>> pprint.pprint(info) # doctest: +ELLIPSIS
+    {'agent': 'main',
+     'call': "<zc.async.job.Job (oid ..., db 'unnamed') ``zc.async.doctest_test.annotateStatus()``>",
+     'completed': datetime.datetime(...),
+     'failed': False,
+     'poll id': ...,
+     'queue': '',
+     'quota names': (),
+     'reassigned': False,
+     'result': '42',
+     'started': datetime.datetime(...),
+     'thread': ...}
+
+     >>> info['thread'] is not None
+     True
+     >>> info['poll id'] is not None
+     True
+
+.. [#idea_for_collapsing_jobs] For instance, here is one approach.  Imagine
+    you are queueing the job of indexing documents. If the same document has a
+    request to index, the job could simply walk the queue and remove (``pull``)
+    similar tasks, perhaps aggregating any necessary data. Since the jobs are
+    serial because of a quota, no other worker should be trying to work on
+    those jobs.
+
+    Alternatively, you could use a standalone, non-zc.async queue of things to
+    do, and have the zc.async job just pull from that queue.  You might use
+    zc.queue for this stand-alone queue, or zc.catalogqueue.
+
+.. [#define_longer_wait]
+    >>> def wait_repeatedly():
+    ...     for i in range(10):
+    ...         reactor.wait_for(job, attempts=3)
+    ...         if job.status == zc.async.interfaces.COMPLETED:
+    ...             break
+    ...     else:
+    ...         assert False, 'never completed'
+    ...
+
+.. [#extra_serial_tricks] The ``serial`` helper can accept a partial closure
+    for a ``postprocess`` argument.
+
+    >>> def postprocess(extra_info, *jobs):
+    ...     return extra_info, tuple(j.result for j in jobs)
+    ...
+    >>> job = queue.put(zc.async.job.serial(
+    ...     job_zero, job_one, job_two,
+    ...     postprocess=zc.async.job.Job(postprocess, 'foo')))
+    >>> transaction.commit()
+
+    >>> wait_repeatedly()
+    ... # doctest: +ELLIPSIS
+    TIME OUT...
+
+    >>> job.result
+    ('foo', (0, 1, 2))
+
+    The list of jobs can be extended by adding them to the args of the job
+    returned by ``serial`` under these circumstances:
+
+    - before the job has started,
+
+    - by an inner job while it is running, or
+
+    - by any callback added to any inner job *before* that inner job has begun.
+
+    Here's an example.
+
+    >>> def postprocess(*jobs):
+    ...     return [j.result for j in jobs]
+    ...
+    >>> job = queue.put(zc.async.job.serial(postprocess=postprocess))
+    >>> def second_job():
+    ...     return 'second'
+    ...
+    >>> def third_job():
+    ...     return 'third'
+    ...
+    >>> def schedule_third(main_job, ignored):
+    ...     main_job.args.append(zc.async.job.Job(third_job))
+    ...
+    >>> def first_job(main_job):
+    ...     j = zc.async.job.Job(second_job)
+    ...     main_job.args.append(j)
+    ...     j.addCallback(zc.async.job.Job(schedule_third, main_job))
+    ...     return 'first'
+    ...
+    >>> job.args.append(zc.async.job.Job(first_job, job))
+    >>> transaction.commit()
+
+    >>> wait_repeatedly()
+    ... # doctest: +ELLIPSIS
+    TIME OUT...
+
+    >>> job.result
+    ['first', 'second', 'third']
+
+    Be warned, these sort of constructs allow infinite loops!
+
+.. [#extra_parallel_tricks] The ``parallel`` helper can accept a partial closure
+    for a ``postprocess`` argument.
+
+    >>> def postprocess(extra_info, *jobs):
+    ...     return extra_info, sum(j.result for j in jobs)
+    ...
+    >>> job = queue.put(zc.async.job.parallel(
+    ...     job_A, job_B, job_C,
+    ...     postprocess=zc.async.job.Job(postprocess, 'foo')))
+
+    >>> transaction.commit()
+
+    >>> wait_repeatedly()
+    ... # doctest: +ELLIPSIS
+    TIME OUT...
+
+    >>> job.result
+    ('foo', 42)
+
+    The list of jobs can be extended by adding them to the args of the job
+    returned by ``parallel`` under these circumstances:
+
+    - before the job has started,
+
+    - by an inner job while it is running,
+
+    - by any callback added to any inner job *before* that inner job has begun.
+
+    Here's an example.
+
+    >>> def postprocess(*jobs):
+    ...     return [j.result for j in jobs]
+    ...
+    >>> job = queue.put(zc.async.job.parallel(postprocess=postprocess))
+    >>> def second_job():
+    ...     return 'second'
+    ...
+    >>> def third_job():
+    ...     return 'third'
+    ...
+    >>> def schedule_third(main_job, ignored):
+    ...     main_job.args.append(zc.async.job.Job(third_job))
+    ...
+    >>> def first_job(main_job):
+    ...     j = zc.async.job.Job(second_job)
+    ...     main_job.args.append(j)
+    ...     j.addCallback(zc.async.job.Job(schedule_third, main_job))
+    ...     return 'first'
+    ...
+    >>> job.args.append(zc.async.job.Job(first_job, job))
+    >>> transaction.commit()
+
+    >>> wait_repeatedly()
+    ... # doctest: +ELLIPSIS
+    TIME OUT...
+
+    >>> job.result
+    ['first', 'second', 'third']
+
+    As with ``serial``, be warned, these sort of constructs allow infinite
+    loops!
+
+.. [#stop_usage_reactor]
+
+    >>> threads = []
+    >>> for queue_pools in dispatcher.queues.values():
+    ...     for pool in queue_pools.values():
+    ...         threads.extend(pool.threads)
+    >>> reactor.stop()
+    >>> zc.async.testing.wait_for_deactivation(dispatcher)
+    >>> for thread in threads:
+    ...     thread.join(3)
+    ...
+    >>> pprint.pprint(dispatcher.getStatistics()) # doctest: +ELLIPSIS
+    {'failed': 2,
+     'longest active': None,
+     'longest failed': (..., 'unnamed'),
+     'longest successful': (..., 'unnamed'),
+     'shortest active': None,
+     'shortest failed': (..., 'unnamed'),
+     'shortest successful': (..., 'unnamed'),
+     'started': 54,
+     'statistics end': datetime.datetime(2006, 8, 10, 15, 44, 22, 211),
+     'statistics start': datetime.datetime(2006, 8, 10, 16, ...),
+     'successful': 52,
+     'unknown': 0}


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/README_1.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/README_2.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/README_2.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/README_2.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,724 @@
+.. _configuration-without-zope-3:
+
+==============================
+Configuration (without Zope 3)
+==============================
+
+This section discusses setting up zc.async without Zope 3. Since Zope 3 is
+ill-defined, we will be more specific: this describes setting up zc.async
+without ZCML, without any zope.app packages, and with as few dependencies as
+possible. A casual way of describing the dependencies is "ZODB, Twisted, and
+zope.component," though we directly depend on some smaller packages and
+indirectly on others [#specific_dependencies]_.
+
+You may have one or two kinds of configurations for your software using
+zc.async. The simplest approach is to have all processes able both to put items
+in queues, and to perform them with a dispatcher. You can then use on-the-fly
+ZODB configuration to determine what jobs, if any, each process' dispatcher
+performs. If a dispatcher has no agents in a given queue, as we'll discuss
+below, the dispatcher will not perform any job for that queue.
+
+However, if you want to create some processes that can only put items in a
+queue, and do not have a dispatcher at all, that is easy to do. We'll call this
+a "client" process, and the full configuration a "client/server process". As
+you might expect, the configuration of a client process is a subset of the
+configuration of the client/server process.
+
+The ``zc.async.configure`` module helps with basic configuration.  The
+:ref:`quickstart-with-virtualenv` shows an example of using this for a very
+:quick start.  The current text uses some of those conveniences, but focuses
+more on understanding the underlying patterns, rather than the conveniences.
+
+We will first describe setting up a client, non-dispatcher process, in which
+you only can put items in a zc.async queue; and then describe setting up a
+dispatcher client/server process that can be used both to request and to
+perform jobs.
+
+Configuring a Client Process
+============================
+
+Generally, zc.async configuration has four basic parts: component
+registrations, ZODB setup, ZODB configuration, and process configuration.  For
+a client process, we'll discuss required component registrations; ZODB
+setup;  minimal ZODB configuration; process configuration; and then circle
+back around for some optional component registrations.
+
+--------------------------------
+Required Component Registrations
+--------------------------------
+
+The required registrations can be installed for you by the
+``zc.async.configure.base`` function. Most other examples in this package,
+such as those in the :ref:`usage` section, use this in their
+test setup.
+
+Again, for a quick start, you might just want to use the helper
+``zc.async.configure.base`` function, and move on to the `Required ZODB Set
+Up`_ section below.
+
+Here, though, we will go over each required registration to briefly explain
+what they are.
+
+You must have three adapter registrations: IConnection to
+ITransactionManager, IPersistent to IConnection, and IPersistent to
+ITransactionManager.
+
+The ``zc.twist`` package provides all of these adapters.  However,
+zope.app.keyreference also provides a version of the ``connection`` adapter
+that is identical or very similar, and that should work fine if you are
+already using that package in your application.
+
+    >>> import zc.twist
+    >>> import zope.component
+    >>> zope.component.provideAdapter(zc.twist.transactionManager)
+    >>> zope.component.provideAdapter(zc.twist.connection)
+    >>> import ZODB.interfaces
+    >>> zope.component.provideAdapter(
+    ...     zc.twist.transactionManager, adapts=(ZODB.interfaces.IConnection,))
+
+We also need to be able to adapt functions and methods to jobs.  The
+zc.async.job.Job class is the expected implementation.
+
+    >>> import types
+    >>> import zc.async.interfaces
+    >>> import zc.async.job
+    >>> zope.component.provideAdapter(
+    ...     zc.async.job.Job,
+    ...     adapts=(types.FunctionType,),
+    ...     provides=zc.async.interfaces.IJob)
+    >>> zope.component.provideAdapter(
+    ...     zc.async.job.Job,
+    ...     adapts=(types.MethodType,),
+    ...     provides=zc.async.interfaces.IJob)
+    >>> zope.component.provideAdapter( # optional, rarely used
+    ...     zc.async.job.Job,
+    ...     adapts=(zc.twist.METHOD_WRAPPER_TYPE,),
+    ...     provides=zc.async.interfaces.IJob)
+
+The queue looks for the UUID utility to set the ``assignerUUID`` job attribute,
+and may want to use it to optionally filter jobs during ``claim`` in the
+future. Also, the dispatcher will look for a UUID utility if a UUID is not
+specifically provided to its constructor.
+
+    >>> from zc.async.instanceuuid import UUID
+    >>> zope.component.provideUtility(
+    ...     UUID, zc.async.interfaces.IUUID, '')
+
+The UUID we register here is a UUID of the instance, which is expected
+to uniquely identify the process when in production. It is stored in
+the file specified by the ``ZC_ASYNC_UUID`` environment variable (or in
+``os.join(os.getcwd(), 'uuid.txt')`` if this is not specified, for easy
+initial experimentation with the package).
+
+    >>> import uuid
+    >>> import os
+    >>> f = open(os.environ["ZC_ASYNC_UUID"])
+    >>> uuid_hex = f.readline().strip()
+    >>> f.close()
+    >>> uuid = uuid.UUID(uuid_hex)
+    >>> UUID == uuid
+    True
+
+The uuid.txt file is intended to stay in the instance home as a persistent
+identifier.
+
+Again, all of the required registrations above can be accomplished quickly with
+``zc.async.configure.base``.
+
+--------------------
+Required ZODB Set Up
+--------------------
+
+On a basic level, zc.async needs a setup that supports good conflict
+resolution.  Most or all production ZODB storages now have the necessary
+APIs to support MVCC.
+
+Of course, if you want to run multiple processes, you need ZEO. You should also
+then make sure that your ZEO server installation has all the code that includes
+conflict resolution, such as zc.queue, because, as of this writing, conflict
+resolution happens in the ZEO server, not in clients.
+
+A more subtle decision is whether to use multiple databases.  The zc.async
+dispatcher can generate a lot of database churn.  It may be wise to put the
+queue in a separate database from your content database(s).
+
+The downsides to this option include the fact that you must be careful to
+specify to which database objects belong; and that broken cross-database
+references are not handled gracefully in the ZODB as of this writing.
+
+We will use multiple databases for our example here, because we are trying to
+demonstrate production-quality examples. We will show this with a pure-Python
+approach, rather than the ZConfig approach usually used by Zope. If you know
+ZConfig, that will be a reasonable approach as well; see zope.app.appsetup
+for how Zope uses ZConfig to set up multidatabases.
+
+In our example, we create two file storages. In production, you might likely
+use ZEO; hooking ClientStorage up instead of FileStorage should be straight
+forward.
+
+    >>> databases = {}
+    >>> import ZODB.FileStorage
+    >>> storage = ZODB.FileStorage.FileStorage(
+    ...     'main.fs', create=True)
+
+    >>> async_storage = ZODB.FileStorage.FileStorage(
+    ...     'async.fs', create=True)
+
+    >>> from ZODB.DB import DB
+    >>> databases[''] = db = DB(storage)
+    >>> databases['async'] = async_db = DB(async_storage)
+    >>> async_db.databases = db.databases = databases
+    >>> db.database_name = ''
+    >>> async_db.database_name = 'async'
+    >>> conn = db.open()
+    >>> root = conn.root()
+
+------------------
+ZODB Configuration
+------------------
+
+A Queue
+-------
+
+All we must have for a client to be able to put jobs in a queue is ... a queue.
+
+For a quick start, the ``zc.async.subscribers`` module provides a subscriber to
+a DatabaseOpened event that does the right dance. See
+``multidb_queue_installer`` and ``queue_installer`` in that module, and you can
+see that in use in :ref:`configuration-with-zope-3`. For now, though, we're taking
+things step by step and explaining what's going on.
+
+Dispatchers look for queues in a mapping off the root of the database in
+a key defined as a constant: zc.async.interfaces.KEY.  This mapping should
+generally be a zc.async.queue.Queues object.
+
+If we were not using a multi-database for our example, we could simply install
+the queues mapping with this line:
+``root[zc.async.interfaces.KEY] = zc.async.queue.Queues()``.  We will need
+something a bit more baroque.  We will add the queues mapping to the 'async'
+database, and then make it available in the main database ('') with the proper
+key.
+
+    >>> conn2 = conn.get_connection('async')
+    >>> import zc.async.queue
+    >>> queues = conn2.root()['mounted_queues'] = zc.async.queue.Queues()
+
+Note that the 'mounted_queues' key in the async database is arbitrary:
+what we care about is the key in the database that the dispatcher will
+see.
+
+Now we add the object explicitly to conn2, so that the ZODB will know the
+"real" database in which the object lives, even though it will be also
+accessible from the main database.
+
+    >>> conn2.add(queues)
+    >>> root[zc.async.interfaces.KEY] = queues
+    >>> import transaction
+    >>> transaction.commit()
+
+Now we need to put a queue in the queues collection.  We can have more than
+one, as discussed below, but we suggest a convention of the primary queue
+being available in a key of '' (empty string).
+
+    >>> queue = queues[''] = zc.async.queue.Queue()
+    >>> transaction.commit()
+
+Quotas
+------
+
+We touched on quotas in the usage section.  Some jobs will need to
+access resources that are shared across processes.  A central data
+structure such as an index in the ZODB is a prime example, but other
+examples might include a network service that only allows a certain
+number of concurrent connections.  These scenarios can be helped by
+quotas.
+
+Quotas are demonstrated in the usage section.  For configuration, you
+should know these characteristics:
+
+- you cannot add a job with a quota name that is not defined in the
+  queue [#undefined_quota_name]_;
+
+- you cannot add a quota name to a job in a queue if the quota name is not
+  defined in the queue [#no_mutation_to_undefined]_;
+
+- you can create and remove quotas on the queue [#create_remove_quotas]_;
+
+- you can remove quotas if pending jobs have their quota names--the quota name
+  is then ignored [#remove_quotas]_;
+
+- quotas default to a size of 1 [#default_size]_;
+
+- this can be changed at creation or later [#change_size]_; and
+
+- decreasing the size of a quota while the old quota size is filled will
+  not affect the currently running jobs [#decreasing_affects_future]_.
+
+Multiple Queues
+---------------
+
+Since we put our queues in a mapping of them, we can also create multiple
+queues.  This can make some scenarios more convenient and simpler to reason
+about.  For instance, while you might have agents filtering jobs as we
+describe above, it might be simpler to say that you have a queue for one kind
+of job--say, processing a video file or an audio file--and a queue for other
+kinds of jobs.  Then it is easy and obvious to set up simple FIFO agents
+as desired for different dispatchers.  The same kind of logic could be
+accomplished with agents, but it is easier to picture the multiple queues.
+
+Another use case for multiple queues might be for specialized queues, like ones
+that broadcast jobs. You could write a queue subclass that broadcasts copies of
+jobs they get to all dispatchers, aggregating results.  This could be used to
+send "events" to all processes, or to gather statistics on certain processes,
+and so on.
+
+Generally, any time the application wants to be able to assert a kind of job
+rather than letting the agents decide what to do, having separate queues is
+a reasonable tool.
+
+---------------------
+Process Configuration
+---------------------
+
+Daemonization
+-------------
+
+You often want to daemonize your software, so that you can restart it if
+there's a problem, keep track of it and monitor it, and so on.  ZDaemon
+(http://pypi.python.org/pypi/zdaemon) and Supervisor (http://supervisord.org/)
+are two fairly simple-to-use ways of doing this for both client and
+client/server processes. If your main application can be packaged as a
+setuptools distribution (egg or source release or even development egg) then
+you can have your main application as a zc.async client and your dispatchers
+running a separate zc.async-only main loop that simply includes your main
+application as a dependency, so the necessary software is around. You may have
+to do a bit more configuration on the client/server side to mimic global
+registries such as zope.component registrations and so on between the client
+and the client/servers, but this shouldn't be too bad.
+
+UUID File Location
+------------------
+
+As discussed above, the instanceuuid module will look for an environmental
+variable ``ZC_ASYNC_UUID`` to find the file name to use, and failing that will
+use ``os.join(os.getcwd(), 'uuid.txt')``.  It's worth noting that daemonization
+tools such as ZDaemon and Supervisor (3 or greater) make setting environment
+values for child processes an easy (and repeatable) configuration file setting.
+
+-----------------------------------------------------
+Optional Component Registrations for a Client Process
+-----------------------------------------------------
+
+The only optional component registration potentially valuable for client
+instances that only put jobs in the queue is registering an adapter from
+persistent objects to a queue.  The ``zc.async.queue.getDefaultQueue`` adapter
+does this for an adapter to the queue named '' (empty string).  Since that's
+what we have from the `ZODB Configuration`_ above section, we'll register it.
+Writing your own adapter is trivial, as you can see if you look at the
+implementation of this function.
+
+    >>> zope.component.provideAdapter(zc.async.queue.getDefaultQueue)
+    >>> zc.async.interfaces.IQueue(root) is queue
+    True
+
+Configuring a Client/Server Process
+===================================
+
+Configuring a client/server process--something that includes a running
+dispatcher--means doing everything described above, plus a bit more.  You
+need to set up and start a reactor and dispatcher; configure agents as desired
+to get the dispatcher to do some work; and optionally configure logging.
+
+For a quick start, the ``zc.async.subscribers`` module has some conveniences
+to start a threaded reactor and dispatcher, and to install agents.  You might
+want to look at those to get started.  They are also used in the Zope 3
+configuration (README_3).  Meanwhile, this document continues to go
+step-by-step instead, to try and explain the components and configuration.
+
+Even though it seems reasonable to first start a dispatcher and then set up its
+agents, we'll first define a subscriber to create an agent. As we'll see below,
+the dispatcher fires an event when it registers with a queue, and another when
+it activates the queue. These events give you the opportunity to register
+subscribers to add one or more agents to a queue, to tell the dispatcher what
+jobs to perform. zc.async.agent.addMainAgentActivationHandler is a reasonable
+starter: it adds a single agent named 'main' if one does not exist. The agent
+has a simple indiscriminate FIFO policy for the queue. If you want to write
+your own subscriber, look at this, or at the more generic subscriber in the
+``zc.async.subscribers`` module.
+
+Agents are an important part of the ZODB configuration, and so are described
+more in depth below.
+
+    >>> import zc.async.agent
+    >>> zope.component.provideHandler(
+    ...     zc.async.agent.addMainAgentActivationHandler)
+
+This subscriber is registered for the IDispatcherActivated event; another
+approach might use the IDispatcherRegistered event.
+
+-----------------------
+Starting the Dispatcher
+-----------------------
+
+Now we can start the reactor, and start the dispatcher.
+In some applications this may be done with an event subscriber to
+DatabaseOpened, as is done in ``zc.async.subscribers``. Here, we will do it
+inline.
+
+Any object that conforms to the specification of zc.async.interfaces.IReactor
+will be usable by the dispatcher.  For our example, we will use our own instance
+of the Twisted select-based reactor running in a separate thread.  This is
+separate from the Twisted reactor installed in twisted.internet.reactor, and
+so this approach can be used with an application that does not otherwise use
+Twisted (for instance, a Zope application using the "classic" zope publisher).
+
+The testing module also has a reactor on which the `Usage` section relies, if
+you would like to see a minimal contract.
+
+Configuring the basics is fairly simple, as we'll see in a moment.  The
+trickiest part is to handle signals cleanly. It is also optional! The
+dispatcher will eventually figure out that there was not a clean shut down
+before and take care of it. Here, though, essentially as an optimization, we
+install signal handlers in the main thread using ``reactor._handleSignals``.
+``reactor._handleSignals`` may work in some real-world applications, but if
+your application already needs to handle signals you may need a more careful
+approach. Again, see ``zc.async.subscribers`` for some options you can explore.
+
+    >>> import twisted.internet.selectreactor
+    >>> reactor = twisted.internet.selectreactor.SelectReactor()
+    >>> reactor._handleSignals()
+
+Now we are ready to instantiate our dispatcher.
+
+    >>> import zc.async.dispatcher
+    >>> dispatcher = zc.async.dispatcher.Dispatcher(db, reactor)
+
+Notice it has the uuid defined in instanceuuid.
+
+    >>> dispatcher.UUID == UUID
+    True
+
+Now we can start the reactor and the dispatcher in a thread.
+
+    >>> import threading
+    >>> def start():
+    ...     dispatcher.activate()
+    ...     reactor.run(installSignalHandlers=0)
+    ...
+    >>> thread = threading.Thread(target=start)
+    >>> thread.setDaemon(True)
+
+    >>> thread.start()
+
+The dispatcher should be starting up now.  Let's wait for it to activate.
+We're using a test convenience, get_poll, defined in the testing module.
+
+    >>> from zc.async.testing import get_poll
+    >>> poll = get_poll(dispatcher, 0)
+
+We're off!  The events have been fired for registering and activating the
+dispatcher.  Therefore, our subscriber to add our agent has fired.
+
+We need to begin our transaction to synchronize our view of the database.
+
+    >>> t = transaction.begin()
+
+We get the collection of dispatcher agents from the queue, using the UUID.
+
+    >>> dispatcher_agents = queue.dispatchers[UUID]
+
+It has one agent--the one placed by our subscriber.
+
+    >>> dispatcher_agents.keys()
+    ['main']
+    >>> agent = dispatcher_agents['main']
+
+Now we have our agent!  But...what is it [#stop_config_reactor]_?
+
+------
+Agents
+------
+
+Agents are the way you control what a dispatcher's worker threads do.  They
+pick the jobs and assign them to their dispatcher when the dispatcher asks.
+
+*If a dispatcher does not have any agents in a given queue, it will not perform
+any tasks for that queue.*
+
+We currently have an agent that simply asks for the next available FIFO job.
+We are using an agent implementation that allows you to specify a callable to
+filter the job.  That callable is now None.
+
+    >>> agent.filter is None
+    True
+
+What does a filter do?  A filter takes a job and returns a value evaluated as a
+boolean.  For instance, let's say we always wanted a certain number of threads
+available for working on a particular call; for the purpose of example, we'll
+use ``operator.mul``, though a more real-world example might be a network call
+or a particular call in your application.
+
+    >>> import operator
+    >>> def chooseMul(job):
+    ...     return job.callable == operator.mul
+    ...
+
+You might want something more sophisticated, such as preferring operator.mul,
+but if one is not in the queue, it will take any; or doing any other priority
+variations.  To do this, you'll want to write your own agent--possibly
+inheriting from the provided one and overriding ``_choose``.
+
+Let's set up another agent, in addition to the default one, that has
+the ``chooseMul`` policy.
+
+    >>> agent2 = dispatcher_agents['mul'] = zc.async.agent.Agent(chooseMul)
+
+Another characteristic of agents is that they specify how many jobs they
+should pick at a time.  The dispatcher actually adjusts the size of the
+ZODB connection pool to accommodate its agents' size.  The default is 3.
+
+    >>> agent.size
+    3
+    >>> agent2.size
+    3
+
+We can change that at creation or later.
+
+Finally, it's worth noting that agents contain the jobs that are currently
+worked on by the dispatcher, on their behalf; and have a ``completed``
+collection of the more recent completed jobs, beginning with the most recently
+completed job.
+
+----------------------
+Logging and Monitoring
+----------------------
+
+Logs are sent to the ``zc.async.events`` log for big events, like startup and
+shutdown, and errors.  Poll and job logs are sent to ``zc.async.trace``.
+Configure the standard Python logging module as usual to send these logs where
+you need.  Be sure to auto-rotate the trace logs.
+
+The package supports monitoring using zc.monitor.  Using this package includes
+only a very few additional dependencies: zc.monitor, simplejson, and zc.ngi. An
+example of setting it up without Zope 3 is in the end of
+:ref:`quickstart-with-virtualenv`.  If you would like to use it, see that
+document, monitor.txt in the package, and our next section:
+:ref:`configuration-with-zope-3`. 
+
+Otherwise, if you want to roll your own monitoring, glance at monitor.py and
+monitordb.py--you'll see that you should be able to reuse most of the heavy
+lifting, so it should be pretty easy to hook up the basic data another way.
+
+    >>> reactor.stop()
+
+.. rubric:: Footnotes
+
+.. [#specific_dependencies]  More specifically, as of this writing,
+    these are the minimal egg dependencies (including indirect
+    dependencies):
+
+    - pytz
+        A Python time zone library
+
+    - rwproperty
+        A small package of descriptor conveniences
+
+    - uuid
+        The uuid module included in Python 2.5
+
+    - zc.dict
+        A ZODB-aware dict implementation based on BTrees.
+
+    - zc.queue
+        A ZODB-aware queue
+
+    - zc.twist
+        Conveniences for working with Twisted and the ZODB
+
+    - twisted
+        The Twisted internet library.
+
+    - ZConfig
+        A general configuration package coming from the Zope project with which
+        the ZODB tests.
+
+    - zdaemon
+        A general daemon tool coming from the Zope project.
+
+    - ZODB3
+        The Zope Object Database.
+
+    - zope.bforest
+        Aggregations of multiple BTrees into a single dict-like structure,
+        reasonable for rotating data structures, among other purposes.
+
+    - zope.component
+        A way to hook together code by contract.
+
+    - zope.deferredimport
+        A way to defer imports in Python packages, often to prevent circular
+        import problems.
+
+    - zope.deprecation
+        A small framework for deprecating features.
+
+    - zope.event
+        An exceedingly small event framework that derives its power from
+        zope.component.
+
+    - zope.i18nmessageid
+        A way to specify strings to be translated.
+
+    - zope.interface
+        A way to specify code contracts and other data structures.
+
+    - zope.proxy
+        A way to proxy other Python objects.
+
+    - zope.testing
+        Testing extensions and helpers.
+
+    The next section, :ref:`configuration-with-zope-3`, still tries to limit
+    dependencies--we only rely on additional packages zc.z3monitor, simplejson,
+    and zope.app.appsetup ourselves--but as of this writing zope.app.appsetup
+    ends up dragging in a large chunk of zope.app.* packages. Hopefully that
+    will be refactored in Zope itself, and our full Zope 3 configuration can
+    benefit from the reduced indirect dependencies.
+
+.. [#undefined_quota_name]
+
+    >>> import operator
+    >>> import zc.async.job
+    >>> job = zc.async.job.Job(operator.mul, 5, 2)
+    >>> job.quota_names = ['content catalog']
+    >>> job.quota_names
+    ('content catalog',)
+    >>> queue.put(job)
+    Traceback (most recent call last):
+    ...
+    ValueError: ('unknown quota name', 'content catalog')
+    >>> len(queue)
+    0
+
+.. [#no_mutation_to_undefined]
+
+    >>> job.quota_names = ()
+    >>> job is queue.put(job)
+    True
+    >>> job.quota_names = ('content catalog',)
+    Traceback (most recent call last):
+    ...
+    ValueError: ('unknown quota name', 'content catalog')
+    >>> job.quota_names
+    ()
+
+.. [#create_remove_quotas]
+
+    >>> list(queue.quotas)
+    []
+    >>> queue.quotas.create('testing')
+    >>> list(queue.quotas)
+    ['testing']
+    >>> queue.quotas.remove('testing')
+    >>> list(queue.quotas)
+    []
+
+.. [#remove_quotas]
+
+    >>> queue.quotas.create('content catalog')
+    >>> job.quota_names = ('content catalog',)
+    >>> queue.quotas.remove('content catalog')
+    >>> job.quota_names
+    ('content catalog',)
+    >>> job is queue.claim()
+    True
+    >>> len(queue)
+    0
+
+.. [#default_size]
+
+    >>> queue.quotas.create('content catalog')
+    >>> queue.quotas['content catalog'].size
+    1
+
+.. [#change_size]
+
+    >>> queue.quotas['content catalog'].size = 2
+    >>> queue.quotas['content catalog'].size
+    2
+    >>> queue.quotas.create('frobnitz account', size=3)
+    >>> queue.quotas['frobnitz account'].size
+    3
+
+.. [#decreasing_affects_future]
+
+    >>> job1 = zc.async.job.Job(operator.mul, 5, 2)
+    >>> job2 = zc.async.job.Job(operator.mul, 5, 2)
+    >>> job3 = zc.async.job.Job(operator.mul, 5, 2)
+    >>> job1.quota_names = job2.quota_names = job3.quota_names = (
+    ...     'content catalog',)
+    >>> job1 is queue.put(job1)
+    True
+    >>> job2 is queue.put(job2)
+    True
+    >>> job3 is queue.put(job3)
+    True
+    >>> job1 is queue.claim()
+    True
+    >>> job2 is queue.claim()
+    True
+    >>> print queue.claim()
+    None
+    >>> quota = queue.quotas['content catalog']
+    >>> len(quota)
+    2
+    >>> list(quota) == [job1, job2]
+    True
+    >>> quota.filled
+    True
+    >>> quota.size = 1
+    >>> quota.filled
+    True
+    >>> print queue.claim()
+    None
+    >>> job1()
+    10
+    >>> print queue.claim()
+    None
+    >>> len(quota)
+    1
+    >>> list(quota) == [job2]
+    True
+    >>> job2()
+    10
+    >>> job3 is queue.claim()
+    True
+    >>> list(quota) == [job3]
+    True
+    >>> len(quota)
+    1
+    >>> job3()
+    10
+    >>> print queue.claim()
+    None
+    >>> len(queue)
+    0
+    >>> quota.clean()
+    >>> len(quota)
+    0
+    >>> quota.filled
+    False
+
+.. [#stop_config_reactor] We don't want the live dispatcher for our demos,
+    actually.  See dispatcher.txt to see the live dispatcher actually in use.
+    So, here we'll stop the "real" reactor and switch to a testing one.
+
+    >>> reactor.callFromThread(reactor.stop)
+    >>> thread.join(3)
+    >>> assert not dispatcher.activated, 'dispatcher did not deactivate'
+
+    >>> import zc.async.testing
+    >>> reactor = zc.async.testing.Reactor()
+    >>> dispatcher._reactor = reactor
+    >>> dispatcher.activate()
+    >>> reactor.start()


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/README_2.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/README_3.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/README_3.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/README_3.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,61 @@
+.. _configuration-with-zope-3:
+
+=========================
+Configuration with Zope 3
+=========================
+
+Our last main section can be the shortest yet, both because we've already
+introduced all of the main concepts, and because we will be leveraging
+conveniences to automate much of the configuration shown in the section
+discussing configuration without Zope 3.
+
+Client Set Up
+=============
+
+If you want to set up a client alone, without a dispatcher, include the egg in
+your setup.py, include the configure.zcml in your applications zcml, make sure
+you share the database in which the queues will be held, and make sure that
+either the zope.app.keyreference.persistent.connectionOfPersistent adapter is
+registered, or zc.twist.connection.
+
+That should be it.
+
+Client/Server Set Up
+====================
+
+For a client/server combination, use zcml that is something like the
+basic_dispatcher_policy.zcml, make sure you have access to the database with
+the queues, configure logging and monitoring as desired, configure the
+``ZC_ASYNC_UUID`` environmental variable in zdaemon.conf if you are in
+production, and start up! Getting started is really pretty easy. You can even
+start a dispatcher-only version by not starting any servers in zcml.
+
+In comparison to the non-Zope 3 usage, an important difference in your setup.py
+is that, if you want the full set up described below, including zc.z3monitor,
+you'll need to specify "zc.async [z3]" as the desired package in your
+``install_requires``, as opposed to just "zc.async" [#extras_require]_.
+
+We'll look at this by making a zope.conf-alike and a site.zcml-alike.  We'll
+need a place to put some files, so we'll use a temporary directory.  This, and
+the comments in the files that we set up, are the primary differences between
+our examples and a real set up.
+
+We'll do this in two versions.  The first version uses a single database, as
+you might do to get started quickly, or for a small site.  The second version
+has one database for the main application, and one database for the async data,
+as will be more appropriate for typical production usage.
+
+.. toctree::
+   :maxdepth: 2
+   
+   README_3a
+   README_3b
+
+.. rubric:: Footnotes
+
+.. [#extras_require] The "[z3]" is an "extra", defined in zc.async's setup.py
+    in ``extras_require``. It pulls along zc.z3monitor and simplejson in
+    addition to the packages described in the
+    :ref:`configuration-without-zope-3` section. Unfortunately, zc.z3monitor
+    depends on zope.app.appsetup, which as of this writing ends up depending
+    indirectly on many, many packages, some as far flung as zope.app.rotterdam.


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/README_3.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/README_3a.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/README_3a.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/README_3a.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,246 @@
+-----------------------------
+Shared Single Database Set Up
+-----------------------------
+
+As described above, using a shared single database will probably be the
+quickest way to get started.  Large-scale production usage will probably prefer
+to use the :ref:`two-database-set-up` described later.
+
+So, without further ado, here is the text of our zope.conf-alike, and of our
+site.zcml-alike [#get_vals]_.
+
+    >>> zope_conf = """
+    ... site-definition %(site_zcml_file)s
+    ...
+    ... <zodb main>
+    ...   <filestorage>
+    ...     create true
+    ...     path %(main_storage_path)s
+    ...   </filestorage>
+    ... </zodb>
+    ...
+    ... <product-config zc.z3monitor>
+    ...   port %(monitor_port)s
+    ... </product-config>
+    ...
+    ... <logger>
+    ...   level debug
+    ...   name zc.async
+    ...   propagate no
+    ...
+    ...   <logfile>
+    ...     path %(async_event_log)s
+    ...   </logfile>
+    ... </logger>
+    ...
+    ... <logger>
+    ...   level debug
+    ...   name zc.async.trace
+    ...   propagate no
+    ...
+    ...   <logfile>
+    ...     path %(async_trace_log)s
+    ...   </logfile>
+    ... </logger>
+    ...
+    ... <eventlog>
+    ...   <logfile>
+    ...     formatter zope.exceptions.log.Formatter
+    ...     path STDOUT
+    ...   </logfile>
+    ...   <logfile>
+    ...     formatter zope.exceptions.log.Formatter
+    ...     path %(event_log)s
+    ...   </logfile>
+    ... </eventlog>
+    ... """ % {'site_zcml_file': site_zcml_file,
+    ...        'main_storage_path': os.path.join(dir, 'main.fs'),
+    ...        'async_storage_path': os.path.join(dir, 'async.fs'),
+    ...        'monitor_port': monitor_port,
+    ...        'event_log': os.path.join(dir, 'z3.log'),
+    ...        'async_event_log': os.path.join(dir, 'async.log'),
+    ...        'async_trace_log': os.path.join(dir, 'async_trace.log'),}
+    ...
+
+In a non-trivial production system, you will also probably want to replace
+the file storage with a <zeoclient> stanza.
+
+Also note that an open monitor port should be behind a firewall, of course.
+
+We'll assume that zdaemon.conf has been set up to put ZC_ASYNC_UUID in the
+proper place too.  It would have looked something like this in the
+zdaemon.conf::
+
+    <environment>
+      ZC_ASYNC_UUID /path/to/uuid.txt
+    </environment>
+
+(Other tools, such as supervisor, also can work, of course; their spellings are
+different and are "left as an exercise to the reader" at the moment.)
+
+We'll do that by hand:
+
+    >>> os.environ['ZC_ASYNC_UUID'] = os.path.join(dir, 'uuid.txt')
+
+Now let's define our site-zcml-alike.
+
+    >>> site_zcml = """
+    ... <configure xmlns='http://namespaces.zope.org/zope'
+    ...            xmlns:meta="http://namespaces.zope.org/meta"
+    ...            >
+    ... <include package="zope.component" file="meta.zcml" />
+    ... <include package="zope.component" />
+    ... <include package="zc.z3monitor" />
+    ... <include package="zc.async" file="basic_dispatcher_policy.zcml" />
+    ...
+    ... <!-- this is usually handled in Zope applications by the
+    ...      zope.app.keyreference.persistent.connectionOfPersistent adapter -->
+    ... <adapter factory="zc.twist.connection" />
+    ... </configure>
+    ... """
+
+Now we're done.
+
+If we process these files, and wait for a poll, we've got a working
+set up [#process]_.
+
+    >>> import zc.async.dispatcher
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> import pprint
+    >>> pprint.pprint(get_poll(dispatcher, 0))
+    {'': {'main': {'active jobs': [],
+                   'error': None,
+                   'len': 0,
+                   'new jobs': [],
+                   'size': 3}}}
+    >>> bool(dispatcher.activated)
+    True
+
+We can ask for a job to be performed, and get the result.
+
+    >>> conn = db.open()
+    >>> root = conn.root()
+    >>> import zc.async.interfaces
+    >>> queue = zc.async.interfaces.IQueue(root)
+    >>> import operator
+    >>> import zc.async.job
+    >>> job = queue.put(zc.async.job.Job(operator.mul, 21, 2))
+    >>> import transaction
+    >>> transaction.commit()
+    >>> wait_for_result(job)
+    42
+
+We can connect to the monitor server with telnet.
+
+    >>> import telnetlib
+    >>> tn = telnetlib.Telnet('127.0.0.1', monitor_port)
+    >>> tn.write('async status\n') # immediately disconnects
+    >>> print tn.read_all() # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    {
+        "poll interval": {
+            "seconds": ...
+        },
+        "status": "RUNNING",
+        "time since last poll": {
+            "seconds": ...
+        },
+        "uptime": {
+            "seconds": ...
+        },
+        "uuid": "..."
+    }
+    <BLANKLINE>
+
+Now we'll "shut down" with a CTRL-C, or SIGINT, and clean up.
+
+    >>> import signal
+    >>> if getattr(os, 'getpid', None) is not None: # UNIXEN, not Windows
+    ...     pid = os.getpid()
+    ...     try:
+    ...         os.kill(pid, signal.SIGINT)
+    ...     except KeyboardInterrupt:
+    ...         if dispatcher.activated:
+    ...             assert False, 'dispatcher did not deactivate'
+    ...     else:
+    ...         print "failed to send SIGINT, or something"
+    ... else:
+    ...     dispatcher.reactor.callFromThread(dispatcher.reactor.stop)
+    ...     for i in range(30):
+    ...         if not dispatcher.activated:
+    ...             break
+    ...         time.sleep(0.1)
+    ...     else:
+    ...         assert False, 'dispatcher did not deactivate'
+    ...
+    >>> import transaction
+    >>> t = transaction.begin() # sync
+    >>> import zope.component
+    >>> import zc.async.interfaces
+    >>> uuid = zope.component.getUtility(zc.async.interfaces.IUUID)
+    >>> da = queue.dispatchers[uuid]
+    >>> bool(da.activated)
+    False
+
+    >>> db.close()
+    >>> import shutil
+    >>> shutil.rmtree(dir)
+
+These instructions are very similar to the :ref:`two-database-set-up`.
+
+.. rubric:: Footnotes
+
+.. [#get_vals]
+
+    >>> import errno, os, random, socket, tempfile
+    >>> dir = tempfile.mkdtemp()
+    >>> site_zcml_file = os.path.join(dir, 'site.zcml')
+
+    >>> s = socket.socket()
+    >>> for i in range(20):
+    ...     monitor_port = random.randint(20000, 49151)
+    ...     try:
+    ...         s.bind(('127.0.0.1', monitor_port))
+    ...     except socket.error, e:
+    ...         if e.args[0] == errno.EADDRINUSE:
+    ...             pass
+    ...         else:
+    ...             raise
+    ...     else:
+    ...         s.close()
+    ...         break
+    ... else:
+    ...     assert False, 'could not find available port'
+    ...     monitor_port = None
+    ...
+
+.. [#process]
+
+    >>> zope_conf_file = os.path.join(dir, 'zope.conf')
+    >>> f = open(zope_conf_file, 'w')
+    >>> f.write(zope_conf)
+    >>> f.close()
+    >>> f = open(site_zcml_file, 'w')
+    >>> f.write(site_zcml)
+    >>> f.close()
+
+    >>> import zdaemon.zdoptions
+    >>> import zope.app.appsetup
+    >>> options = zdaemon.zdoptions.ZDOptions()
+    >>> options.schemadir = os.path.join(
+    ...     os.path.dirname(os.path.abspath(zope.app.appsetup.__file__)),
+    ...     'schema')
+    >>> options.realize(['-C', zope_conf_file])
+    >>> config = options.configroot
+
+    >>> import zope.app.appsetup.product
+    >>> zope.app.appsetup.product.setProductConfigurations(
+    ...     config.product_config)
+    >>> ignore = zope.app.appsetup.config(config.site_definition)
+    >>> import zope.app.appsetup.appsetup
+    >>> db = zope.app.appsetup.appsetup.multi_database(config.databases)[0][0]
+
+    >>> import zope.event
+    >>> import zc.async.interfaces
+    >>> zope.event.notify(zc.async.interfaces.DatabaseOpened(db))
+
+    >>> from zc.async.testing import get_poll, wait_for_result


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/README_3a.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/README_3b.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/README_3b.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/README_3b.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,213 @@
+.. _two-database-set-up:
+
+-------------------
+Two Database Set Up
+-------------------
+
+Even though it is a bit more trouble to set up, large-scale production usage
+will probably prefer to use this approach, over the shared single database
+described above.
+
+For our zope.conf, we only need one additional stanza to the one seen above::
+
+    <zodb async>
+      <filestorage>
+        create true
+        path REPLACE_THIS_WITH_PATH_TO_STORAGE
+      </filestorage>
+    </zodb>
+
+(You would replace "REPLACE_THIS_WITH_PATH_TO_STORAGE" with the path to the
+storage file.)
+
+As before, you will probably prefer to use ZEO rather than FileStorage in
+production.
+
+The zdaemon.conf instructions are the same: set the ZC_ASYNC_UUID environment
+variable properly in the zdaemon.conf file.
+
+For our site.zcml, the only difference is that we use the
+multidb_dispatcher_policy.zcml file rather than the
+basic_dispatcher_policy.zcml file.
+
+If you want to change policy, change "multidb_dispatcher_policy.zcml" to
+"dispatcher.zcml" in the example above and register your replacement bits for
+the policy in "multidb_dispatcher_policy.zcml".  You'll see that most of that
+comes from code in subscribers.py, which can be adjusted easily.
+
+If we process the files described above, and wait for a poll, we've got a
+working set up [#process_multi]_.
+
+    >>> import zc.async.dispatcher
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> import pprint
+    >>> pprint.pprint(get_poll(dispatcher, 0))
+    {'': {'main': {'active jobs': [],
+                   'error': None,
+                   'len': 0,
+                   'new jobs': [],
+                   'size': 3}}}
+    >>> bool(dispatcher.activated)
+    True
+
+As before, we can ask for a job to be performed, and get the result.
+
+    >>> conn = db.open()
+    >>> root = conn.root()
+    >>> import zc.async.interfaces
+    >>> queue = zc.async.interfaces.IQueue(root)
+    >>> import operator
+    >>> import zc.async.job
+    >>> job = queue.put(zc.async.job.Job(operator.mul, 21, 2))
+    >>> import transaction
+    >>> transaction.commit()
+    >>> wait_for_result(job)
+    42
+
+Hopefully zc.async will be an easy-to-configure, easy-to-use, and useful tool
+for you! Good luck! [#shutdown]_
+
+.. rubric:: Footnotes
+
+.. [#process_multi]
+
+    >>> import errno, os, random, socket, tempfile
+    >>> dir = tempfile.mkdtemp()
+    >>> site_zcml_file = os.path.join(dir, 'site.zcml')
+
+    >>> s = socket.socket()
+    >>> for i in range(20):
+    ...     monitor_port = random.randint(20000, 49151)
+    ...     try:
+    ...         s.bind(('127.0.0.1', monitor_port))
+    ...     except socket.error, e:
+    ...         if e.args[0] == errno.EADDRINUSE:
+    ...             pass
+    ...         else:
+    ...             raise
+    ...     else:
+    ...         s.close()
+    ...         break
+    ... else:
+    ...     assert False, 'could not find available port'
+    ...     monitor_port = None
+    ...
+
+    >>> zope_conf = """
+    ... site-definition %(site_zcml_file)s
+    ...
+    ... <zodb main>
+    ...   <filestorage>
+    ...     create true
+    ...     path %(main_storage_path)s
+    ...   </filestorage>
+    ... </zodb>
+    ... 
+    ... <zodb async>
+    ...   <filestorage>
+    ...     create true
+    ...     path %(async_storage_path)s
+    ...   </filestorage>
+    ... </zodb>
+    ... 
+    ... <product-config zc.z3monitor>
+    ...   port %(monitor_port)s
+    ... </product-config>
+    ... 
+    ... <logger>
+    ...   level debug
+    ...   name zc.async
+    ...   propagate no
+    ... 
+    ...   <logfile>
+    ...     path %(async_event_log)s
+    ...   </logfile>
+    ... </logger>
+    ... 
+    ... <logger>
+    ...   level debug
+    ...   name zc.async.trace
+    ...   propagate no
+    ... 
+    ...   <logfile>
+    ...     path %(async_trace_log)s
+    ...   </logfile>
+    ... </logger>
+    ... 
+    ... <eventlog>
+    ...   <logfile>
+    ...     formatter zope.exceptions.log.Formatter
+    ...     path STDOUT
+    ...   </logfile>
+    ...   <logfile>
+    ...     formatter zope.exceptions.log.Formatter
+    ...     path %(event_log)s
+    ...   </logfile>
+    ... </eventlog>
+    ... """ % {'site_zcml_file': site_zcml_file,
+    ...        'main_storage_path': os.path.join(dir, 'main.fs'),
+    ...        'async_storage_path': os.path.join(dir, 'async.fs'),
+    ...        'monitor_port': monitor_port,
+    ...        'event_log': os.path.join(dir, 'z3.log'),
+    ...        'async_event_log': os.path.join(dir, 'async.log'),
+    ...        'async_trace_log': os.path.join(dir, 'async_trace.log'),}
+    ... 
+
+    >>> os.environ['ZC_ASYNC_UUID'] = os.path.join(dir, 'uuid.txt')
+
+    >>> site_zcml = """
+    ... <configure xmlns='http://namespaces.zope.org/zope'
+    ...            xmlns:meta="http://namespaces.zope.org/meta"
+    ...            >
+    ... <include package="zope.component" file="meta.zcml" />
+    ... <include package="zope.component" />
+    ... <include package="zc.z3monitor" />
+    ... <include package="zc.async" file="multidb_dispatcher_policy.zcml" />
+    ...
+    ... <!-- this is usually handled in Zope applications by the
+    ...      zope.app.keyreference.persistent.connectionOfPersistent adapter -->
+    ... <adapter factory="zc.twist.connection" />
+    ... </configure>
+    ... """
+
+    >>> zope_conf_file = os.path.join(dir, 'zope.conf')
+    >>> f = open(zope_conf_file, 'w')
+    >>> f.write(zope_conf)
+    >>> f.close()
+    >>> f = open(site_zcml_file, 'w')
+    >>> f.write(site_zcml)
+    >>> f.close()
+
+    >>> import zdaemon.zdoptions
+    >>> import zope.app.appsetup
+    >>> options = zdaemon.zdoptions.ZDOptions()
+    >>> options.schemadir = os.path.join(
+    ...     os.path.dirname(os.path.abspath(zope.app.appsetup.__file__)),
+    ...     'schema')
+    >>> options.realize(['-C', zope_conf_file])
+    >>> config = options.configroot
+
+    >>> import zope.app.appsetup.product
+    >>> zope.app.appsetup.product.setProductConfigurations(
+    ...     config.product_config)
+    >>> ignore = zope.app.appsetup.config(config.site_definition)
+    >>> import zope.app.appsetup.appsetup
+    >>> db = zope.app.appsetup.appsetup.multi_database(config.databases)[0][0]
+
+    >>> import zope.event
+    >>> import zc.async.interfaces
+    >>> zope.event.notify(zc.async.interfaces.DatabaseOpened(db))
+
+    >>> from zc.async.testing import get_poll, wait_for_result
+
+.. [#shutdown]
+
+    >>> import zc.async.dispatcher
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> dispatcher.reactor.callFromThread(dispatcher.reactor.stop)
+    >>> dispatcher.thread.join(3)
+
+    >>> db.close()
+    >>> db.databases['async'].close()
+    >>> import shutil
+    >>> shutil.rmtree(dir)


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/README_3b.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/catastrophes.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/catastrophes.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/catastrophes.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,888 @@
+.. _recovering-from-catastrophes:
+
+Recovering from Catastrophes
+============================
+
+--------------------
+What Might Go Wrong?
+--------------------
+
+Sometimes bad things happen in the course of processing tasks. What might go
+wrong? How does zc.async handle these errors? What are your responsibilities?
+
+First, what might go wrong?
+
+- zc.async could have a problem while polling for jobs.  We'll call this a
+  "polling exception."
+
+- zc.async could have a problem while performing a particular job.  We'll call
+  this a "job-related exception."
+
+For the purpose of this discussion, we will omit the possibility that zc.async
+has a bug. That is certainly a possibility, but the recovery story is not
+predictable, and if we knew of a bug, we'd try to fix it, rather than discuss
+it here!
+
+We'll discuss both polling exceptions and job related exceptions, then drill
+down into some specific scenarios. This will illuminate how your code and
+zc.async's can work together to handle them.
+
+Polling Exceptions
+------------------
+
+Polling exceptions are, at least in theory, the least of your worries. You
+shouldn't have to worry about them; and if you do, it is probably a basic
+configuration problem that you need to address, such as making sure that the
+dispatcher process has access to the needed databases and software; or making
+sure that the dispatcher process is run by a daemonizing software that will
+restart if needed, such as zdaemon (http://pypi.python.org/pypi/zdaemon) or
+supervisor (http://supervisord.org/).
+
+zc.async is largely responsible for dealing with polling exceptions. What does
+it have to handle?
+
+- The process running the poll ends, perhaps in the middle of a poll.
+
+- zc.async cannot commit a transaction during the poll, for instance because of
+  a ConflictError, or because the database is unavailable.
+
+What needs to happen to handle these problems?
+
+Process Ends while Polling
+..........................
+
+If the process ends, your daemonizing front-end (zdaemon, supervisor, etc.)
+needs to restart it. The ZODB will discard incomplete transaction data, if any.
+
+The only thing a zc.async dispatcher needs to handle is clean up.
+
+- Ideally it will be able to deactivate its record in the ZODB during the
+  process shutdown.
+
+- Instead, if it was a "hard crash" that didn't allow deactivation, a sibling
+  dispatcher will realize that the dispatcher is down and deactivate it.
+
+- Or, finally, if it was a hard crash without a sibling, and the daemon
+  restarts a process for the original dispatcher instance, the new process
+  needs to realize that the old process is dead, not competing with it.
+
+Transaction Error while Polling
+...............................
+
+If the poll gets a conflict error, it should simply abort and retry the poll,
+forever, with a small back-off.
+
+If the database goes away (perhaps the ZEO server goes down for a bit, and the
+ZEO client to which the dispatcher is connected is trying to reconnect) it
+should gracefully try to wait for the database to return, and resume when it
+does.
+
+Other, more dramatic errors, such as POSKey errors, are generally considered to
+be out of zc.async's domain and control. It should ideally continue to try to
+resume as long as the process is alive, in case somehow the situation improves,
+but this may be difficult and the expectations for zc.async's recovery are
+lower than with ConflictErrors and ClientDisconnected errors.
+
+Summary of Polling Exceptions
+.............................
+
+To repeat, then, polling exceptions have two basic scenarios.
+
+If a dispatcher process ends, it needs to deactivate its record in the ZODB, or
+let another process know to deactivate it.
+
+If a ZODB.POSException.ConflictError occurs, retry forever with a small
+backoff; or if ZEO.Exceptions.ClientDisconnected occurs, retry forever with a
+small backoff, waiting for the database to come back.
+
+Most anything else will ideally keep zc.async attempting to re-poll, but it may
+not happen: expectations are lower.
+
+Job-Related Exceptions
+----------------------
+
+What about job-related exceptions? Responsibility for handling job-related
+exceptions is shared between your code and zc.async's.  What might happen?
+
+- Your job might fail internally.
+
+- The process running your task ends before completing your task.
+
+- zc.async cannot commit a transaction after your task completes, for instance
+  because of a ConflictError, or because the database is unavailable.
+
+What should occur to handle these problems?
+
+Job Fails
+.........
+
+As discussed elsewhere, if your job fails in your own code, this is mostly
+your responsibility. You should handle possible errors both within your job's
+code, and in callbacks, as appropriate.
+
+The other tool at your disposal for this situation, as with others below, is a
+retry policy. Retry policies let you determine what zc.async should do when
+your job fails. The default retry policy for job failures (as well as commit
+failures, below) is that transaction errors, such as conflict errors, are
+retried five times, and a ZEO ClientDisconnected error is retried forever with
+a backoff. You can customize these.
+
+Other than supporting these tools, zc.async's only other responsibilities are
+to report.
+
+By default, zc.async will log a failure of a job entered in a queue at the
+"ERROR" level in the ``zc.async.events`` log, and it will log a failure of a
+callback or other internal job at the "CRITICAL" level. This can be controlled
+per-process and per-job, as we'll see below. These tracebacks include
+information about the local and global variables for each frame in the stack,
+which can be useful to deduce the problem that occurred.
+
+zc.async also includes a ``Failure`` object on the job as a result, to let you
+react to the problem in a callback, and analyze it later.  This is discussed in
+detail in other documents.
+
+Process Ends During Job
+.......................
+
+If a process ends while it is performing a job, that is similar, in large part,
+to the possibility of the process ending the polling job: we need to restart
+the process, and realize that we had started the job. But should we restart the
+job, or abort it?
+
+Answering this question is a matter of policy, and requires knowing what each
+job does.
+
+Generally, if a job is fully transactional, such as writing something to the
+ZODB, and the job has not timed out yet, you'll want to restart it. You might
+want to restart some reasonably large number of times, and then suspect that,
+since you can't seem to finish the job, maybe the job is causing the process to
+die, and you should abort.  Or perhaps you want to restart for ever.
+
+If the job isn't transactional, such as communicating with an external service,
+you might want to abort the job, and set up some callbacks to handle the
+fallout.
+
+As we'll see below, zc.async defaults to guessing that jobs placed directly in
+a queue are transactional, and can be tried up to ten times; and that jobs
+used as callbacks are also transactional, and can be tried until they
+succeed.  The defaults can be changed and the behavior of an individual
+job can be changed.
+
+These settings are controlled with a RetryPolicy, discussed below.
+
+Transaction Error During Job
+............................
+
+Handling transaction errors after processing a job is also similar to the
+handling of transaction errors for polling exceptions. ConflictErrors and
+ClientDisconnected errors should often cause jobs to be aborted and restarted.
+However, if the job is not transactional, such as communicating with an
+external service, a simple abort and retry may be hazardous. Also, many jobs
+should be stopped if they retry on ConflictError more than some number of
+times--a heuristic bellweather--with the logic that they may be simply doing
+something too problematic, and they are blocking other tasks from starting. But
+other jobs should be retried until they complete.
+
+As mentioned above, zc.async defaults to guessing that jobs are transactional.
+Client Disconnected errors are retried forever, with a small backoff. Jobs
+placed in a queue retry transaction errors, such as ConflictErrors, four times,
+while callbacks retry them forever. The defaults can be changed and the
+behavior of an individual job can be changed, using the RetryPolicy described
+below.
+
+Summary of Job-Related Exceptions
+.................................
+
+If an exception occurs in your job's code, zc.async will log it as an ERROR
+if a main queue job and as CRITICAL if it is a callback; and it will make the
+result of the call a ``Failure`` with error information, as shown elsewhere.
+Everything else is your responsibility, to be handled with try:except or
+try:finally blocks in your code, callbacks, or custom RetryPolicies.
+
+Process death, conflict errors, and ``ClientDisconnected`` errors all may need
+to be handled differently for different jobs. zc.async has a default policy for
+jobs placed in a queue, and for callback jobs. The default policy, a
+RetryPolicy, can be changed and can be set explicitly per-job.
+
+Your Responsibilities
+---------------------
+
+As the author of a zc.async job, your responsibilities, then, are to handle
+your own exceptions; and to make sure that the retry policy for each job is
+appropriate.  This is controlled with an IRetryPolicy, as shown below.
+
+As someone configuring a running dispatcher, you need to make sure that you
+give the dispatcher the necessary access to databases and software to perform
+your jobs, and you need to review (and rotate!) your logs.
+
+zc.async's Responsibilities
+---------------------------
+
+zc.async needs to have polling robust in the face of restarts, ConflictErrors
+and ClientDisconnected errors. It needs to give your code a chance to decide
+what to do in these circumstances, and log your errors.
+
+Retry Policies
+--------------
+
+The rest of the document uses scenarios to illustrate how zc.async handles
+errors, and how you might want to configure retry policies.
+
+What is a retry policy?  It is used in three circumstances.
+
+- When the job starts but fails to complete because the system is interrupted,
+  the job will try to call ``retry_policy.interrupted()`` to get a boolean as
+  to whether the job should be retried.
+
+- When the code the job ran fails, the job will try to call
+  ``retry_policy.jobError(failure, data_cache)`` to get a boolean as to whether
+  the job should be retried.
+
+- When the commit fails, the job will try to call
+  ``retry_policy.commitError(failure, data_cache)`` to get a boolean as to
+  whether the job should be retried.
+
+Why does this need to be a policy?  Can't it be a simpler arrangement?
+
+The heart of the problem is that different jobs need different error
+resolutions.
+
+In some cases, jobs may not be fully transactional.  For instance, the job
+may be communicating with an external system, such as a credit card system.
+The retry policy here should typically be "never": perhaps a callback should be
+in charge of determining what to do next.
+
+If a job is fully transactional, it can be retried.  But even then the desired
+behavior may differ.
+
+- In typical cases, some errors should simply cause a failure, while other
+  errors, such as database conflict errors, should cause a limited number of
+  retries.
+
+- In some jobs, conflict errors should be retried forever, because the job must
+  be run to completion or else the system should fall over. Callbacks that try
+  to handle errors themselves may take this approach, for instance.
+
+zc.async currently ships with three retry policies.
+
+1.  The default, appropriate for most fully transactional jobs, is the
+    zc.async.job.RetryCommonFourTimes.  This retries ZEO disconnects forever;
+    and interrupts and transaction errors such as conflicts a set number of
+    times.
+
+2.  The other available (pre-written) option for transactional jobs is
+    zc.async.job.RetryCommonForever. Callbacks will get this policy by
+    default.  This retries ZEO disconnects, transaction errors such as conflict
+    errors, interrupts, and *anything* that happens during the job's commit,
+    forever.
+
+3.  The last retry policy is zc.async.job.NeverRetry.  This is appropriate for
+    non-transactional jobs. You'll still typically need to handle errors in
+    your callbacks.
+
+If you look at these, you will see that it is trivial to write your own, if
+desired.
+
+Scenarios
+---------
+
+We'll examine polling error scenarios and job error scenarios.
+
+- Polling errors
+
+  * The system is polling and gets a ConflictError.
+
+  * The system is polling and gets a ClientDisconnected error.
+
+- Job errors
+
+  * A worker process is working on a job with the default retry policy. The
+    process dies gracefully and restarts.
+
+  * Like the previous scenario, a worker process is working on a job with the
+    default retry policy. The process crashes hard (does not die gracefully)
+    and restarts.
+
+  * Like the previous scenario, a worker process is working on a job with the
+    default retry policy. The process crashes hard (does not die gracefully)
+    and a sibling notices and takes over.
+
+  * A worker process is working on a job with the default retry policy and gets
+    an error during the job or the commit.
+
+-------------------------
+Scenarios: Polling Errors
+-------------------------
+
+ConflictError
+-------------
+
+A common place for a conflict error is with two dispatchers trying to claim the
+same job from the queue.  This example will mimic that situation.
+
+Imagine we have a full set up with a dispatcher, agent, and queue. [#setUp]_
+We'll actually replace the agent's chooser with one that behaves badly: it
+blocks, waiting for our lock.
+
+    >>> import threading
+    >>> lock1 = threading.Lock()
+    >>> lock2 = threading.Lock()
+    >>> lock1.acquire()
+    True
+    >>> lock2.acquire()
+    True
+    >>> def acquireLockAndChooseFirst(agent):
+    ...     res = agent.queue.claim()
+    ...     if res is not None:
+    ...         lock2.release()
+    ...         lock1.acquire()
+    ...     return res
+    ...
+    >>> import zc.async.instanceuuid
+    >>> import zc.async.interfaces
+    >>> import zc.async.testing
+    >>> import zc.async.dispatcher
+    >>> import pprint
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> pprint.pprint(zc.async.testing.get_poll(dispatcher, 0))
+    {'': {'main': {'active jobs': [],
+                   'error': None,
+                   'len': 0,
+                   'new jobs': [],
+                   'size': 3}}}
+    >>> import transaction
+    >>> _ = transaction.begin()
+    >>> queues = root[zc.async.interfaces.KEY]
+    >>> queue = queues['']
+    >>> da = queue.dispatchers[zc.async.instanceuuid.UUID]
+    >>> agent = da['main']
+    >>> agent.chooser = acquireLockAndChooseFirst
+    >>> def returnSomething():
+    ...     return 42
+    ...
+    >>> job = queue.put(returnSomething)
+    >>> transaction.commit()
+
+Now, when the agent tries to get our job, we'll start and commit another
+transaction that removes it from the queue.  This will generate a conflict
+error for the poll's thread and transaction, because it cannot also remove the
+same job.
+
+    >>> lock2.acquire()
+    True
+    >>> _ = transaction.begin()
+    >>> job is queue.pull()
+    True
+    >>> transaction.commit()
+    >>> lock1.release()
+
+However, the ConflictError is handled, and polling continues.
+
+    >>> _ = transaction.begin()
+    >>> import zc.async.agent
+    >>> agent.chooser = zc.async.agent.chooseFirst
+    >>> transaction.commit()
+    >>> import zc.async.dispatcher
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> import zc.async.testing
+    >>> pprint.pprint(zc.async.testing.get_poll(dispatcher))
+    {'': {'main': {'active jobs': [],
+                   'error': None,
+                   'len': 0,
+                   'new jobs': [],
+                   'size': 3}}}
+
+And if we put the job back, it will be performed.
+
+    >>> job is queue.put(job)
+    True
+    >>> transaction.commit()
+    >>> zc.async.testing.wait_for_result(job)
+    42
+
+Client Disconnected
+-------------------
+
+The story is very similar if the ZEO connection goes away for a while.  We'll
+mimic a ZEO ClientDisconnected error by monkeypatching
+transaction.TranasctionManager.commit.
+
+    >>> lock1.locked()
+    True
+    >>> lock2.locked()
+    True
+
+    >>> agent.chooser = acquireLockAndChooseFirst
+    >>> job = queue.put(returnSomething)
+    >>> transaction.commit()
+
+    >>> lock2.acquire()
+    True
+    >>> import ZEO.Exceptions
+    >>> def commit(self):
+    ...     raise ZEO.Exceptions.ClientDisconnected()
+    ...
+    >>> import transaction
+    >>> old_commit = transaction.TransactionManager.commit
+    >>> transaction.TransactionManager.commit = commit
+    >>> import time
+    >>> sleep_requests = []
+    >>> def sleep(i):
+    ...     sleep_requests.append(i)
+    ...
+    >>> old_sleep = time.sleep
+    >>> time.sleep = sleep
+    >>> agent.chooser = zc.async.agent.chooseFirst
+    >>> transaction.commit()
+    >>> lock1.release()
+    >>> info = zc.async.testing.get_poll(dispatcher)['']['main']
+    >>> len(info['active jobs'] + info['new jobs'])
+    1
+    >>> transaction.TransactionManager.commit = old_commit
+    >>> zc.async.testing.wait_for_result(job)
+    42
+    >>> bool(sleep_requests)
+    True
+
+Here's another variant that mimics being unable to read the storage during a
+poll, and then recuperating.
+
+    >>> error_raised = False
+    >>> def raiseDisconnectedThenChooseFirst(agent):
+    ...     global error_raised
+    ...     if not error_raised:
+    ...         error_raised = True
+    ...         raise ZEO.Exceptions.ClientDisconnected()
+    ...     return agent.queue.claim()
+    >>> agent.chooser = raiseDisconnectedThenChooseFirst
+    >>> def returnSomething():
+    ...     return 42
+    ...
+    >>> job = queue.put(returnSomething)
+    >>> transaction.commit()
+    >>> pprint.pprint(zc.async.testing.get_poll(dispatcher)) # doctest: +ELLIPSIS
+    {'': {'main': {'active jobs': [],
+                   'error': <zc.twist.Failure ...ClientDisconnected>,
+                   'len': 0,
+                   'new jobs': [],
+                   'size': 3}}}
+    >>> zc.async.testing.wait_for_result(job)
+    42
+
+-----------------------------
+Scenarios: Job-Related Errors
+-----------------------------
+
+Graceful Shutdown During Job
+----------------------------
+
+First let's consider how a failed job with a callback or two is handled when
+the dispatcher dies.
+
+Here we start a job.
+
+    >>> import zope.component
+    >>> import transaction
+    >>> import zc.async.interfaces
+    >>> import zc.async.testing
+    >>> import zc.async.dispatcher
+
+    >>> queue = root[zc.async.interfaces.KEY]['']
+    >>> lock = threading.Lock()
+    >>> lock.acquire()
+    True
+    >>> fail_flag = True
+    >>> def wait_for_me():
+    ...     global fail_flag
+    ...     if fail_flag:
+    ...         fail_flag = False
+    ...         lock.acquire()
+    ...         lock.release() # so we can use the same lock again later
+    ...         raise SystemExit() # this will cause the worker thread to exit
+    ...     else:
+    ...         return 42
+    ...
+    >>> def handle_result(result):
+    ...     return 'I got result %r' % (result,)
+    ...
+    >>> job = queue.put(wait_for_me)
+    >>> callback_job = job.addCallback(handle_result)
+    >>> transaction.commit()
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> poll = zc.async.testing.get_poll(dispatcher)
+    >>> zc.async.testing.wait_for_start(job)
+
+In this scenario, ``wait_for_me`` is a job that, the first time it is run, will
+"unexpectedly" be lost while the dispatcher stops working. ``handle_result``
+will simply show us that callbacks will be called successfully.
+
+The job has started. Now, the dispatcher suddenly dies without the thread
+performing ``wait_for_me`` getting a chance to finish. For our first example,
+let's give the dispatcher a graceful exit. The dispatcher gets a chance to
+clean up its dispatcher agents, and job.handleInterrupt() goes into the queue.
+
+    >>> dispatcher.reactor.callFromThread(dispatcher.reactor.stop)
+    >>> zc.async.testing.wait_for_deactivation(dispatcher)
+    >>> _ = transaction.begin()
+    >>> job.status == zc.async.interfaces.ACTIVE
+    True
+    >>> len(queue)
+    1
+    >>> interrupt_job = queue[0]
+    >>> interrupt_job # doctest: +ELLIPSIS
+    <zc.async.job.Job ... ``zc.async.job.Job ... :handleInterrupt()``>
+    >>> queue[0].callable # doctest: +ELLIPSIS
+    <bound method Job.handleInterrupt of <...Job ... ``...wait_for_me()``>>
+
+Now when the process starts back up again, ``handleInterrupt`` checks with the
+default retry policy as to what should be done. It requests that the job be
+retried. It's put back in the queue, and it is called again normally.
+
+    >>> old_dispatcher = dispatcher
+    >>> zc.async.dispatcher.clear()
+    >>> zc.async.subscribers.ThreadedDispatcherInstaller(
+    ...         poll_interval=0.1)(zc.async.interfaces.DatabaseOpened(db))
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> zc.async.testing.wait_for_result(interrupt_job)
+
+Now we need to wait for the job.
+
+    >>> zc.async.testing.wait_for_result(job)
+    42
+    >>> callback_job.status == zc.async.interfaces.COMPLETED
+    True
+    >>> callback_job.result
+    'I got result 42'
+
+The job now has a retry policy with some currently non-interface values that
+are still worth showing here.
+
+    >>> policy = job.getRetryPolicy()
+    >>> policy.data.get('interruptions')
+    1
+
+This shows that the policy registered one interruption. [#cleanup1]_
+
+Hard Crash During Job
+---------------------
+
+Our next catastrophe only changes one aspect to the previous one: the
+dispatcher does not stop gracefully, and does not have a chance to clean up its
+active jobs.  It is a "hard" crash.
+
+To show this, we will start a job, simulate the dispatcher dying "hard," and
+restart it so it clean up.
+
+So, first we start a long-running job in the dispatcher.
+
+    >>> lock.acquire()
+    True
+    >>> fail_flag = True
+    >>> job = queue.put(wait_for_me)
+    >>> callback_job = job.addCallback(handle_result)
+    >>> transaction.commit()
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> poll = zc.async.testing.get_poll(dispatcher)
+    >>> zc.async.testing.wait_for_start(job)
+
+Now we'll "crash" the dispatcher.
+
+    >>> dispatcher.activated = False # this will make polling stop, without
+    ...                              # cleanup
+    >>> dispatcher.reactor.callFromThread(dispatcher.reactor.crash)
+    >>> dispatcher.thread.join(3)
+
+Hard crashes can be detected because the dispatchers write datetimes to the
+database every few polls. A given dispatcher instance does this for each queue
+on a ``DispatcherAgents`` object available in ``queue.dispatchers[UUID]``,
+where ``UUID`` is the uuid of that dispatcher.
+
+The ``DispatcherAgents`` object has four pertinent attributes:
+``ping_interval``, ``ping_death_interval``, ``last_ping.value``, and ``dead``.
+About every ``ping_interval`` (a ``datetime.timedelta``), the dispatcher is
+supposed to write a ``datetime`` to ``last_ping.value``. If the
+``last_ping.value`` plus the ``ping_death_interval`` (also a ``timedelta``) is
+older than now, the dispatcher is considered to be ``dead``, and old jobs
+should be cleaned up.
+
+The ``ping_interval`` defaults to 30 seconds, and the ``ping_death_interval``
+defaults to 60 seconds. Generally, the ``ping_death_interval`` should be at
+least two or three poll intervals (``zc.async.dispatcher.get().poll_interval``)
+greater than the ``ping_interval``.
+
+The ping hasn't timed out yet, so the dispatcher isn't considered dead yet.
+
+    >>> _ = transaction.begin()
+    >>> import zc.async.instanceuuid
+    >>> da = queue.dispatchers[zc.async.instanceuuid.UUID]
+    >>> da.ping_death_interval
+    datetime.timedelta(0, 60)
+    >>> da.ping_interval
+    datetime.timedelta(0, 30)
+    >>> bool(da.activated)
+    True
+    >>> da.dead
+    False
+
+Therefore, the job is still sitting around in the dispatcher's pile in the
+database (the ``main`` key is for the ``main`` agent installed in this
+dispatcher in the set up for these examples).
+
+    >>> job in da['main']
+    True
+    >>> job.status == zc.async.interfaces.ACTIVE
+    True
+
+Let's start our dispatcher up again.
+
+    >>> old_dispatcher = dispatcher
+    >>> zc.async.dispatcher.clear()
+    >>> zc.async.subscribers.ThreadedDispatcherInstaller(
+    ...         poll_interval=0.1)(zc.async.interfaces.DatabaseOpened(db))
+    >>> dispatcher = zc.async.dispatcher.get()
+
+Initially, it's going to be a bit confused, because it sees that the
+DispatcherAgents object is ``activated``, and not ``dead``. It can't tell if
+there's another process using its same UUID, or if it is looking at the result
+of a hard crash.
+
+    >>> zc.async.testing.wait_for_result(job, seconds=1)
+    Traceback (most recent call last):
+    ...
+    AssertionError: job never completed
+    >>> zc.async.testing.get_poll(dispatcher, seconds=1)
+    {'': None}
+    >>> for r in reversed(event_logs.records):
+    ...     if r.levelname == 'ERROR':
+    ...         break
+    ... else:
+    ...     assert False, 'did not find log'
+    ...
+    >>> print r.getMessage() # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    UUID ... already activated in queue  (oid 4): another process?
+    (To stop poll attempts in this process, set
+    ``zc.async.dispatcher.get().activated = False``.  To stop polls
+    permanently, don't start a zc.async.dispatcher!)
+
+To speed up the realization of our dispatcher that the previous activation is
+``dead``, we'll set the ping_death_interval to just one second.
+
+    >>> _ = transaction.begin()
+    >>> da.dead
+    False
+    >>> job in da['main']
+    True
+    >>> len(queue)
+    0
+    >>> import datetime
+    >>> da.ping_death_interval = datetime.timedelta(seconds=1)
+    >>> transaction.commit()
+    >>> zc.async.testing.wait_for_death(da)
+    >>> bool(da.activated)
+    True
+
+After the next poll, the dispatcher will have cleaned up its old tasks in the
+same way we saw in the previous example. The job's ``handleInterrupt`` method
+will be called, and the job will be put back in the queue to be retried. The
+DispatcherAgents object is no longer dead, because it is tied to the new
+instance of the dispatcher.
+
+    >>> poll = zc.async.testing.get_poll(dispatcher)
+    >>> t = transaction.begin()
+    >>> da.ping_death_interval = datetime.timedelta(seconds=60)
+    >>> transaction.commit()
+    >>> from zc.async.testing import time_sleep
+    >>> def wait_for_pending(job):
+    ...     for i in range(600):
+    ...         t = transaction.begin()
+    ...         if job.status in (zc.async.interfaces.PENDING):
+    ...             break
+    ...         time_sleep(0.01)
+    ...     else:
+    ...         assert False, 'job never pending: ' + str(job.status)
+    ...
+    >>> wait_for_pending(job)
+    >>> job in da['main']
+    False
+    >>> bool(da.activated)
+    True
+    >>> da.dead
+    False
+    >>> queue[0] is job
+    True
+
+Now we need to wait for the job.
+
+    >>> zc.async.testing.wait_for_result(job)
+    42
+    >>> callback_job.status == zc.async.interfaces.COMPLETED
+    True
+    >>> callback_job.result
+    'I got result 42'
+    >>> policy = job.getRetryPolicy()
+    >>> policy.data.get('interruptions')
+    1
+
+The dispatcher cleaned up its own "hard" crash.
+
+[#cleanup1]_
+
+.. _hard-crash-with-sibling-recovery:
+
+Hard Crash During Job with Sibling Recovery
+-------------------------------------------
+
+Our next catastrophe is the same as the one before, except, after one
+dispatcher's hard crash, another dispatcher is around to clean up the dead
+jobs.
+
+To show this, we will start a job, start a second dispatcher, simulate the
+first dispatcher dying "hard," and watch the second dispatcher clean up
+after the first one.
+
+So, first we start a long-running job in the dispatcher as before.
+
+    >>> lock.acquire()
+    True
+    >>> fail_flag = True
+    >>> job = queue.put(wait_for_me)
+    >>> callback_job = job.addCallback(handle_result)
+    >>> transaction.commit()
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> poll = zc.async.testing.get_poll(dispatcher)
+    >>> zc.async.testing.wait_for_start(job, seconds=30) # XXX not sure why so long
+
+Now we'll start up an alternate dispatcher.
+
+    >>> import uuid
+    >>> alt_uuid = uuid.uuid1()
+    >>> zc.async.subscribers.ThreadedDispatcherInstaller(
+    ...     poll_interval=0.5, uuid=alt_uuid)(
+    ...     zc.async.interfaces.DatabaseOpened(db))
+    >>> alt_dispatcher = zc.async.dispatcher.get(alt_uuid)
+
+Now we'll "crash" the dispatcher.
+
+    >>> dispatcher.activated = False # this will make polling stop, without
+    ...                              # cleanup
+    >>> dispatcher.reactor.callFromThread(dispatcher.reactor.crash)
+    >>> dispatcher.thread.join(3)
+    >>> dispatcher.thread.isAlive()
+    False
+
+As discussed in the previous example, the polling hasn't timed out yet, so the
+alternate dispatcher can't know that the first one is dead. Therefore, the job
+is still sitting around in the old dispatcher's pile in the database.
+
+    >>> _ = transaction.begin()
+    >>> bool(da.activated)
+    True
+    >>> da.dead
+    False
+    >>> job.status == zc.async.interfaces.ACTIVE
+    True
+    >>> alt_poll_1 = zc.async.testing.get_poll(alt_dispatcher)
+    >>> _ = transaction.begin()
+    >>> job in da['main']
+    True
+    >>> bool(da.activated)
+    True
+    >>> da.dead
+    False
+    >>> alt_poll_2 = zc.async.testing.get_poll(alt_dispatcher)
+    >>> _ = transaction.begin()
+    >>> job in da['main']
+    True
+    >>> bool(da.activated)
+    True
+    >>> da.dead
+    False
+
+Above, the ping_death_interval was returned to the default of 60 seconds. To
+speed up the realization of our second dispatcher that the first one is dead,
+we'll set the ping_death_interval back down to just one second.
+
+    >>> bool(da.activated)
+    True
+    >>> da.ping_death_interval
+    datetime.timedelta(0, 60)
+    >>> import datetime
+    >>> da.ping_death_interval = datetime.timedelta(seconds=1)
+    >>> transaction.commit()
+    >>> zc.async.testing.wait_for_death(da)
+
+After the second dispatcher gets a poll--a chance to notice--it will have
+cleaned up the first dispatcher's old tasks in the same way we saw in the
+previous example.  The job's ``handleInterrupt`` method will be called, which
+in this case will put it back in the queue to be claimed and performed.
+
+    >>> alt_poll_3 = zc.async.testing.get_poll(alt_dispatcher)
+    >>> _ = transaction.begin()
+    >>> job in da['main']
+    False
+    >>> bool(da.activated)
+    False
+    >>> da.dead
+    True
+    >>> wait_for_pending(job)
+    >>> queue[0] is job
+    True
+
+Now we need to wait for the job.
+
+    >>> zc.async.testing.wait_for_result(job)
+    42
+    >>> callback_job.status == zc.async.interfaces.COMPLETED
+    True
+    >>> callback_job.result
+    'I got result 42'
+
+The sibling, then, was able to clean up the mess left by the "hard" crash of
+the first dispatcher.
+
+[#cleanup2]_
+
+Other Job-Related Errors
+------------------------
+
+Other problems--errors when performing or committing jobs--are handled within
+jobs, getting the decisions from retry policies as described above.  These
+are demonstrated in the job.txt document.
+
+.. rubric:: Footnotes
+
+.. [#setUp]
+
+    >>> import ZODB.FileStorage
+    >>> storage = ZODB.FileStorage.FileStorage(
+    ...     'main.fs', create=True)
+    >>> from ZODB.DB import DB
+    >>> db = DB(storage)
+    >>> conn = db.open()
+    >>> root = conn.root()
+    >>> import zc.async.configure
+    >>> zc.async.configure.base()
+    >>> import zc.async.subscribers
+    >>> import zope.component
+    >>> zope.component.provideHandler(zc.async.subscribers.queue_installer)
+    >>> zope.component.provideHandler(
+    ...     zc.async.subscribers.ThreadedDispatcherInstaller(
+    ...         poll_interval=0.1))
+    >>> zope.component.provideHandler(zc.async.subscribers.agent_installer)
+    >>> import zope.event
+    >>> import zc.async.interfaces
+    >>> zope.event.notify(zc.async.interfaces.DatabaseOpened(db))
+    >>> import transaction
+    >>> _ = transaction.begin()
+
+.. [#cleanup1]
+
+    >>> lock.release()
+    >>> zc.async.testing.tear_down_dispatcher(old_dispatcher)
+
+.. [#cleanup2]
+
+    >>> lock.release()
+    >>> zc.async.testing.tear_down_dispatcher(dispatcher)
+    >>> zc.async.testing.tear_down_dispatcher(alt_dispatcher)
+    >>> time.sleep = old_sleep


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/catastrophes.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/ftesting.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/ftesting.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/ftesting.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,264 @@
+Zope 3 Testing Tips and Tricks
+==============================
+
+-------
+Summary
+-------
+
+- Make sure you are using zope.app.testing version 3.4.2 or newer, or else
+  ftests may intermittently raise spurious errors having to do with a missing
+  ``_result`` attribute on a request's response.
+
+- The Zope 3 tests use DemoStorage, which does not use MVCC.  This can lead
+  to your tests having occasional ConflictErrors that will not occur in
+  production. In common cases for ftests, you won't notice these because of
+  Zope's usual retry policy.  Unit or integration tests may show these
+  problems.
+
+- Set up the basic configuration in zcml or Python (see examples below), but
+  you need to make sure that ftests do not use dispatchers started in the
+  application. Start up ftest (or integration test) dispatchers separately,
+  using ``zc.async.ftesting.setUp``, and then tear down after the tests are
+  done with ``zc.async.ftesting.tearDown``.  The ``tearDown`` feature tries
+  to shut down all threads, and tries to let you know what job was running in
+  threads that couldn't be cleanly stopped.
+
+  The ftest dispatcher polls every tenth of a second, so you shouldn't need to
+  wait long for you job to get started in your tests.
+
+- General zc.async testing tools such as ``zc.async.dispatcher.get``,
+  ``zc.async.testing.get_poll`` and ``zc.async.testing.wait_for_result`` can
+  still be useful for in-depth zc.async tests.
+
+- If you don't want to dig into guts in your functional tests to use the tools
+  described in the previous point, consider making a view to check on job
+  status using a data structure like JSON, and looking at that in your tests.
+  Alternatively, investigate the tools in monitordb.py--although the tools
+  were created for zc.monitor, they can still be used effectively in Python.
+
+- The ``setUp`` code by default sends critical log messages to __stdout__ so it
+  can help diagnose why a callback might never complete.
+
+----------
+Discussion
+----------
+
+Normally, in a Zope 3 configuration that uses zc.async, you configure it
+when you start your application.  For instance, you might include a zc.async
+zcml file like basic_dispatcher_policy.zcml that performs the necessary set up.
+
+However, the Zope 3 ftesting layer database dance doesn't play well with
+zc.async unless you take a bit of extra care.
+
+This is because zc.async will be started with the ftests' underlying database,
+and then the test will be run with a DemoStorage wrapper. The zc.async
+dispatcher will run, then, but it will never see the changes that you make in
+the wrapper DemoStorage that your test manipulates.  This can be mystifying
+and frustrating.
+
+Because of this, when you write a Zope 3 app that wants to use both layered
+ftests and zc.async, you have to set things up in a mildly inconvenient way.
+
+When you start your application normally, use configuration (zcml or grok or
+whatever) to register subscribers like the ones in subscribers.py: adding
+queues, starting dispatchers, and adding agents.
+
+But don't have this configuration registered for your ftests. Instead, bypass
+that part of your site's configuration in your ftesting layer, and use the
+``zc.async.ftesting.setUp`` function to set zc.async up in tests when you need
+it, in a footnote of your test or in a similar spot.
+
+You'll still want the basic adapters registered, as found in zc.async's
+configure.zcml or configure.py files; and maybe the
+zc.async.queue.getDefaultQueue adapter too. This can be registered in
+ftesting.zcml with this snippet::
+
+  <include package="zc.async" />
+  <adapter factory="zc.async.queue.getDefaultQueue" />
+
+Or in Python, you might want to do something like this:
+
+    >>> import zc.async.configure
+    >>> zc.async.configure.base() # or, more likely, ``minimal`` for Zope 3
+    >>> import zope.component
+    >>> import zc.async.queue
+    >>> zope.component.provideAdapter(zc.async.queue.getDefaultQueue)
+
+Don't forget to call ``tearDown`` (see below) at the end of your test!
+
+Here's a usage example.
+
+As mentioned above, ``setUp`` does expect the necessary basic adapters to
+already be installed.
+
+Zope 3 ftests generally have a ``getRootObject`` hanging around to give you the
+root object in the Zope application (but not in the ZODB). Therefore, this
+function tries to be helpful, for better and worse, and muck around in the
+locals to find it. If you want it to leave your locals alone, pass it a
+database connection.
+
+So, here's some set up.  We create a database and make our stub
+``getRootFolder`` function in the globals.
+
+    >>> import transaction
+    >>> import BTrees
+    >>> import ZODB.FileStorage
+    >>> storage = ZODB.FileStorage.FileStorage(
+    ...     'zc_async.fs', create=True)
+    >>> from ZODB.DB import DB
+    >>> db = DB(storage)
+    >>> conn = db.open()
+    >>> root = conn.root()
+    >>> PseudoZopeRoot = root['Application'] = BTrees.family32.OO.BTree()
+    >>> transaction.commit()
+    >>> def _getRootObject():
+    ...     return PseudoZopeRoot
+    ...
+    >>> globals()['getRootFolder'] = _getRootObject
+
+Notice we are using a real FileStorage, and not a DemoStorage, as is usually
+used in ftests. The fact that DemoStorage does not have MVCC can sometimes lead
+standard ftests to raise spurious ReadConflictErrors that will not actually
+occur in production. The ConflictErrors will generally be retried, so your
+tests should usually pass, even though you might see some "complaints".
+
+Now we can call ``setUp`` as if we were in a functional test.
+
+    >>> import zc.async.ftesting
+    >>> zc.async.ftesting.setUp()
+
+Now the dispatcher is activated and the polls are running. The function sets up
+a dispatcher that polls much more frequently than usual--every 0.1 seconds
+rather than every 5, so that tests might run faster--but otherwise uses typical
+zc.async default values.
+
+It's worth noting a few tricks that are particularly useful for tests here.
+We'll also use a couple of them to verify that ``setUp`` did its work.
+
+``zc.async.dispatcher.get()`` returns the currently installed dispatcher. This
+can let you check if it is activated and polling and use its simple statistical
+methods, if you want.
+
+    >>> import zc.async.dispatcher
+    >>> dispatcher = zc.async.dispatcher.get()
+
+For now, we'll just see that the dispatcher is activated.
+
+    >>> bool(dispatcher.activated)
+    True
+
+See the dispatcher.txt for information on information you can get from the
+dispatcher object.
+
+zc.async.testing has a number of helpful functions for testing. ``get_poll`` is
+the most pertinent here: given a dispatcher, it will give you the next poll.
+This is a good way to make sure that a job you just put in has had a chance to
+be claimed by a dispatcher.  It's also a reasonable way to verify that the
+dispatcher has started.  ``setUp`` already gets the first two polls, so
+it's definitely all started.
+
+    >>> import zc.async.testing
+    >>> import pprint
+    >>> pprint.pprint(zc.async.testing.get_poll(dispatcher))
+    {'': {'main': {'active jobs': [],
+                   'error': None,
+                   'len': 0,
+                   'new jobs': [],
+                   'size': 3}}}
+
+Other useful testing functions are ``zc.async.testing.wait_for_result``, which
+waits for the result on a give job and returns it; and
+``zc.async.testing.wait_for_annotation``, which waits for a given annotation
+on a given job.  These are demonstrated in various doctests in this package,
+but should also be reasonably simple and self-explanatory.
+
+Callbacks will retry some errors forever, by default.  The logic is that
+callbacks are often the "cleanup" and must be run.  This can lead to confusion
+in debugging tests, though, because the retry warnings are sent to the log,
+and the log is not usually monitored in functional tests.
+
+``setUp`` tries to help with this by adding logging of ``CRITICAL`` log
+messages in the "zc.async" logger to stdout.
+
+    >>> import logging
+    >>> logging.getLogger('zc.async.event').critical('Foo!')
+    Foo!
+    >>> logging.getLogger('zc.async.event').error('Bar!')
+
+Once you have finished your tests, make sure to shut down your dispatcher, or
+the testing framework will complain about an unstopped daemon thread.
+zc.async.ftesting.tearDown will do the trick.
+
+    >>> zc.async.ftesting.tearDown()
+    >>> dispatcher.activated
+    False
+
+You can then start another async-enabled functional test up again later in the
+same layer, of course.
+
+    >>> zc.async.ftesting.setUp()
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> bool(dispatcher.activated)
+    True
+
+    >>> zc.async.ftesting.tearDown()
+    >>> dispatcher.activated
+    False
+
+ftesting.tearDown attempts to join all threads in the dispatchers' queues, but
+will raise an error if a job or dispatcher fails to shut down.
+
+If the thread is performing a job, the error informs you what job is being
+performed.
+
+    >>> zc.async.ftesting.setUp()
+    >>> _ = transaction.begin()
+    >>> queue = root[zc.async.interfaces.KEY]['']
+    >>> def bad_job():
+    ...     zc.async.testing.time_sleep(4)
+    >>> job = queue.put(bad_job)
+    >>> transaction.commit()
+    >>> zc.async.testing.wait_for_start(job)
+    >>> zc.async.ftesting.tearDown() # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TearDownDispatcherError: 
+    Job in pool 'main' failed to stop:
+      <zc.async.job.Job (oid ..., db ...) ``zc.async.doctest_test.bad_job()``>
+    >>> zc.async.testing.wait_for_result(job)
+
+If the dispatcher isn't shutting down for some reason, the UUID is given.
+
+    >>> zc.async.ftesting.tearDown()
+    >>> zc.async.ftesting.setUp()
+    >>> dispatcher = zc.async.dispatcher.get()
+    >>> def noop(*kw):
+    ...     pass
+    >>> original_stop = dispatcher.reactor.stop
+    >>> dispatcher.reactor.stop = noop
+    >>> zc.async.ftesting.tearDown() # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    Traceback (most recent call last):
+    ...
+    TearDownDispatcherError: 
+    Dispatcher (..., ...) failed to stop.
+
+Let's restore the original reactor.stop method and call tearDown again, which
+will work this time.
+
+    >>> dispatcher.reactor.stop = original_stop
+    >>> zc.async.ftesting.tearDown()
+
+Also worth noting, as mentioned in the summary, is that you should use
+zope.app.testing version 3.4.2 or higher to avoid getting spurious,
+intermittent bug reports from ftests that use zc.async.
+
+In your test or your test's tearDown, if you used a FileStorage, as we did
+here, you'll need to clean up as well.  We normally do this in our tests'
+tearDowns, but we do it here, now, to make the point.
+
+    >>> db.close()
+    >>> storage.close()
+    >>> storage.cleanup()
+
+    >>> del storage # we just do this to not confuse our own tearDown code.
+    >>> del globals()['getRootFolder'] # clean up globals; probably unnecessary


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/ftesting.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/index.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/index.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/index.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,136 @@
+|async|
+=======
+
+What is it?
+-----------
+
+The |async|_ package provides **an easy-to-use Python tool that schedules
+durable tasks across multiple processes and machines.**
+
+For instance...
+
+- *Web apps*: 
+
+  maybe your web application lets users request the creation of a large PDF, or
+  some other expensive task.
+
+- *Postponed work*:
+
+  maybe you have a job that needs to be done at a certain time, not right now.
+
+- *Parallel processing*: 
+
+  maybe you have a long-running problem that can be made to complete faster by
+  splitting it up into discrete parts, each performed in parallel, across
+  multiple machines.
+
+- *Serial processing*:
+
+  maybe you want to decompose and serialize a job.
+
+High-level features include the following.
+
+- **Easy to use.**
+
+  At its simplest, put a function in a |async| queue and commit a
+  transaction.  See the quick-starts for examples.
+
+- **Flexible configuration, changeable dynamically in production.**
+
+  Add and remove worker processes on the fly, with configurable policy on how
+  to handle interrupts.  Let processes decide how many of which tasks to
+  perform.  Configuration for each process is stored in the database so no
+  restarts are needed and a change can happen for any process from any
+  database client.
+
+- **Reliable and fault tolerant, supporting high availability.**
+
+  Configurable policy lets |async| know when, how, and under what circumstances
+  to retry jobs that encounter problems.  Multiple processes and machines can
+  be available to work on jobs, and a machine or process that suddenly dies
+  lets siblings decide what to do with incomplete jobs, with policy on a
+  per-job basis.  The central ZODB_ database server can be replicated with
+  commercial tools (ZRS_) or open-source tools (RelStorage_ plus, for instance
+  PostgreSQL and slony; or `gocept.zeoraid`_).
+
+- **Good debugging tools.**
+
+  Exceptions generate persistent ``Failure`` objects (from the Twisted_
+  project) for analysis, and verbose log messages.
+
+- **Well-tested.**
+
+  The package has good automated tests and is in use in mission-critical
+  applications for large software deployments.
+
+- **Friendly to testing.**
+
+  The package exposes testing helpers for a variety of circumstances, to make
+  writing automated tests for zc.async-enabled software fairly painless.
+
+While developed as part of the Zope project, zc.async can be used stand-alone,
+as seen in the quick-starts and the majority of the tests.
+
+How does it work?
+-----------------
+
+The system uses the Zope Object Database (ZODB_), a transactional, pickle-based
+Python object database, for communication and coordination among participating
+processes.
+
+|async| participants can each run in their own process, or share a process
+(run in threads) with other code.
+
+The Twisted_ framework supplies some code (failures and reactor
+implementations, primarily) and some concepts to the package.
+
+Quick starts
+------------
+
+These quick-starts can help you get a feel for the package.  **Please note:
+the Grok quickstart is only just begun, and should be regarded mostly
+as a placeholder.**
+
+.. toctree::
+   :maxdepth: 1
+   
+   QUICKSTART_1_VIRTUALENV
+   QUICKSTART_2_GROK
+
+Documentation
+-------------
+
+Contents:
+
+.. toctree::
+   :maxdepth: 2
+
+   README
+   README_1
+   README_2
+   README_3
+   tips
+   CHANGES
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
+
+
+.. |async| replace:: ``zc.async``
+
+.. _`async`: http://pypi.python.org/pypi/zc.async
+
+.. _Twisted: http://pypi.python.org/pypi/Twisted
+
+.. _ZODB: http://pypi.python.org/pypi/ZODB3
+
+.. _ZRS: http://www.zope.com/products/zope_replication_services.html
+
+.. _RelStorage: http://wiki.zope.org/ZODB/RelStorage
+
+.. _`gocept.zeoraid`: http://pypi.python.org/pypi/gocept.zeoraid


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/index.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/tips.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/tips.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/tips.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,87 @@
+===============
+Tips and Tricks
+===============
+
+General Tips and Tricks
+=======================
+
+* If you have multiple machines working as zc.async dispatchers, it is
+  strongly suggested that you get the associated servers connected to a shared
+  time server.  You generally don't want your machines to disagree by more than
+  a few seconds.
+
+* Avoid long transactions if possible.  Really try to avoid long transactions
+  involving frequently written objects.  One possible strategy is to divide up
+  your code into a job for low-conflict tasks and one or more jobs for
+  high-conflict tasks, perhaps created in a callback.
+
+* Sometimes you can't avoid long transactions. But *really* try to avoid long
+  commits. Commits hold a lock on the ZODB, and if you end up writing so much
+  in a single transaction that you take noticeable time to write, realize that
+  you are affecting--postponing--every single subsequent commit to the
+  database.
+
+* Callbacks should be quick and reliable. If you want to do something that
+  might take a while, put another job in the queue.
+
+* Some tasks are non-transactional.  If you want to do them in a ``Job``, you
+  don't want them to be retried!  Use the NeverRetry retry policy for these,
+  as described in the :ref:`recovering-from-catastrophes` section.
+
+* zc.async works fine with both Python 2.4 and Python 2.5.  Note that building
+  Twisted with Python 2.4 generates a SyntaxError in a test, but as of this
+  writing Twisted 8.1.0 is supported for Python 2.4.
+
+* Using the ``transaction`` package's before-commit hooks can wreak havoc if
+  your hook causes an exception during commit, and the job uses the zc.async
+  ``RetryCommonForever`` retry policy (which all callbacks use by default).
+  This policy has a contract that it *will* commit, or die trying, so it
+  retries all transactions that have an error on commit, and emits a critical
+  log message every few retries (configurable on the policy).  If the error
+  never goes away, this will retry *forever*.  Make sure critical log messages
+  actually alert someone!
+
+Testing Tips and Tricks
+=======================
+
+* In tests, don't check to see if poll is activated until after the first
+  poll. Try ``zc.async.testing.get_poll(zc.async.dispatcher.get(), 0)``, for
+  instance.
+
+* In tests, be aware that DemoStorage does not support mvcc and does not
+  support conflict resolution, so you may experience ConflictError (write and
+  particularly read) problems with it that you will not experience as much,
+  or at all, with a storage that supports those features such as FileStorage.
+  Notice that all of the tests in this package use FileStorage.
+
+* If you get a failure as a result and you didn't expect it, don't forget
+  the ``getTraceback`` and ``printTraceback`` methods on the failure.  The
+  whole point of the failure is to help you diagnose problems.
+
+* ``zc.async.dispatcher.get()`` will get you the dispatcher.  You can then check
+  if it is ``activated`` and also use the other introspection and status
+  methods.
+
+* The ``zc.async.testing`` module has a number of helpful functions for
+  testing. ``get_poll``, given a dispatcher, will give you the next poll. This
+  is a good way to make sure that a job you just put in has had a chance to be
+  claimed by a dispatcher. It's also a reasonable way to verify that the
+  dispatcher has started. Other useful testing functions are
+  ``zc.async.testing.wait_for_result``, which waits for the result on a give
+  job and returns it; and ``zc.async.testing.wait_for_annotation``, which waits
+  for a given annotation on a given job. These are demonstrated in various
+  doctests in this package, but should also be reasonably simple and
+  self-explanatory.
+
+More Tips and Tricks
+====================
+
+The following documents describe specific tips and tricks for specific
+situations.
+
+.. toctree::
+   :maxdepth: 2
+
+   catastrophes
+   z3
+   ftesting


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/tips.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_sources/z3.txt
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_sources/z3.txt	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_sources/z3.txt	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,167 @@
+Zope 3 General Tips and Tricks
+==============================
+
+If you use Zope 3, sometimes you want async jobs that have local sites and
+security set up. ``zc.async.z3.Job`` is a subclass of the main
+``zc.async.job.Job`` implementation that leverages the ``setUp`` and
+``tearDown`` hooks to accomplish this.
+
+It takes the site and the ids of the principals in the security context at its
+instantiation. The values can be mutated. Then when the job runs it sets the
+context up for the job's code, and then tears it down after the work has been
+committed (or aborted, if there was a failure). This can be very convenient for
+jobs that care about site-based component registries, or that care about the
+participants in zope.security interactions.
+
+This is different than a ``try: finally:`` wrapper around your main code that
+does the work, both because it is handled for you transparently, and because
+the context is cleaned up *after* the job's main transaction is committed. This
+means that code that expects a security or site context during a
+pre-transaction hook will be satisfied.
+
+For instance, let's imagine we have a database, and we establish a local site
+and an interaction with a request. [#zope3job_database_setup]_ [#empty_setup]_
+Unfortunately, this is a lot of set up. [#zope3job_setup]_
+
+    >>> import zope.app.component.hooks
+    >>> zope.app.component.hooks.setSite(site)
+    >>> import zope.security.management
+    >>> import zc.async.z3
+    >>> zope.security.management.newInteraction(
+    ...     zc.async.z3.Participation(mickey)) # usually would be a request
+
+Now we create a new job.
+
+    >>> def reportOnContext():
+    ...     print (zope.app.component.hooks.getSite().__class__.__name__,
+    ...             tuple(p.principal.id for p in
+    ...             zope.security.management.getInteraction().participations))
+    >>> j = root['j'] = zc.async.z3.Job(reportOnContext)
+
+The ids of the principals in the participations in the current interaction
+are in a ``participants`` tuple.  The site is on the job's ``site`` attribute.
+
+    >>> j.participants
+    ('mickey',)
+    >>> j.site is site
+    True
+
+If we end the interaction, clear the local site, and run the job, the job we
+used (``reportOnContext`` above) shows that the context was correctly in place.
+
+    >>> zope.security.management.endInteraction()
+    >>> zope.app.component.hooks.setSite(None)
+    >>> transaction.commit()
+    >>> j()
+    ('StubSite', ('mickey',))
+
+However, now the site and interaction are empty.
+
+    >>> print zope.security.management.queryInteraction()
+    None
+    >>> print zope.app.component.hooks.getSite()
+    None
+
+As mentioned, the context will be maintained through the transaction's commit.
+Let's illustrate.
+
+    >>> import zc.async
+    >>> import transaction.interfaces
+    >>> def setTransactionHook():
+    ...     t = transaction.interfaces.ITransactionManager(j).get()
+    ...     t.addBeforeCommitHook(reportOnContext)
+    ...
+    >>> zope.app.component.hooks.setSite(site)
+    >>> zope.security.management.newInteraction(
+    ...     zc.async.z3.Participation(mickey), zc.async.z3.Participation(jack),
+    ...     zc.async.z3.Participation(foo)) # >1 == rare but possible scenario
+    >>> j = root['j'] = zc.async.z3.Job(setTransactionHook)
+    >>> j.participants
+    ('mickey', 'jack', 'foo')
+    >>> j.site is site
+    True
+
+    >>> zope.security.management.endInteraction()
+    >>> zope.app.component.hooks.setSite(None)
+    >>> transaction.commit()
+    >>> j()
+    ('StubSite', ('mickey', 'jack', 'foo'))
+
+    >>> print zope.security.management.queryInteraction()
+    None
+    >>> print zope.app.component.hooks.getSite()
+    None
+
+.. rubric:: Footnotes
+
+.. [#zope3job_database_setup]
+
+    >>> from ZODB.tests.util import DB
+    >>> db = DB()
+    >>> conn = db.open()
+    >>> root = conn.root()
+
+    >>> import zc.async.configure
+    >>> zc.async.configure.base()
+
+    >>> import zc.async.testing
+    >>> zc.async.testing.setUpDatetime() # pins datetimes
+
+.. [#empty_setup] Without a site or an interaction, you can still instantiate
+    and run the job normally.
+
+    >>> import zc.async.z3
+    >>> import operator
+    >>> j = root['j'] = zc.async.z3.Job(operator.mul, 6, 7)
+    >>> j.participants
+    ()
+    >>> print j.site
+    None
+    >>> import transaction
+    >>> transaction.commit()
+    >>> j()
+    42
+
+.. [#zope3job_setup] To do this, we need to set up the zope.app.component
+    hooks, create a site, set up an authentication utility, and create some
+    principals that the authentication utility can return.
+
+    >>> import zope.app.component.hooks
+    >>> zope.app.component.hooks.setHooks()
+
+    >>> import zope.app.component.site
+    >>> import persistent
+    >>> class StubSite(persistent.Persistent,
+    ...                zope.app.component.site.SiteManagerContainer):
+    ...     pass
+    >>> site = root['site'] = StubSite()
+    >>> sm = zope.app.component.site.LocalSiteManager(site)
+    >>> site.setSiteManager(sm)
+
+    >>> import zope.security.interfaces
+    >>> import zope.app.security.interfaces
+    >>> import zope.interface
+    >>> import zope.location
+    >>> class StubPrincipal(object):
+    ...     zope.interface.implements(zope.security.interfaces.IPrincipal)
+    ...     def __init__(self, identifier, title, description=''):
+    ...         self.id = identifier
+    ...         self.title = title
+    ...         self.description = description
+    ...
+    >>> class StubPersistentAuth(persistent.Persistent,
+    ...                          zope.location.Location):
+    ...     zope.interface.implements(
+    ...         zope.app.security.interfaces.IAuthentication)
+    ...     _mapping = {'foo': 'Foo Fighter',
+    ...                 'jack': 'Jack, Giant Killer',
+    ...                 'mickey': 'Mickey Mouse'}
+    ...     def getPrincipal(self, principal_id):
+    ...         return StubPrincipal(principal_id, self._mapping[principal_id])
+    ...
+    >>> auth = StubPersistentAuth()
+    >>> sm.registerUtility(auth, zope.app.security.interfaces.IAuthentication)
+    >>> transaction.commit()
+    >>> mickey = auth.getPrincipal('mickey')
+    >>> jack = auth.getPrincipal('jack')
+    >>> foo = auth.getPrincipal('foo')


Property changes on: zc.async/trunk/htmldocs/1.5.0/_sources/z3.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zc.async/trunk/htmldocs/1.5.0/_static/contents.png
===================================================================
(Binary files differ)


Property changes on: zc.async/trunk/htmldocs/1.5.0/_static/contents.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: zc.async/trunk/htmldocs/1.5.0/_static/default.css
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_static/default.css	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_static/default.css	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,882 @@
+/**
+ * Sphinx Doc Design
+ */
+
+body {
+    font-family: sans-serif;
+    font-size: 100%;
+    background-color: #68b89e;
+    color: #000;
+    margin: 0;
+    padding: 0;
+}
+
+/* :::: LAYOUT :::: */
+
+div.document {
+    background-color: #f2f2f2;
+}
+
+div.documentwrapper {
+    float: left;
+    width: 100%;
+}
+
+div.bodywrapper {
+    margin: 0 230px 0 0px;
+	border-right: 4px solid #4c4c4c;
+	border-top: 4px solid #4c4c4c;
+	border-bottom: 4px solid #4c4c4c;
+}
+
+div.body {
+    background-color: white;
+    padding: 0 20px 30px 20px;
+}
+
+div.sphinxsidebarwrapper {
+    padding: 10px 5px 0 10px;
+}
+
+div.sphinxsidebar {
+    float: right;
+    width: 230px;
+    margin-left: -100%;
+    font-size: 90%;
+}
+
+div.clearer {
+    clear: both;
+}
+
+div.footer {
+    color: #fff;
+    width: 100%;
+    padding: 20px 0;
+    text-align: center;
+    font-size: 75%;
+}
+
+div.footer a {
+    color: #fff;
+    text-decoration: underline;
+}
+
+div.related {
+    background-color: #f2f2f2;
+    color: #000;
+    width: 100%;
+    height: 30px;
+    line-height: 30px;
+    font-size: 90%;
+}
+
+div.related h3 {
+    display: none;
+}
+
+div.related ul {
+    margin: 0;
+    padding: 0 0 0 10px;
+    list-style: none;
+}
+
+div.related li {
+    display: inline;
+}
+
+div.related li.right {
+    float: right;
+    margin-right: 5px;
+}
+
+div.related a {
+    color: #666;
+}
+
+/* ::: TOC :::: */
+div.sphinxsidebar h3 {
+    font-family: "American Typewriter", Georgia, "Times New Roman", Times, serif;
+    color: #000;
+    font-size: 1.4em;
+    font-weight: normal;
+    margin: 0;
+    padding: 0;
+}
+
+div.sphinxsidebar h4 {
+    font-family: "American Typewriter", Georgia, "Times New Roman", Times, serif;
+    color: #d94365;
+    font-size: 1.3em;
+    font-weight: normal;
+    margin: 5px 0 0 0;
+    padding: 0;
+}
+
+div.sphinxsidebar p {
+    color: white;
+}
+
+div.sphinxsidebar p.topless {
+    margin: 5px 10px 10px 10px;
+}
+
+div.sphinxsidebar ul {
+    margin: 10px;
+    padding: 0;
+    list-style: none;
+    color: #000;
+}
+
+div.sphinxsidebar ul ul,
+div.sphinxsidebar ul.want-points {
+    margin-left: 20px;
+    list-style: square;
+}
+
+div.sphinxsidebar ul ul {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+div.sphinxsidebar a {
+    color: #666;
+}
+
+div.sphinxsidebar form {
+    margin-top: 10px;
+}
+
+div.sphinxsidebar input {
+    border: 1px solid #98dbcc;
+    font-family: sans-serif;
+    font-size: 1em;
+}
+
+/* :::: MODULE CLOUD :::: */
+div.modulecloud {
+    margin: -5px 10px 5px 10px;
+    padding: 10px;
+    line-height: 160%;
+    border: 1px solid #cbe7e5;
+    background-color: #f2fbfd;
+}
+
+div.modulecloud a {
+    padding: 0 5px 0 5px;
+}
+
+/* :::: SEARCH :::: */
+ul.search {
+    margin: 10px 0 0 20px;
+    padding: 0;
+}
+
+ul.search li {
+    padding: 5px 0 5px 20px;
+    background-image: url(file.png);
+    background-repeat: no-repeat;
+    background-position: 0 7px;
+}
+
+ul.search li a {
+    font-weight: bold;
+}
+
+ul.search li div.context {
+    color: #888;
+    margin: 2px 0 0 30px;
+    text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+    font-weight: bold;
+}
+
+/* :::: COMMON FORM STYLES :::: */
+
+div.actions {
+    padding: 5px 10px 5px 10px;
+    border-top: 1px solid #cbe7e5;
+    border-bottom: 1px solid #cbe7e5;
+    background-color: #e0f6f4;
+}
+
+form dl {
+    color: #333;
+}
+
+form dt {
+    clear: both;
+    float: left;
+    min-width: 110px;
+    margin-right: 10px;
+    padding-top: 2px;
+}
+
+input#homepage {
+    display: none;
+}
+
+div.error {
+    margin: 5px 20px 0 0;
+    padding: 5px;
+    border: 1px solid #d00;
+    font-weight: bold;
+}
+
+/* :::: INLINE COMMENTS :::: */
+
+div.inlinecomments {
+    position: absolute;
+    right: 20px;
+}
+
+div.inlinecomments a.bubble {
+    display: block;
+    float: right;
+    background-image: url(style/comment.png);
+    background-repeat: no-repeat;
+    width: 25px;
+    height: 25px;
+    text-align: center;
+    padding-top: 3px;
+    font-size: 0.9em;
+    line-height: 14px;
+    font-weight: bold;
+    color: black;
+}
+
+div.inlinecomments a.bubble span {
+    display: none;
+}
+
+div.inlinecomments a.emptybubble {
+    background-image: url(style/nocomment.png);
+}
+
+div.inlinecomments a.bubble:hover {
+    background-image: url(style/hovercomment.png);
+    text-decoration: none;
+    color: #3ca0a4;
+}
+
+div.inlinecomments div.comments {
+    float: right;
+    margin: 25px 5px 0 0;
+    max-width: 50em;
+    min-width: 30em;
+    border: 1px solid #2eabb0;
+    background-color: #f2fbfd;
+    z-index: 150;
+}
+
+div#comments {
+    border: 1px solid #2eabb0;
+    margin-top: 20px;
+}
+
+div#comments div.nocomments {
+    padding: 10px;
+    font-weight: bold;
+}
+
+div.inlinecomments div.comments h3,
+div#comments h3 {
+    margin: 0;
+    padding: 0;
+    background-color: #2eabb0;
+    color: white;
+    border: none;
+    padding: 3px;
+}
+
+div.inlinecomments div.comments div.actions {
+    padding: 4px;
+    margin: 0;
+    border-top: none;
+}
+
+div#comments div.comment {
+    margin: 10px;
+    border: 1px solid #2eabb0;
+}
+
+div.inlinecomments div.comment h4,
+div.commentwindow div.comment h4,
+div#comments div.comment h4 {
+    margin: 10px 0 0 0;
+    background-color: #2eabb0;
+    color: white;
+    border: none;
+    padding: 1px 4px 1px 4px;
+}
+
+div#comments div.comment h4 {
+    margin: 0;
+}
+
+div#comments div.comment h4 a {
+    color: #d5f4f4;
+}
+
+div.inlinecomments div.comment div.text,
+div.commentwindow div.comment div.text,
+div#comments div.comment div.text {
+    margin: -5px 0 -5px 0;
+    padding: 0 10px 0 10px;
+}
+
+div.inlinecomments div.comment div.meta,
+div.commentwindow div.comment div.meta,
+div#comments div.comment div.meta {
+    text-align: right;
+    padding: 2px 10px 2px 0;
+    font-size: 95%;
+    color: #538893;
+    border-top: 1px solid #cbe7e5;
+    background-color: #e0f6f4;
+}
+
+div.commentwindow {
+    position: absolute;
+    width: 500px;
+    border: 1px solid #cbe7e5;
+    background-color: #f2fbfd;
+    display: none;
+    z-index: 130;
+}
+
+div.commentwindow h3 {
+    margin: 0;
+    background-color: #2eabb0;
+    color: white;
+    border: none;
+    padding: 5px;
+    font-size: 1.5em;
+    cursor: pointer;
+}
+
+div.commentwindow div.actions {
+    margin: 10px -10px 0 -10px;
+    padding: 4px 10px 4px 10px;
+    color: #538893;
+}
+
+div.commentwindow div.actions input {
+    border: 1px solid #2eabb0;
+    background-color: white;
+    color: #135355;
+    cursor: pointer;
+}
+
+div.commentwindow div.form {
+    padding: 0 10px 0 10px;
+}
+
+div.commentwindow div.form input,
+div.commentwindow div.form textarea {
+    border: 1px solid #3c9ea2;
+    background-color: white;
+    color: black;
+}
+
+div.commentwindow div.error {
+    margin: 10px 5px 10px 5px;
+    background-color: #fbe5dc;
+    display: none;
+}
+
+div.commentwindow div.form textarea {
+    width: 99%;
+}
+
+div.commentwindow div.preview {
+    margin: 10px 0 10px 0;
+    background-color: #70d0d4;
+    padding: 0 1px 1px 25px;
+}
+
+div.commentwindow div.preview h4 {
+    margin: 0 0 -5px -20px;
+    padding: 4px 0 0 4px;
+    color: white;
+    font-size: 1.3em;
+}
+
+div.commentwindow div.preview div.comment {
+    background-color: #f2fbfd;
+}
+
+div.commentwindow div.preview div.comment h4 {
+    margin: 10px 0 0 0!important;
+    padding: 1px 4px 1px 4px!important;
+    font-size: 1.2em;
+}
+
+/* :::: SUGGEST CHANGES :::: */
+div#suggest-changes-box input, div#suggest-changes-box textarea {
+    border: 1px solid #ccc;
+    background-color: white;
+    color: black;
+}
+
+div#suggest-changes-box textarea {
+    width: 99%;
+    height: 400px;
+}
+
+
+/* :::: PREVIEW :::: */
+div.preview {
+    background-image: url(style/preview.png);
+    padding: 0 20px 20px 20px;
+    margin-bottom: 30px;
+}
+
+
+/* :::: INDEX PAGE :::: */
+
+table.contentstable {
+    width: 90%;
+}
+
+table.contentstable p.biglink {
+    line-height: 150%;
+}
+
+a.biglink {
+    font-size: 1.3em;
+}
+
+span.linkdescr {
+    font-style: italic;
+    padding-top: 5px;
+    font-size: 90%;
+}
+
+/* :::: INDEX STYLES :::: */
+
+table.indextable td {
+    text-align: left;
+    vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+    height: 10px;
+}
+
+table.indextable tr.cap {
+    margin-top: 10px;
+    background-color: #f2f2f2;
+}
+
+img.toggler {
+    margin-right: 3px;
+    margin-top: 3px;
+    cursor: pointer;
+}
+
+form.pfform {
+    margin: 10px 0 20px 0;
+}
+
+/* :::: GLOBAL STYLES :::: */
+
+.docwarning {
+    background-color: #ffe4e4;
+    padding: 10px;
+    margin: 0 -20px 0 -20px;
+    border-bottom: 1px solid #f66;
+}
+
+p.subhead {
+    font-weight: bold;
+    margin-top: 20px;
+}
+
+a {
+    color: #355f7c;
+    text-decoration: none;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+    font-family: "American Typewriter", Georgia, "Times New Roman", Times, serif;
+    background-color: #f2f2f2;
+    font-weight: normal;
+    color: #000;
+    border-bottom: 1px solid #ccc;
+    margin: 20px -20px 10px -20px;
+    padding: 3px 0 3px 10px;
+	clear: right;
+}
+
+div.body h1 { margin-top: 0; font-size: 200%;
+	background-color: #74c990;
+	color: #fff;
+}
+div.body h2 { font-size: 160%;
+	color: #d94164;
+}
+div.body h3 { font-size: 140%; }
+div.body h4 { font-size: 120%; }
+div.body h5 { font-size: 110%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+    color: #c60f0f;
+    font-size: 0.8em;
+    padding: 0 4px 0 4px;
+    text-decoration: none;
+    visibility: hidden;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+    visibility: visible;
+}
+
+a.headerlink:hover {
+    background-color: #c60f0f;
+    color: white;
+}
+
+div.body p, div.body dd, div.body li {
+    text-align: left;
+    line-height: 130%;
+}
+
+div.body p.caption {
+    text-align: inherit;
+}
+
+div.body td {
+    text-align: left;
+}
+
+ul.fakelist {
+    list-style: none;
+    margin: 10px 0 10px 20px;
+    padding: 0;
+}
+
+.field-list ul {
+    padding-left: 1em;
+}
+
+.first {
+    margin-top: 0 !important;
+}
+
+/* "Footnotes" heading */
+p.rubric {
+    margin-top: 150px;
+	border-top: 30px solid #e6e6e6;
+	font-style: italic;
+	font-weight: bold;
+	font-family: "American Typewriter", Georgia, "Times New Roman", Times, serif;
+	padding-top: 10px;
+	padding-bottom: 20px;
+}
+
+/* "Topics" */
+
+div.topic {
+    background-color: #eee;
+    border: 1px solid #ccc;
+    padding: 0 7px 0 7px;
+    margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+    font-size: 1.1em;
+    font-weight: bold;
+    margin-top: 10px;
+}
+
+/* Admonitions */
+
+div.admonition {
+    margin-top: 10px;
+    margin-bottom: 10px;
+    padding: 7px;
+}
+
+div.admonition dt {
+    font-weight: bold;
+}
+
+div.admonition dl {
+    margin-bottom: 0;
+}
+
+div.admonition p {
+    display: inline;
+}
+
+div.seealso {
+    background-color: #ffc;
+    border: 1px solid #ff6;
+}
+
+div.warning {
+    background-color: #ffe4e4;
+    border: 1px solid #f66;
+}
+
+div.note {
+    background-color: #eee;
+    border: 1px solid #ccc;
+}
+
+p.admonition-title {
+    margin: 0px 10px 5px 0px;
+    font-weight: bold;
+    display: inline;
+}
+
+p.admonition-title:after {
+    content: ":";
+}
+
+div.body p.centered {
+    text-align: center;
+    margin-top: 25px;
+}
+
+table.docutils {
+    border: 0;
+}
+
+table.docutils td, table.docutils th {
+    padding: 1px 8px 1px 0;
+    border-top: 0;
+    border-left: 0;
+    border-right: 0;
+    border-bottom: 1px solid #aaa;
+}
+
+table.field-list td, table.field-list th {
+    border: 0 !important;
+}
+
+table.footnote td, table.footnote th {
+    border: 0 !important;
+}
+
+.field-list ul {
+    margin: 0;
+    padding-left: 1em;
+}
+
+.field-list p {
+    margin: 0;
+}
+
+dl {
+    margin-bottom: 15px;
+    clear: both;
+}
+
+dd p {
+    margin-top: 0px;
+}
+
+dd ul, dd table {
+    margin-bottom: 10px;
+}
+
+dd {
+    margin-top: 3px;
+    margin-bottom: 10px;
+    margin-left: 30px;
+}
+
+.refcount {
+    color: #060;
+}
+
+dt:target,
+.highlight {
+    background-color: #fbe54e;
+}
+
+dl.glossary dt {
+    font-weight: bold;
+    font-size: 1.1em;
+}
+
+th {
+    text-align: left;
+    padding-right: 5px;
+}
+
+pre {
+    padding: 5px;
+    background-color: #efc;
+    color: #333;
+    border: 1px solid #ac9;
+    border-left: none;
+    border-right: none;
+    overflow: auto;
+}
+
+td.linenos pre {
+    padding: 5px 0px;
+    border: 0;
+    background-color: transparent;
+    color: #aaa;
+}
+
+table.highlighttable {
+    margin-left: 0.5em;
+}
+
+table.highlighttable td {
+    padding: 0 0.5em 0 0.5em;
+}
+
+tt {
+    background-color: #ecf0f3;
+    padding: 0 1px 0 1px;
+    font-size: 0.95em;
+}
+
+tt.descname {
+    background-color: transparent;
+    font-weight: bold;
+    font-size: 1.2em;
+}
+
+tt.descclassname {
+    background-color: transparent;
+}
+
+tt.xref, a tt {
+    background-color: transparent;
+    font-weight: bold;
+}
+
+.footnote:target  { background-color: #ffa }
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+    background-color: transparent;
+}
+
+.optional {
+    font-size: 1.3em;
+}
+
+.versionmodified {
+    font-style: italic;
+}
+
+form.comment {
+    margin: 0;
+    padding: 10px 30px 10px 30px;
+    background-color: #eee;
+}
+
+form.comment h3 {
+    background-color: #326591;
+    color: white;
+    margin: -10px -30px 10px -30px;
+    padding: 5px;
+    font-size: 1.4em;
+}
+
+form.comment input,
+form.comment textarea {
+    border: 1px solid #ccc;
+    padding: 2px;
+    font-family: sans-serif;
+    font-size: 100%;
+}
+
+form.comment input[type="text"] {
+    width: 240px;
+}
+
+form.comment textarea {
+    width: 100%;
+    height: 200px;
+    margin-bottom: 10px;
+}
+
+.system-message {
+    background-color: #fda;
+    padding: 5px;
+    border: 3px solid red;
+}
+
+/* :::: PRINT :::: */
+ at media print {
+    div.document,
+    div.documentwrapper,
+    div.bodywrapper {
+        margin: 0;
+        width : 100%;
+    }
+
+    div.sphinxsidebar,
+    div.related,
+    div.footer,
+    div#comments div.new-comment-box,
+    #top-link {
+        display: none;
+    }
+}
+
+div.sidebar {
+	float: right;
+	width: 27em;
+	text-align: left;
+	margin-left: 10px;
+	padding: 2px;
+	border: 2px solid #afafaf;
+}
+
+div.sidebar p {
+	text-align: left;
+	padding-right: 4px;
+	padding-left: 10px;
+}
+
+div.sidebar p.sidebar-title {
+	background-color: #f2f2f2;
+	padding: 2px 2px 2px 4px;
+    border-bottom: 1px solid #ccc;
+    font-family: "American Typewriter", Georgia, "Times New Roman", Times, serif;
+    background-color: #f2f2f2;
+    font-weight: normal;
+    color: #d94164;
+}
+
+div.sidebar p.sidebar-title tt {
+	background: transparent;
+}
+
+div#agents div.highlight,
+div#a-job div.highlight,
+div#make-a-file div.highlight {
+	background-color: #fff;
+}
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/_static/doctools.js
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_static/doctools.js	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_static/doctools.js	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,352 @@
+/// XXX: make it cross browser
+
+/**
+ * make the code below compatible with browsers without
+ * an installed firebug like debugger
+ */
+if (!window.console || !console.firebug) {
+  var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
+      "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
+  window.console = {};
+  for (var i = 0; i < names.length; ++i)
+    window.console[names[i]] = function() {}
+}
+
+/**
+ * small helper function to urldecode strings
+ */
+jQuery.urldecode = function(x) {
+  return decodeURIComponent(x).replace(/\+/g, ' ');
+}
+
+/**
+ * small helper function to urlencode strings
+ */
+jQuery.urlencode = encodeURIComponent;
+
+/**
+ * This function returns the parsed url parameters of the
+ * current request. Multiple values per key are supported,
+ * it will always return arrays of strings for the value parts.
+ */
+jQuery.getQueryParameters = function(s) {
+  if (typeof s == 'undefined')
+    s = document.location.search;
+  var parts = s.substr(s.indexOf('?') + 1).split('&');
+  var result = {};
+  for (var i = 0; i < parts.length; i++) {
+    var tmp = parts[i].split('=', 2);
+    var key = jQuery.urldecode(tmp[0]);
+    var value = jQuery.urldecode(tmp[1]);
+    if (key in result)
+      result[key].push(value);
+    else
+      result[key] = [value];
+  }
+  return result;
+}
+
+/**
+ * small function to check if an array contains
+ * a given item.
+ */
+jQuery.contains = function(arr, item) {
+  for (var i = 0; i < arr.length; i++) {
+    if (arr[i] == item)
+      return true;
+  }
+  return false;
+}
+
+/**
+ * highlight a given string on a jquery object by wrapping it in
+ * span elements with the given class name.
+ */
+jQuery.fn.highlightText = function(text, className) {
+  function highlight(node) {
+    if (node.nodeType == 3) {
+      var val = node.nodeValue;
+      var pos = val.toLowerCase().indexOf(text);
+      if (pos >= 0 && !jQuery.className.has(node.parentNode, className)) {
+        var span = document.createElement("span");
+        span.className = className;
+        span.appendChild(document.createTextNode(val.substr(pos, text.length)));
+        node.parentNode.insertBefore(span, node.parentNode.insertBefore(
+          document.createTextNode(val.substr(pos + text.length)),
+          node.nextSibling));
+        node.nodeValue = val.substr(0, pos);
+      }
+    }
+    else if (!jQuery(node).is("button, select, textarea")) {
+      jQuery.each(node.childNodes, function() {
+        highlight(this)
+      });
+    }
+  }
+  return this.each(function() {
+    highlight(this);
+  });
+}
+
+/**
+ * Small JavaScript module for the documentation.
+ */
+var Documentation = {
+
+  init : function() {
+    /* this.addContextElements(); -- now done statically */
+    this.fixFirefoxAnchorBug();
+    this.highlightSearchWords();
+    this.initModIndex();
+    this.initComments();
+  },
+
+  /**
+   * add context elements like header anchor links
+   */
+  addContextElements : function() {
+    for (var i = 1; i <= 6; i++) {
+      $('h' + i + '[@id]').each(function() {
+        $('<a class="headerlink">\u00B6</a>').
+        attr('href', '#' + this.id).
+        attr('title', 'Permalink to this headline').
+        appendTo(this);
+      });
+    }
+    $('dt[@id]').each(function() {
+      $('<a class="headerlink">\u00B6</a>').
+      attr('href', '#' + this.id).
+      attr('title', 'Permalink to this definition').
+      appendTo(this);
+    });
+  },
+
+  /**
+   * workaround a firefox stupidity
+   */
+  fixFirefoxAnchorBug : function() {
+    if (document.location.hash && $.browser.mozilla)
+      window.setTimeout(function() {
+        document.location.href += '';
+      }, 10);
+  },
+
+  /**
+   * highlight the search words provided in the url in the text
+   */
+  highlightSearchWords : function() {
+    var params = $.getQueryParameters();
+    var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
+    if (terms.length) {
+      var body = $('div.body');
+      window.setTimeout(function() {
+        $.each(terms, function() {
+          body.highlightText(this.toLowerCase(), 'highlight');
+        });
+      }, 10);
+      $('<li class="highlight-link"><a href="javascript:Documentation.' +
+        'hideSearchWords()">Hide Search Matches</a></li>')
+          .appendTo($('.sidebar .this-page-menu'));
+    }
+  },
+
+  /**
+   * init the modindex toggle buttons
+   */
+  initModIndex : function() {
+    var togglers = $('img.toggler').click(function() {
+      var src = $(this).attr('src');
+      var idnum = $(this).attr('id').substr(7);
+      console.log($('tr.cg-' + idnum).toggle());
+      if (src.substr(-9) == 'minus.png')
+        $(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
+      else
+        $(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
+    }).css('display', '');
+    if (DOCUMENTATION_OPTIONS.COLLAPSE_MODINDEX) {
+        togglers.click();
+    }
+  },
+
+  /**
+   * init the inline comments
+   */
+  initComments : function() {
+    $('.inlinecomments div.actions').each(function() {
+      this.innerHTML += ' | ';
+      $(this).append($('<a href="#">hide comments</a>').click(function() {
+        $(this).parent().parent().toggle();
+        return false;
+      }));
+    });
+    $('.inlinecomments .comments').hide();
+    $('.inlinecomments a.bubble').each(function() {
+      $(this).click($(this).is('.emptybubble') ? function() {
+          var params = $.getQueryParameters(this.href);
+          Documentation.newComment(params.target[0]);
+          return false;
+        } : function() {
+          $('.comments', $(this).parent().parent()[0]).toggle();
+          return false;
+      });
+    });
+    $('#comments div.actions a.newcomment').click(function() {
+      Documentation.newComment();
+      return false;
+    });
+    if (document.location.hash.match(/^#comment-/))
+      $('.inlinecomments .comments ' + document.location.hash)
+        .parent().toggle();
+  },
+
+  /**
+   * helper function to hide the search marks again
+   */
+  hideSearchWords : function() {
+    $('.sidebar .this-page-menu li.highlight-link').fadeOut(300);
+    $('span.highlight').removeClass('highlight');
+  },
+
+  /**
+   * show the comment window for a certain id or the whole page.
+   */
+  newComment : function(id) {
+    Documentation.CommentWindow.openFor(id || '');
+  },
+
+  /**
+   * write a new comment from within a comment view box
+   */
+  newCommentFromBox : function(link) {
+    var params = $.getQueryParameters(link.href);
+    $(link).parent().parent().fadeOut('slow');
+    this.newComment(params.target);
+  },
+
+  /**
+   * make the url absolute
+   */
+  makeURL : function(relativeURL) {
+    return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
+  },
+
+  /**
+   * get the current relative url
+   */
+  getCurrentURL : function() {
+    var path = document.location.pathname;
+    var parts = path.split(/\//);
+    $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
+      if (this == '..')
+        parts.pop();
+    });
+    var url = parts.join('/');
+    return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
+  },
+
+  /**
+   * class that represents the comment window
+   */
+  CommentWindow : (function() {
+    var openWindows = {};
+
+    var Window = function(sectionID) {
+      this.url = Documentation.makeURL('@comments/' + Documentation.getCurrentURL()
+        + '/?target=' + $.urlencode(sectionID) + '&mode=ajax');
+      this.sectionID = sectionID;
+
+      this.root = $('<div class="commentwindow"></div>');
+      this.root.appendTo($('body'));
+      this.title = $('<h3>New Comment</h3>').appendTo(this.root);
+      this.body = $('<div class="form">please wait...</div>').appendTo(this.root);
+      this.resizeHandle = $('<div class="resizehandle"></div>').appendTo(this.root);
+
+      this.root.Draggable({
+        handle:       this.title[0]
+      });
+
+      this.root.css({
+        left:         window.innerWidth / 2 - $(this.root).width() / 2,
+        top:          window.scrollY + (window.innerHeight / 2 - 150)
+      });
+      this.root.fadeIn('slow');
+      this.updateView();
+    };
+
+    Window.prototype.updateView = function(data) {
+      var self = this;
+      function update(data) {
+        if (data.posted) {
+          document.location.hash = '#comment-' + data.commentID;
+          document.location.reload();
+        }
+        else {
+          self.body.html(data.body);
+          $('div.actions', self.body).append($('<input>')
+            .attr('type', 'button')
+            .attr('value', 'Close')
+            .click(function() { self.close(); })
+          );
+          $('div.actions input[@name="preview"]')
+            .attr('type', 'button')
+            .click(function() { self.submitForm($('form', self.body)[0], true); });
+          $('form', self.body).bind("submit", function() {
+            self.submitForm(this);
+            return false;
+          });
+
+          if (data.error) {
+            self.root.Highlight(1000, '#aadee1');
+            $('div.error', self.root).slideDown(500);
+          }
+        }
+      }
+
+      if (typeof data == 'undefined')
+        $.getJSON(this.url, function(json) { update(json); });
+      else
+        $.ajax({
+          url:      this.url,
+          type:     'POST',
+          dataType: 'json',
+          data:     data,
+          success:  function(json) { update(json); }
+        });
+    }
+
+    Window.prototype.getFormValue = function(name) {
+      return $('*[@name="' + name + '"]', this.body)[0].value;
+    }
+
+    Window.prototype.submitForm = function(form, previewMode) {
+      this.updateView({
+        author:         form.author.value,
+        author_mail:    form.author_mail.value,
+        title:          form.title.value,
+        comment_body:   form.comment_body.value,
+        preview:        previewMode ? 'yes' : ''
+      });
+    }
+
+    Window.prototype.close = function() {
+      var self = this;
+      delete openWindows[this.sectionID];
+      this.root.fadeOut('slow', function() {
+        self.root.remove();
+      });
+    }
+
+    Window.openFor = function(sectionID) {
+      if (sectionID in openWindows)
+        return openWindows[sectionID];
+      return new Window(sectionID);
+    }
+
+    return Window;
+  })()
+};
+
+
+$(document).ready(function() {
+  Documentation.init();
+});

Added: zc.async/trunk/htmldocs/1.5.0/_static/file.png
===================================================================
(Binary files differ)


Property changes on: zc.async/trunk/htmldocs/1.5.0/_static/file.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: zc.async/trunk/htmldocs/1.5.0/_static/interface.js
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_static/interface.js	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_static/interface.js	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,8 @@
+/*
+ * Interface elements for jQuery - http://interface.eyecon.ro
+ *
+ * Copyright (c) 2006 Stefan Petre
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ */
+ eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('k.1a={2R:u(e){D x=0;D y=0;D 5H=I;D es=e.18;if(k(e).B(\'19\')==\'1n\'){62=es.3j;9C=es.Y;es.3j=\'2O\';es.19=\'2E\';es.Y=\'1O\';5H=1b}D el=e;7o(el){x+=el.8n+(el.4Y&&!k.3h.7N?T(el.4Y.5a)||0:0);y+=el.8t+(el.4Y&&!k.3h.7N?T(el.4Y.4Z)||0:0);el=el.dr}el=e;7o(el&&el.4S&&el.4S.5Z()!=\'2e\'){x-=el.3g||0;y-=el.2V||0;el=el.3e}if(5H){es.19=\'1n\';es.Y=9C;es.3j=62}E{x:x,y:y}},bN:u(el){D x=0,y=0;7o(el){x+=el.8n||0;y+=el.8t||0;el=el.dr}E{x:x,y:y}},2p:u(e){D w=k.B(e,\'Z\');D h=k.B(e,\'V\');D 1D=0;D hb=0;D es=e.18;if(k(e).B(\'19\')!=\'1n\'){1D=e.4b;hb=e.63}P{62=es.3j;9C=es.Y;es.3j=\'2O\';es.19=\'2E\';es.Y=\'1O\';1D=e.4b;hb=e.63;es.19=\'1n\';es.Y=9C;es.3j=62}E{w:w,h:h,1D
 :1D,hb:hb}},82:u(el){E{1D:el.4b||0,hb:el.63||0}},bq:u(e){D h,w,de;if(e){w=e.8k;h=e.8z}P{de=1j.4J;w=1V.d0||9B.d0||(de&&de.8k)||1j.2e.8k;h=1V.d1||9B.d1||(de&&de.8z)||1j.2e.8z}E{w:w,h:h}},6W:u(e){D t,l,w,h,iw,ih;if(e&&e.9A.5Z()!=\'2e\'){t=e.2V;l=e.3g;w=e.cY;h=e.cW;iw=0;ih=0}P{if(1j.4J&&1j.4J.2V){t=1j.4J.2V;l=1j.4J.3g;w=1j.4J.cY;h=1j.4J.cW}P if(1j.2e){t=1j.2e.2V;l=1j.2e.3g;w=1j.2e.cY;h=1j.2e.cW}iw=9B.d0||1j.4J.8k||1j.2e.8k||0;ih=9B.d1||1j.4J.8z||1j.2e.8z||0}E{t:t,l:l,w:w,h:h,iw:iw,ih:ih}},c8:u(e,7C){D el=k(e);D t=el.B(\'5o\')||\'\';D r=el.B(\'5p\')||\'\';D b=el.B(\'5m\')||\'\';D l=el.B(\'5k\')||\'\';if(7C)E{t:T(t)||0,r:T(r)||0,b:T(b)||0,l:T(l)};P E{t:t,r:r,b:b,l:l}},aj:u(e,7C){D el=k(e);D t=el.B(\'66\')||\'\';D r=el.B(\'6j\')||\'\';D b=el.B(\'5M\')||\'\';D l=el.B(\'4X\')||\'\';if(7C)E{t:T(t)||0,r:T(r)||0,b:T(b)||0,l:T(l)};P E{t:t,r:r,b:b,l:l}},6h:u(e,7C){D el=k(e);D t=el.B(\'4Z\')||\'\';D r=el.B(\'6k\')||\'\';D b=el.B(\'6g\')||\'\';D l=el.B(\'5a\')||\'\';if(7C)E{t:T(t)||0,r:T(r)
 ||0,b:T(b)||0,l:T(l)||0};P E{t:t,r:r,b:b,l:l}},44:u(2l){D x=2l.hI||(2l.hK+(1j.4J.3g||1j.2e.3g))||0;D y=2l.hL||(2l.hM+(1j.4J.2V||1j.2e.2V))||0;E{x:x,y:y}},cS:u(54,cT){cT(54);54=54.77;7o(54){k.1a.cS(54,cT);54=54.hU}},i1:u(54){k.1a.cS(54,u(el){1Y(D 1p in el){if(2h el[1p]===\'u\'){el[1p]=U}}})},i3:u(el,1N){D 5C=$.1a.6W();D d3=$.1a.2p(el);if(!1N||1N==\'4i\')$(el).B({Q:5C.t+((14.3v(5C.h,5C.ih)-5C.t-d3.hb)/2)+\'S\'});if(!1N||1N==\'4a\')$(el).B({O:5C.l+((14.3v(5C.w,5C.iw)-5C.l-d3.1D)/2)+\'S\'})},i0:u(el,dP){D 1Q=$(\'1U[@2M*="95"]\',el||1j),95;1Q.1B(u(){95=q.2M;q.2M=dP;q.18.69="aw:ax.ay.hZ(2M=\'"+95+"\')"})}};[].3F||(7b.hV.3F=u(v,n){n=(n==U)?0:n;D m=q.1h;1Y(D i=n;i<m;i++)if(q[i]==v)E i;E-1});k.4O=u(e){if(/^hW$|^hX$|^hY$|^6v$|^hH$|^hG$|^hp$|^hq$|^hs$|^2e$|^ht$|^ho$|^hn$|^hj$|^hi$|^hk$|^hl$/i.43(e.9A))E I;P E 1b};k.fx.9g=u(e,65){D c=e.77;D cs=c.18;cs.Y=65.Y;cs.5o=65.3A.t;cs.5k=65.3A.l;cs.5m=65.3A.b;cs.5p=65.3A.r;cs.Q=65.Q+\'S\';cs.O=65.O+\'S\';e.3e.dk(c,e);e.3e.hu(e)};k.fx.9h=u(e){if(!
 k.4O(e))E I;D t=k(e);D es=e.18;D 5H=I;D W={};W.Y=t.B(\'Y\');if(t.B(\'19\')==\'1n\'){62=t.B(\'3j\');es.3j=\'2O\';es.19=\'\';5H=1b}W.1q=k.1a.2p(e);W.3A=k.1a.c8(e);D d7=e.4Y?e.4Y.dM:t.B(\'hv\');W.Q=T(t.B(\'Q\'))||0;W.O=T(t.B(\'O\'))||0;D dC=\'hC\'+T(14.6w()*cd);D 6C=1j.3t(/^1U$|^br$|^hD$|^hr$|^8Z$|^hE$|^8i$|^3E$|^hF$|^hB$|^hA$|^aX$|^dl$|^hw$/i.43(e.9A)?\'26\':e.9A);k.1p(6C,\'id\',dC);6C.3b=\'hy\';D 3C=6C.18;D Q=0;D O=0;if(W.Y==\'2y\'||W.Y==\'1O\'){Q=W.Q;O=W.O}3C.19=\'1n\';3C.Q=Q+\'S\';3C.O=O+\'S\';3C.Y=W.Y!=\'2y\'&&W.Y!=\'1O\'?\'2y\':W.Y;3C.2Y=\'2O\';3C.V=W.1q.hb+\'S\';3C.Z=W.1q.1D+\'S\';3C.5o=W.3A.t;3C.5p=W.3A.r;3C.5m=W.3A.b;3C.5k=W.3A.l;if(k.3h.4I){3C.dM=d7}P{3C.i5=d7}e.3e.dk(6C,e);es.5o=\'3c\';es.5p=\'3c\';es.5m=\'3c\';es.5k=\'3c\';es.Y=\'1O\';es.dV=\'1n\';es.Q=\'3c\';es.O=\'3c\';if(5H){es.19=\'1n\';es.3j=62}6C.iK(e);3C.19=\'2E\';E{W:W,3o:k(6C)}};k.fx.8m={iM:[0,1X,1X],iI:[dJ,1X,1X],iH:[dG,dG,iD],iC:[0,0,0],iE:[0,0,1X],iF:[dE,42,42],iG:[0,1X,1X],iN:[0,0,7B],iO:[0,7B,7B],iV:[c
 n,cn,cn],iX:[0,2b,0],iY:[iU,iT,da],iP:[7B,0,7B],iQ:[85,da,47],iS:[1X,dI,0],iB:[iA,50,ig],ii:[7B,0,0],ij:[ik,fd,ie],ic:[i8,0,9y],i7:[1X,0,1X],i9:[1X,hh,0],ia:[0,6F,0],ib:[75,0,il],im:[dJ,dK,dI],iy:[iz,iu,dK],io:[dA,1X,1X],ip:[dL,iq,dL],iZ:[9y,9y,9y],gn:[1X,gr,gl],gq:[1X,1X,dA],gs:[0,1X,0],gj:[1X,0,1X],gh:[6F,0,0],gi:[0,0,6F],gd:[6F,6F,0],ge:[1X,dE,0],gf:[1X,9z,gk],gu:[6F,0,6F],gp:[1X,0,0],gv:[9z,9z,9z],gg:[1X,1X,1X],hg:[1X,1X,0]};k.fx.6H=u(4C,dH){if(k.fx.8m[4C])E{r:k.fx.8m[4C][0],g:k.fx.8m[4C][1],b:k.fx.8m[4C][2]};P if(2W=/^7K\\(\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*\\)$/.9D(4C))E{r:T(2W[1]),g:T(2W[2]),b:T(2W[3])};P if(2W=/7K\\(\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*\\)$/.9D(4C))E{r:2m(2W[1])*2.55,g:2m(2W[2])*2.55,b:2m(2W[3])*2.55};P if(2W=/^#([a-fA-7y-9])([a-fA-7y-9])([a-fA-7y-9])$/.9D(4C))E{r:T("7z"+2W[1]+2W[1]),g:T("7z"+2W[2]+2W[2]),b:T("7z"+2W[3]+2W[3])};P if(2W=/^#([a-fA-7y-9]{2})([a-fA
 -7y-9]{2})([a-fA-7y-9]{2})$/.9D(4C))E{r:T("7z"+2W[1]),g:T("7z"+2W[2]),b:T("7z"+2W[3])};P E dH==1b?I:{r:1X,g:1X,b:1X}};k.fx.d8={6g:1,5a:1,6k:1,4Z:1,4l:1,4w:1,V:1,O:1,cH:1,h3:1,5m:1,5k:1,5p:1,5o:1,8M:1,6q:1,8L:1,9s:1,1J:1,h0:1,gZ:1,5M:1,4X:1,6j:1,66:1,2N:1,gV:1,Q:1,Z:1,3B:1};k.fx.d9={7f:1,gW:1,gX:1,gY:1,h4:1,4C:1,h5:1};k.fx.8p=[\'gw\',\'hd\',\'he\',\'hf\'];k.fx.cw={\'cD\':[\'2B\',\'ds\'],\'9I\':[\'2B\',\'cq\'],\'6X\':[\'6X\',\'\'],\'92\':[\'92\',\'\']};k.fn.21({5K:u(5U,H,G,J){E q.1w(u(){D 9E=k.H(H,G,J);D e=11 k.dg(q,9E,5U)})},cK:u(H,J){E q.1w(u(){D 9E=k.H(H,J);D e=11 k.cK(q,9E)})},8v:u(2D){E q.1B(u(){if(q.5R)k.cv(q,2D)})},ha:u(2D){E q.1B(u(){if(q.5R)k.cv(q,2D);if(q.1w&&q.1w[\'fx\'])q.1w.fx=[]})}});k.21({cK:u(2i,M){D z=q,3u;z.2D=u(){if(k.eI(M.23))M.23.1F(2i)};z.2H=6I(u(){z.2D()},M.1m);2i.5R=z},G:{bV:u(p,n,1W,1I,1m){E((-14.5v(p*14.2Q)/2)+0.5)*1I+1W}},dg:u(2i,M,5U){D z=q,3u;D y=2i.18;D eH=k.B(2i,"2Y");D 7M=k.B(2i,"19");D 2k={};z.9x=(11 72()).71();M.G=M.G&&k.G[M.G]?M.G:\'bV\';z.9F
 =u(2z,49){if(k.fx.d8[2z]){if(49==\'22\'||49==\'2G\'||49==\'3Y\'){if(!2i.6u)2i.6u={};D r=2m(k.3M(2i,2z));2i.6u[2z]=r&&r>-cd?r:(2m(k.B(2i,2z))||0);49=49==\'3Y\'?(7M==\'1n\'?\'22\':\'2G\'):49;M[49]=1b;2k[2z]=49==\'22\'?[0,2i.6u[2z]]:[2i.6u[2z],0];if(2z!=\'1J\')y[2z]=2k[2z][0]+(2z!=\'3B\'&&2z!=\'8h\'?\'S\':\'\');P k.1p(y,"1J",2k[2z][0])}P{2k[2z]=[2m(k.3M(2i,2z)),2m(49)||0]}}P if(k.fx.d9[2z])2k[2z]=[k.fx.6H(k.3M(2i,2z)),k.fx.6H(49)];P if(/^6X$|92$|2B$|9I$|cD$/i.43(2z)){D m=49.4v(/\\s+/g,\' \').4v(/7K\\s*\\(\\s*/g,\'7K(\').4v(/\\s*,\\s*/g,\',\').4v(/\\s*\\)/g,\')\').bU(/([^\\s]+)/g);3m(2z){1e\'6X\':1e\'92\':1e\'cD\':1e\'9I\':m[3]=m[3]||m[1]||m[0];m[2]=m[2]||m[0];m[1]=m[1]||m[0];1Y(D i=0;i<k.fx.8p.1h;i++){D 5X=k.fx.cw[2z][0]+k.fx.8p[i]+k.fx.cw[2z][1];2k[5X]=2z==\'9I\'?[k.fx.6H(k.3M(2i,5X)),k.fx.6H(m[i])]:[2m(k.3M(2i,5X)),2m(m[i])]}1r;1e\'2B\':1Y(D i=0;i<m.1h;i++){D cC=2m(m[i]);D 9H=!h8(cC)?\'ds\':(!/b7|1n|2O|gT|gF|gG|gH|gE|gD|gz|gA/i.43(m[i])?\'cq\':I);if(9H){1Y(D j=0;j<k.fx.8p.1h;
 j++){5X=\'2B\'+k.fx.8p[j]+9H;2k[5X]=9H==\'cq\'?[k.fx.6H(k.3M(2i,5X)),k.fx.6H(m[i])]:[2m(k.3M(2i,5X)),cC]}}P{y[\'gQ\']=m[i]}}1r}}P{y[2z]=49}E I};1Y(p in 5U){if(p==\'18\'){D 5u=k.cu(5U[p]);1Y(7L in 5u){q.9F(7L,5u[7L])}}P if(p==\'3b\'){if(1j.9G)1Y(D i=0;i<1j.9G.1h;i++){D 7G=1j.9G[i].7G||1j.9G[i].gP||U;if(7G){1Y(D j=0;j<7G.1h;j++){if(7G[j].gO==\'.\'+5U[p]){D 7H=11 cp(\'\\.\'+5U[p]+\' {\');D 5S=7G[j].18.9T;D 5u=k.cu(5S.4v(7H,\'\').4v(/}/g,\'\'));1Y(7L in 5u){q.9F(7L,5u[7L])}}}}}}P{q.9F(p,5U[p])}}y.19=7M==\'1n\'?\'2E\':7M;y.2Y=\'2O\';z.2D=u(){D t=(11 72()).71();if(t>M.1m+z.9x){6c(z.2H);z.2H=U;1Y(p in 2k){if(p=="1J")k.1p(y,"1J",2k[p][1]);P if(2h 2k[p][1]==\'8i\')y[p]=\'7K(\'+2k[p][1].r+\',\'+2k[p][1].g+\',\'+2k[p][1].b+\')\';P y[p]=2k[p][1]+(p!=\'3B\'&&p!=\'8h\'?\'S\':\'\')}if(M.2G||M.22)1Y(D p in 2i.6u)if(p=="1J")k.1p(y,p,2i.6u[p]);P y[p]="";y.19=M.2G?\'1n\':(7M!=\'1n\'?7M:\'2E\');y.2Y=eH;2i.5R=U;if(k.eI(M.23))M.23.1F(2i)}P{D n=t-q.9x;D 8x=n/M.1m;1Y(p in 2k){if(2h 2k[p][1]==\'8i\'
 ){y[p]=\'7K(\'+T(k.G[M.G](8x,n,2k[p][0].r,(2k[p][1].r-2k[p][0].r),M.1m))+\',\'+T(k.G[M.G](8x,n,2k[p][0].g,(2k[p][1].g-2k[p][0].g),M.1m))+\',\'+T(k.G[M.G](8x,n,2k[p][0].b,(2k[p][1].b-2k[p][0].b),M.1m))+\')\'}P{D cG=k.G[M.G](8x,n,2k[p][0],(2k[p][1]-2k[p][0]),M.1m);if(p=="1J")k.1p(y,"1J",cG);P y[p]=cG+(p!=\'3B\'&&p!=\'8h\'?\'S\':\'\')}}}};z.2H=6I(u(){z.2D()},13);2i.5R=z},cv:u(2i,2D){if(2D)2i.5R.9x-=kM;P{1V.6c(2i.5R.2H);2i.5R=U;k.2L(2i,"fx")}}});k.cu=u(5S){D 5u={};if(2h 5S==\'5g\'){5S=5S.5Z().7h(\';\');1Y(D i=0;i<5S.1h;i++){7H=5S[i].7h(\':\');if(7H.1h==2){5u[k.eP(7H[0].4v(/\\-(\\w)/g,u(m,c){E c.kn()}))]=k.eP(7H[1])}}}E 5u};k.12={1c:U,F:U,58:u(){E q.1B(u(){if(q.9q){q.A.5e.3p(\'5b\',k.12.cU);q.A=U;q.9q=I;if(k.3h.4I){q.d4="fQ"}P{q.18.kk=\'\';q.18.ej=\'\';q.18.e6=\'\'}}})},cU:u(e){if(k.12.F!=U){k.12.9w(e);E I}D C=q.3Z;k(1j).1H(\'3H\',k.12.d6).1H(\'61\',k.12.9w);C.A.1s=k.1a.44(e);C.A.4t=C.A.1s;C.A.7W=I;C.A.ki=q!=q.3Z;k.12.F=C;if(C.A.5i&&q!=q.3Z){ce=k.1a.2R(C.3e);cf=k.1a.2p(C);cg={x:T
 (k.B(C,\'O\'))||0,y:T(k.B(C,\'Q\'))||0};dx=C.A.4t.x-ce.x-cf.1D/2-cg.x;dy=C.A.4t.y-ce.y-cf.hb/2-cg.y;k.3d.59(C,[dx,dy])}E k.7Z||I},dT:u(e){D C=k.12.F;C.A.7W=1b;D 9p=C.18;C.A.7i=k.B(C,\'19\');C.A.4m=k.B(C,\'Y\');if(!C.A.c4)C.A.c4=C.A.4m;C.A.2c={x:T(k.B(C,\'O\'))||0,y:T(k.B(C,\'Q\'))||0};C.A.9l=0;C.A.9m=0;if(k.3h.4I){D cl=k.1a.6h(C,1b);C.A.9l=cl.l||0;C.A.9m=cl.t||0}C.A.1C=k.21(k.1a.2R(C),k.1a.2p(C));if(C.A.4m!=\'2y\'&&C.A.4m!=\'1O\'){9p.Y=\'2y\'}k.12.1c.5t();D 5s=C.dn(1b);k(5s).B({19:\'2E\',O:\'3c\',Q:\'3c\'});5s.18.5o=\'0\';5s.18.5p=\'0\';5s.18.5m=\'0\';5s.18.5k=\'0\';k.12.1c.1R(5s);D 3X=k.12.1c.K(0).18;if(C.A.cO){3X.Z=\'ao\';3X.V=\'ao\'}P{3X.V=C.A.1C.hb+\'S\';3X.Z=C.A.1C.1D+\'S\'}3X.19=\'2E\';3X.5o=\'3c\';3X.5p=\'3c\';3X.5m=\'3c\';3X.5k=\'3c\';k.21(C.A.1C,k.1a.2p(5s));if(C.A.2S){if(C.A.2S.O){C.A.2c.x+=C.A.1s.x-C.A.1C.x-C.A.2S.O;C.A.1C.x=C.A.1s.x-C.A.2S.O}if(C.A.2S.Q){C.A.2c.y+=C.A.1s.y-C.A.1C.y-C.A.2S.Q;C.A.1C.y=C.A.1s.y-C.A.2S.Q}if(C.A.2S.2N){C.A.2c.x+=C.A.1s.x-C.A.1C.x-C.A.
 1C.hb+C.A.2S.2N;C.A.1C.x=C.A.1s.x-C.A.1C.1D+C.A.2S.2N}if(C.A.2S.4l){C.A.2c.y+=C.A.1s.y-C.A.1C.y-C.A.1C.hb+C.A.2S.4l;C.A.1C.y=C.A.1s.y-C.A.1C.hb+C.A.2S.4l}}C.A.2x=C.A.2c.x;C.A.2r=C.A.2c.y;if(C.A.8g||C.A.2o==\'96\'){89=k.1a.6h(C.3e,1b);C.A.1C.x=C.8n+(k.3h.4I?0:k.3h.7N?-89.l:89.l);C.A.1C.y=C.8t+(k.3h.4I?0:k.3h.7N?-89.t:89.t);k(C.3e).1R(k.12.1c.K(0))}if(C.A.2o){k.12.bP(C);C.A.5J.2o=k.12.bH}if(C.A.5i){k.3d.bO(C)}3X.O=C.A.1C.x-C.A.9l+\'S\';3X.Q=C.A.1C.y-C.A.9m+\'S\';3X.Z=C.A.1C.1D+\'S\';3X.V=C.A.1C.hb+\'S\';k.12.F.A.9n=I;if(C.A.gx){C.A.5J.67=k.12.bI}if(C.A.3B!=I){k.12.1c.B(\'3B\',C.A.3B)}if(C.A.1J){k.12.1c.B(\'1J\',C.A.1J);if(1V.7a){k.12.1c.B(\'69\',\'9V(1J=\'+C.A.1J*2b+\')\')}}if(C.A.7w){k.12.1c.2Z(C.A.7w);k.12.1c.K(0).77.18.19=\'1n\'}if(C.A.4A)C.A.4A.1F(C,[5s,C.A.2c.x,C.A.2c.y]);if(k.1x&&k.1x.8W>0){k.1x.ea(C)}if(C.A.4j==I){9p.19=\'1n\'}E I},bP:u(C){if(C.A.2o.1K==b5){if(C.A.2o==\'96\'){C.A.24=k.21({x:0,y:0},k.1a.2p(C.3e));D 84=k.1a.6h(C.3e,1b);C.A.24.w=C.A.24.1D-84.l-84.r;C.A.24.
 h=C.A.24.hb-84.t-84.b}P if(C.A.2o==\'1j\'){D cM=k.1a.bq();C.A.24={x:0,y:0,w:cM.w,h:cM.h}}}P if(C.A.2o.1K==7b){C.A.24={x:T(C.A.2o[0])||0,y:T(C.A.2o[1])||0,w:T(C.A.2o[2])||0,h:T(C.A.2o[3])||0}}C.A.24.dx=C.A.24.x-C.A.1C.x;C.A.24.dy=C.A.24.y-C.A.1C.y},9o:u(F){if(F.A.8g||F.A.2o==\'96\'){k(\'2e\',1j).1R(k.12.1c.K(0))}k.12.1c.5t().2G().B(\'1J\',1);if(1V.7a){k.12.1c.B(\'69\',\'9V(1J=2b)\')}},9w:u(e){k(1j).3p(\'3H\',k.12.d6).3p(\'61\',k.12.9w);if(k.12.F==U){E}D F=k.12.F;k.12.F=U;if(F.A.7W==I){E I}if(F.A.48==1b){k(F).B(\'Y\',F.A.4m)}D 9p=F.18;if(F.5i){k.12.1c.B(\'94\',\'8C\')}if(F.A.7w){k.12.1c.4p(F.A.7w)}if(F.A.6o==I){if(F.A.fx>0){if(!F.A.1N||F.A.1N==\'4a\'){D x=11 k.fx(F,{1m:F.A.fx},\'O\');x.1L(F.A.2c.x,F.A.8c)}if(!F.A.1N||F.A.1N==\'4i\'){D y=11 k.fx(F,{1m:F.A.fx},\'Q\');y.1L(F.A.2c.y,F.A.8j)}}P{if(!F.A.1N||F.A.1N==\'4a\')F.18.O=F.A.8c+\'S\';if(!F.A.1N||F.A.1N==\'4i\')F.18.Q=F.A.8j+\'S\'}k.12.9o(F);if(F.A.4j==I){k(F).B(\'19\',F.A.7i)}}P if(F.A.fx>0){F.A.9n=1b;D dh=I;if(k.1x&&k.1t&&F
 .A.48){dh=k.1a.2R(k.1t.1c.K(0))}k.12.1c.5K({O:dh?dh.x:F.A.1C.x,Q:dh?dh.y:F.A.1C.y},F.A.fx,u(){F.A.9n=I;if(F.A.4j==I){F.18.19=F.A.7i}k.12.9o(F)})}P{k.12.9o(F);if(F.A.4j==I){k(F).B(\'19\',F.A.7i)}}if(k.1x&&k.1x.8W>0){k.1x.ed(F)}if(k.1t&&F.A.48){k.1t.dp(F)}if(F.A.2T&&(F.A.8c!=F.A.2c.x||F.A.8j!=F.A.2c.y)){F.A.2T.1F(F,F.A.bQ||[0,0,F.A.8c,F.A.8j])}if(F.A.3S)F.A.3S.1F(F);E I},bI:u(x,y,dx,dy){if(dx!=0)dx=T((dx+(q.A.gx*dx/14.3R(dx))/2)/q.A.gx)*q.A.gx;if(dy!=0)dy=T((dy+(q.A.gy*dy/14.3R(dy))/2)/q.A.gy)*q.A.gy;E{dx:dx,dy:dy,x:0,y:0}},bH:u(x,y,dx,dy){dx=14.3D(14.3v(dx,q.A.24.dx),q.A.24.w+q.A.24.dx-q.A.1C.1D);dy=14.3D(14.3v(dy,q.A.24.dy),q.A.24.h+q.A.24.dy-q.A.1C.hb);E{dx:dx,dy:dy,x:0,y:0}},d6:u(e){if(k.12.F==U||k.12.F.A.9n==1b){E}D F=k.12.F;F.A.4t=k.1a.44(e);if(F.A.7W==I){46=14.dm(14.5Y(F.A.1s.x-F.A.4t.x,2)+14.5Y(F.A.1s.y-F.A.4t.y,2));if(46<F.A.6m){E}P{k.12.dT(e)}}D dx=F.A.4t.x-F.A.1s.x;D dy=F.A.4t.y-F.A.1s.y;1Y(D i in F.A.5J){D 3q=F.A.5J[i].1F(F,[F.A.2c.x+dx,F.A.2c.y+dy,dx,dy]);if(3q&&3
 q.1K==7n){dx=i!=\'7l\'?3q.dx:(3q.x-F.A.2c.x);dy=i!=\'7l\'?3q.dy:(3q.y-F.A.2c.y)}}F.A.2x=F.A.1C.x+dx-F.A.9l;F.A.2r=F.A.1C.y+dy-F.A.9m;if(F.A.5i&&(F.A.3z||F.A.2T)){k.3d.3z(F,F.A.2x,F.A.2r)}if(F.A.4x)F.A.4x.1F(F,[F.A.2c.x+dx,F.A.2c.y+dy]);if(!F.A.1N||F.A.1N==\'4a\'){F.A.8c=F.A.2c.x+dx;k.12.1c.K(0).18.O=F.A.2x+\'S\'}if(!F.A.1N||F.A.1N==\'4i\'){F.A.8j=F.A.2c.y+dy;k.12.1c.K(0).18.Q=F.A.2r+\'S\'}if(k.1x&&k.1x.8W>0){k.1x.a3(F)}E I},2s:u(o){if(!k.12.1c){k(\'2e\',1j).1R(\'<26 id="dW"></26>\');k.12.1c=k(\'#dW\');D el=k.12.1c.K(0);D 4P=el.18;4P.Y=\'1O\';4P.19=\'1n\';4P.94=\'8C\';4P.dV=\'1n\';4P.2Y=\'2O\';if(1V.7a){el.d4="en"}P{4P.kh=\'1n\';4P.e6=\'1n\';4P.ej=\'1n\'}}if(!o){o={}}E q.1B(u(){if(q.9q||!k.1a)E;if(1V.7a){q.kf=u(){E I};q.kj=u(){E I}}D el=q;D 5e=o.3y?k(q).kp(o.3y):k(q);if(k.3h.4I){5e.1B(u(){q.d4="en"})}P{5e.B(\'-kE-7l-8Z\',\'1n\');5e.B(\'7l-8Z\',\'1n\');5e.B(\'-ko-7l-8Z\',\'1n\')}q.A={5e:5e,6o:o.6o?1b:I,4j:o.4j?1b:I,48:o.48?o.48:I,5i:o.5i?o.5i:I,8g:o.8g?o.8g:I,3B:o.3B?T(o.3B)||
 0:I,1J:o.1J?2m(o.1J):I,fx:T(o.fx)||U,6p:o.6p?o.6p:I,5J:{},1s:{},4A:o.4A&&o.4A.1K==2C?o.4A:I,3S:o.3S&&o.3S.1K==2C?o.3S:I,2T:o.2T&&o.2T.1K==2C?o.2T:I,1N:/4i|4a/.43(o.1N)?o.1N:I,6m:o.6m?T(o.6m)||0:0,2S:o.2S?o.2S:I,cO:o.cO?1b:I,7w:o.7w||I};if(o.5J&&o.5J.1K==2C)q.A.5J.7l=o.5J;if(o.4x&&o.4x.1K==2C)q.A.4x=o.4x;if(o.2o&&((o.2o.1K==b5&&(o.2o==\'96\'||o.2o==\'1j\'))||(o.2o.1K==7b&&o.2o.1h==4))){q.A.2o=o.2o}if(o.2K){q.A.2K=o.2K}if(o.67){if(2h o.67==\'kl\'){q.A.gx=T(o.67)||1;q.A.gy=T(o.67)||1}P if(o.67.1h==2){q.A.gx=T(o.67[0])||1;q.A.gy=T(o.67[1])||1}}if(o.3z&&o.3z.1K==2C){q.A.3z=o.3z}q.9q=1b;5e.1B(u(){q.3Z=el});5e.1H(\'5b\',k.12.cU)})}};k.fn.21({a4:k.12.58,6Y:k.12.2s});k.1x={ee:u(5r,5y,7j,7g){E 5r<=k.12.F.A.2x&&(5r+7j)>=(k.12.F.A.2x+k.12.F.A.1C.w)&&5y<=k.12.F.A.2r&&(5y+7g)>=(k.12.F.A.2r+k.12.F.A.1C.h)?1b:I},by:u(5r,5y,7j,7g){E!(5r>(k.12.F.A.2x+k.12.F.A.1C.w)||(5r+7j)<k.12.F.A.2x||5y>(k.12.F.A.2r+k.12.F.A.1C.h)||(5y+7g)<k.12.F.A.2r)?1b:I},1s:u(5r,5y,7j,7g){E 5r<k.12.F.A.4t.x&&(5r+7j)>k.
 12.F.A.4t.x&&5y<k.12.F.A.4t.y&&(5y+7g)>k.12.F.A.4t.y?1b:I},5l:I,3W:{},8W:0,3J:{},ea:u(C){if(k.12.F==U){E}D i;k.1x.3W={};D cZ=I;1Y(i in k.1x.3J){if(k.1x.3J[i]!=U){D 1k=k.1x.3J[i].K(0);if(k(k.12.F).is(\'.\'+1k.1i.a)){if(1k.1i.m==I){1k.1i.p=k.21(k.1a.2R(1k),k.1a.82(1k));1k.1i.m=1b}if(1k.1i.ac){k.1x.3J[i].2Z(1k.1i.ac)}k.1x.3W[i]=k.1x.3J[i];if(k.1t&&1k.1i.s&&k.12.F.A.48){1k.1i.el=k(\'.\'+1k.1i.a,1k);C.18.19=\'1n\';k.1t.c5(1k);1k.1i.9Z=k.1t.8o(k.1p(1k,\'id\')).7U;C.18.19=C.A.7i;cZ=1b}if(1k.1i.9v){1k.1i.9v.1F(k.1x.3J[i].K(0),[k.12.F])}}}}if(cZ){k.1t.28()}},ek:u(){k.1x.3W={};1Y(i in k.1x.3J){if(k.1x.3J[i]!=U){D 1k=k.1x.3J[i].K(0);if(k(k.12.F).is(\'.\'+1k.1i.a)){1k.1i.p=k.21(k.1a.2R(1k),k.1a.82(1k));if(1k.1i.ac){k.1x.3J[i].2Z(1k.1i.ac)}k.1x.3W[i]=k.1x.3J[i];if(k.1t&&1k.1i.s&&k.12.F.A.48){1k.1i.el=k(\'.\'+1k.1i.a,1k);C.18.19=\'1n\';k.1t.c5(1k);C.18.19=C.A.7i}}}}},a3:u(e){if(k.12.F==U){E}k.1x.5l=I;D i;D cb=I;D ec=0;1Y(i in k.1x.3W){D 1k=k.1x.3W[i].K(0);if(k.1x.5l==I&&k.1x[1k.1i.t](1k.1
 i.p.x,1k.1i.p.y,1k.1i.p.1D,1k.1i.p.hb)){if(1k.1i.hc&&1k.1i.h==I){k.1x.3W[i].2Z(1k.1i.hc)}if(1k.1i.h==I&&1k.1i.7T){cb=1b}1k.1i.h=1b;k.1x.5l=1k;if(k.1t&&1k.1i.s&&k.12.F.A.48){k.1t.1c.K(0).3b=1k.1i.eb;k.1t.a3(1k)}ec++}P if(1k.1i.h==1b){if(1k.1i.7Q){1k.1i.7Q.1F(1k,[e,k.12.1c.K(0).77,1k.1i.fx])}if(1k.1i.hc){k.1x.3W[i].4p(1k.1i.hc)}1k.1i.h=I}}if(k.1t&&!k.1x.5l&&k.12.F.48){k.1t.1c.K(0).18.19=\'1n\'}if(cb){k.1x.5l.1i.7T.1F(k.1x.5l,[e,k.12.1c.K(0).77])}},ed:u(e){D i;1Y(i in k.1x.3W){D 1k=k.1x.3W[i].K(0);if(1k.1i.ac){k.1x.3W[i].4p(1k.1i.ac)}if(1k.1i.hc){k.1x.3W[i].4p(1k.1i.hc)}if(1k.1i.s){k.1t.7V[k.1t.7V.1h]=i}if(1k.1i.9r&&1k.1i.h==1b){1k.1i.h=I;1k.1i.9r.1F(1k,[e,1k.1i.fx])}1k.1i.m=I;1k.1i.h=I}k.1x.3W={}},58:u(){E q.1B(u(){if(q.9u){if(q.1i.s){id=k.1p(q,\'id\');k.1t.5j[id]=U;k(\'.\'+q.1i.a,q).a4()}k.1x.3J[\'d\'+q.bn]=U;q.9u=I;q.f=U}})},2s:u(o){E q.1B(u(){if(q.9u==1b||!o.3P||!k.1a||!k.12){E}q.1i={a:o.3P,ac:o.a8||I,hc:o.a7||I,eb:o.4V||I,9r:o.kO||o.9r||I,7T:o.7T||o.dN||I,7Q:o.7Q||o.dz||I,
 9v:o.9v||I,t:o.6n&&(o.6n==\'ee\'||o.6n==\'by\')?o.6n:\'1s\',fx:o.fx?o.fx:I,m:I,h:I};if(o.bD==1b&&k.1t){id=k.1p(q,\'id\');k.1t.5j[id]=q.1i.a;q.1i.s=1b;if(o.2T){q.1i.2T=o.2T;q.1i.9Z=k.1t.8o(id).7U}}q.9u=1b;q.bn=T(14.6w()*cd);k.1x.3J[\'d\'+q.bn]=k(q);k.1x.8W++})}};k.fn.21({df:k.1x.58,dO:k.1x.2s});k.kH=k.1x.ek;k.R={1A:U,3Q:U,F:U,1s:U,1q:U,Y:U,7r:u(e){k.R.F=(q.a2)?q.a2:q;k.R.1s=k.1a.44(e);k.R.1q={Z:T(k(k.R.F).B(\'Z\'))||0,V:T(k(k.R.F).B(\'V\'))||0};k.R.Y={Q:T(k(k.R.F).B(\'Q\'))||0,O:T(k(k.R.F).B(\'O\'))||0};k(1j).1H(\'3H\',k.R.bj).1H(\'61\',k.R.bs);if(2h k.R.F.1g.ei===\'u\'){k.R.F.1g.ei.1F(k.R.F)}E I},bs:u(e){k(1j).3p(\'3H\',k.R.bj).3p(\'61\',k.R.bs);if(2h k.R.F.1g.e7===\'u\'){k.R.F.1g.e7.1F(k.R.F)}k.R.F=U},bj:u(e){if(!k.R.F){E}1s=k.1a.44(e);7u=k.R.Y.Q-k.R.1s.y+1s.y;7v=k.R.Y.O-k.R.1s.x+1s.x;7u=14.3v(14.3D(7u,k.R.F.1g.8U-k.R.1q.V),k.R.F.1g.7s);7v=14.3v(14.3D(7v,k.R.F.1g.8T-k.R.1q.Z),k.R.F.1g.7p);if(2h k.R.F.1g.4x===\'u\'){D 8J=k.R.F.1g.4x.1F(k.R.F,[7v,7u]);if(2h 8J==\'kc\'&&8J.1h=
 =2){7v=8J[0];7u=8J[1]}}k.R.F.18.Q=7u+\'S\';k.R.F.18.O=7v+\'S\';E I},28:u(e){k(1j).1H(\'3H\',k.R.8C).1H(\'61\',k.R.8v);k.R.1A=q.1A;k.R.3Q=q.3Q;k.R.1s=k.1a.44(e);if(k.R.1A.1g.4A){k.R.1A.1g.4A.1F(k.R.1A,[q])}k.R.1q={Z:T(k(q.1A).B(\'Z\'))||0,V:T(k(q.1A).B(\'V\'))||0};k.R.Y={Q:T(k(q.1A).B(\'Q\'))||0,O:T(k(q.1A).B(\'O\'))||0};E I},8v:u(){k(1j).3p(\'3H\',k.R.8C).3p(\'61\',k.R.8v);if(k.R.1A.1g.3S){k.R.1A.1g.3S.1F(k.R.1A,[k.R.3Q])}k.R.1A=U;k.R.3Q=U},6V:u(dx,9t){E 14.3D(14.3v(k.R.1q.Z+dx*9t,k.R.1A.1g.9s),k.R.1A.1g.6q)},6Q:u(dy,9t){E 14.3D(14.3v(k.R.1q.V+dy*9t,k.R.1A.1g.8L),k.R.1A.1g.8M)},dX:u(V){E 14.3D(14.3v(V,k.R.1A.1g.8L),k.R.1A.1g.8M)},8C:u(e){if(k.R.1A==U){E}1s=k.1a.44(e);dx=1s.x-k.R.1s.x;dy=1s.y-k.R.1s.y;1E={Z:k.R.1q.Z,V:k.R.1q.V};2n={Q:k.R.Y.Q,O:k.R.Y.O};3m(k.R.3Q){1e\'e\':1E.Z=k.R.6V(dx,1);1r;1e\'eO\':1E.Z=k.R.6V(dx,1);1E.V=k.R.6Q(dy,1);1r;1e\'w\':1E.Z=k.R.6V(dx,-1);2n.O=k.R.Y.O-1E.Z+k.R.1q.Z;1r;1e\'5O\':1E.Z=k.R.6V(dx,-1);2n.O=k.R.Y.O-1E.Z+k.R.1q.Z;1E.V=k.R.6Q(dy,1);1r;1e\'7q
 \':1E.V=k.R.6Q(dy,-1);2n.Q=k.R.Y.Q-1E.V+k.R.1q.V;1E.Z=k.R.6V(dx,-1);2n.O=k.R.Y.O-1E.Z+k.R.1q.Z;1r;1e\'n\':1E.V=k.R.6Q(dy,-1);2n.Q=k.R.Y.Q-1E.V+k.R.1q.V;1r;1e\'9J\':1E.V=k.R.6Q(dy,-1);2n.Q=k.R.Y.Q-1E.V+k.R.1q.V;1E.Z=k.R.6V(dx,1);1r;1e\'s\':1E.V=k.R.6Q(dy,1);1r}if(k.R.1A.1g.4D){if(k.R.3Q==\'n\'||k.R.3Q==\'s\')4B=1E.V*k.R.1A.1g.4D;P 4B=1E.Z;5c=k.R.dX(4B*k.R.1A.1g.4D);4B=5c/k.R.1A.1g.4D;3m(k.R.3Q){1e\'n\':1e\'7q\':1e\'9J\':2n.Q+=1E.V-5c;1r}3m(k.R.3Q){1e\'7q\':1e\'w\':1e\'5O\':2n.O+=1E.Z-4B;1r}1E.V=5c;1E.Z=4B}if(2n.Q<k.R.1A.1g.7s){5c=1E.V+2n.Q-k.R.1A.1g.7s;2n.Q=k.R.1A.1g.7s;if(k.R.1A.1g.4D){4B=5c/k.R.1A.1g.4D;3m(k.R.3Q){1e\'7q\':1e\'w\':1e\'5O\':2n.O+=1E.Z-4B;1r}1E.Z=4B}1E.V=5c}if(2n.O<k.R.1A.1g.7p){4B=1E.Z+2n.O-k.R.1A.1g.7p;2n.O=k.R.1A.1g.7p;if(k.R.1A.1g.4D){5c=4B*k.R.1A.1g.4D;3m(k.R.3Q){1e\'n\':1e\'7q\':1e\'9J\':2n.Q+=1E.V-5c;1r}1E.V=5c}1E.Z=4B}if(2n.Q+1E.V>k.R.1A.1g.8U){1E.V=k.R.1A.1g.8U-2n.Q;if(k.R.1A.1g.4D){1E.Z=1E.V/k.R.1A.1g.4D}}if(2n.O+1E.Z>k.R.1A.1g.8T){1E.Z=k.R.1A.1g.8T
 -2n.O;if(k.R.1A.1g.4D){1E.V=1E.Z*k.R.1A.1g.4D}}D 6O=I;5L=k.R.1A.18;5L.O=2n.O+\'S\';5L.Q=2n.Q+\'S\';5L.Z=1E.Z+\'S\';5L.V=1E.V+\'S\';if(k.R.1A.1g.dY){6O=k.R.1A.1g.dY.1F(k.R.1A,[1E,2n]);if(6O){if(6O.1q){k.21(1E,6O.1q)}if(6O.Y){k.21(2n,6O.Y)}}}5L.O=2n.O+\'S\';5L.Q=2n.Q+\'S\';5L.Z=1E.Z+\'S\';5L.V=1E.V+\'S\';E I},2s:u(M){if(!M||!M.3U||M.3U.1K!=7n){E}E q.1B(u(){D el=q;el.1g=M;el.1g.9s=M.9s||10;el.1g.8L=M.8L||10;el.1g.6q=M.6q||6x;el.1g.8M=M.8M||6x;el.1g.7s=M.7s||-aF;el.1g.7p=M.7p||-aF;el.1g.8T=M.8T||6x;el.1g.8U=M.8U||6x;b3=k(el).B(\'Y\');if(!(b3==\'2y\'||b3==\'1O\')){el.18.Y=\'2y\'}eM=/n|9J|e|eO|s|5O|w|7q/g;1Y(i in el.1g.3U){if(i.5Z().bU(eM)!=U){if(el.1g.3U[i].1K==b5){3y=k(el.1g.3U[i]);if(3y.1P()>0){el.1g.3U[i]=3y.K(0)}}if(el.1g.3U[i].4S){el.1g.3U[i].1A=el;el.1g.3U[i].3Q=i;k(el.1g.3U[i]).1H(\'5b\',k.R.28)}}}if(el.1g.4N){if(2h el.1g.4N===\'5g\'){9K=k(el.1g.4N);if(9K.1P()>0){9K.1B(u(){q.a2=el});9K.1H(\'5b\',k.R.7r)}}P if(el.1g.4N.4S){el.1g.4N.a2=el;k(el.1g.4N).1H(\'5b\',k.R.7r)}P if(e
 l.1g.4N==1b){k(q).1H(\'5b\',k.R.7r)}}})},58:u(){E q.1B(u(){D el=q;1Y(i in el.1g.3U){el.1g.3U[i].1A=U;el.1g.3U[i].3Q=U;k(el.1g.3U[i]).3p(\'5b\',k.R.28)}if(el.1g.4N){if(2h el.1g.4N===\'5g\'){3y=k(el.1g.4N);if(3y.1P()>0){3y.3p(\'5b\',k.R.7r)}}P if(el.1g.4N==1b){k(q).3p(\'5b\',k.R.7r)}}el.1g=U})}};k.fn.21({j5:k.R.2s,j4:k.R.58});k.2u=U;k.7Z=I;k.3n=U;k.81=[];k.a0=u(e){D 3O=e.7F||e.7A||-1;if(3O==17||3O==16){k.7Z=1b}};k.9Y=u(e){k.7Z=I};k.eW=u(e){q.f.1s=k.1a.44(e);q.f.1M=k.21(k.1a.2R(q),k.1a.2p(q));q.f.3a=k.1a.6W(q);q.f.1s.x-=q.f.1M.x;q.f.1s.y-=q.f.1M.y;if(q.f.hc)k.2u.2Z(q.f.hc);k.2u.B({19:\'2E\',Z:\'83\',V:\'83\'});if(q.f.o){k.2u.B(\'1J\',q.f.o)}k.3n=q;k.8K=I;k.81=[];q.f.el.1B(u(){q.1M={x:q.8n+(q.4Y&&!k.3h.7N?T(q.4Y.5a)||0:0)+(k.3n.3g||0),y:q.8t+(q.4Y&&!k.3h.7N?T(q.4Y.4Z)||0:0)+(k.3n.2V||0),1D:q.4b,hb:q.63};if(q.s==1b){if(k.7Z==I){q.s=I;k(q).4p(k.3n.f.7X)}P{k.8K=1b;k.81[k.81.1h]=k.1p(q,\'id\')}}});k(q).1R(k.2u.K(0));q.f.93=k.1a.6h(k.2u[0],1b);k.a1.1F(q,[e]);k(1j).1H(\'3H\',k.a1).1H(
 \'61\',k.bT);E I};k.a1=u(e){if(!k.3n)E;k.eU.1F(k.3n,[e])};k.eU=u(e){if(!k.3n)E;D 1s=k.1a.44(e);D 3a=k.1a.6W(k.3n);1s.x+=3a.l-q.f.3a.l-q.f.1M.x;1s.y+=3a.t-q.f.3a.t-q.f.1M.y;D 8D=14.3D(1s.x,q.f.1s.x);D 5O=14.3D(14.3R(1s.x-q.f.1s.x),14.3R(q.f.3a.w-8D));D 9f=14.3D(1s.y,q.f.1s.y);D 8R=14.3D(14.3R(1s.y-q.f.1s.y),14.3R(q.f.3a.h-9f));if(q.2V>0&&1s.y-20<q.2V){D 3T=14.3D(3a.t,10);9f-=3T;8R+=3T;q.2V-=3T}P if(q.2V+q.f.1M.h<q.f.3a.h&&1s.y+20>q.2V+q.f.1M.h){D 3T=14.3D(q.f.3a.h-q.2V,10);q.2V+=3T;if(q.2V!=3a.t)8R+=3T}if(q.3g>0&&1s.x-20<q.3g){D 3T=14.3D(3a.l,10);8D-=3T;5O+=3T;q.3g-=3T}P if(q.3g+q.f.1M.w<q.f.3a.w&&1s.x+20>q.3g+q.f.1M.w){D 3T=14.3D(q.f.3a.w-q.3g,10);q.3g+=3T;if(q.3g!=3a.l)5O+=3T}k.2u.B({O:8D+\'S\',Q:9f+\'S\',Z:5O-(q.f.93.l+q.f.93.r)+\'S\',V:8R-(q.f.93.t+q.f.93.b)+\'S\'});k.2u.l=8D+q.f.3a.l;k.2u.t=9f+q.f.3a.t;k.2u.r=k.2u.l+5O;k.2u.b=k.2u.t+8R;k.8K=I;q.f.el.1B(u(){9k=k.81.3F(k.1p(q,\'id\'));if(!(q.1M.x>k.2u.r||(q.1M.x+q.1M.1D)<k.2u.l||q.1M.y>k.2u.b||(q.1M.y+q.1M.hb)<k.2u.t)){k.8
 K=1b;if(q.s!=1b){q.s=1b;k(q).2Z(k.3n.f.7X)}if(9k!=-1){q.s=I;k(q).4p(k.3n.f.7X)}}P if((q.s==1b)&&(9k==-1)){q.s=I;k(q).4p(k.3n.f.7X)}P if((!q.s)&&(k.7Z==1b)&&(9k!=-1)){q.s=1b;k(q).2Z(k.3n.f.7X)}});E I};k.bT=u(e){if(!k.3n)E;k.ex.1F(k.3n,[e])};k.ex=u(e){k(1j).3p(\'3H\',k.a1).3p(\'61\',k.bT);if(!k.3n)E;k.2u.B(\'19\',\'1n\');if(q.f.hc)k.2u.4p(q.f.hc);k.3n=I;k(\'2e\').1R(k.2u.K(0));if(k.8K==1b){if(q.f.8Y)q.f.8Y(k.c2(k.1p(q,\'id\')))}P{if(q.f.8X)q.f.8X(k.c2(k.1p(q,\'id\')))}k.81=[]};k.c2=u(s){D h=\'\';D o=[];if(a=k(\'#\'+s)){a.K(0).f.el.1B(u(){if(q.s==1b){if(h.1h>0){h+=\'&\'}h+=s+\'[]=\'+k.1p(q,\'id\');o[o.1h]=k.1p(q,\'id\')}})}E{7U:h,o:o}};k.fn.jZ=u(o){if(!k.2u){k(\'2e\',1j).1R(\'<26 id="2u"></26>\').1H(\'7E\',k.a0).1H(\'6S\',k.9Y);k.2u=k(\'#2u\');k.2u.B({Y:\'1O\',19:\'1n\'});if(1V.2l){k(\'2e\',1j).1H(\'7E\',k.a0).1H(\'6S\',k.9Y)}P{k(1j).1H(\'7E\',k.a0).1H(\'6S\',k.9Y)}}if(!o){o={}}E q.1B(u(){if(q.eX)E;q.eX=1b;q.f={a:o.3P,o:o.1J?2m(o.1J):I,7X:o.eE?o.eE:I,hc:o.4V?o.4V:I,8Y:o.8Y?o.8Y
 :I,8X:o.8X?o.8X:I};q.f.el=k(\'.\'+o.3P);k(q).1H(\'5b\',k.eW)})};k.1t={7V:[],5j:{},1c:I,7Y:U,28:u(){if(k.12.F==U){E}D 4M,3A,c,cs;k.1t.1c.K(0).3b=k.12.F.A.6p;4M=k.1t.1c.K(0).18;4M.19=\'2E\';k.1t.1c.1C=k.21(k.1a.2R(k.1t.1c.K(0)),k.1a.2p(k.1t.1c.K(0)));4M.Z=k.12.F.A.1C.1D+\'S\';4M.V=k.12.F.A.1C.hb+\'S\';3A=k.1a.c8(k.12.F);4M.5o=3A.t;4M.5p=3A.r;4M.5m=3A.b;4M.5k=3A.l;if(k.12.F.A.4j==1b){c=k.12.F.dn(1b);cs=c.18;cs.5o=\'3c\';cs.5p=\'3c\';cs.5m=\'3c\';cs.5k=\'3c\';cs.19=\'2E\';k.1t.1c.5t().1R(c)}k(k.12.F).dj(k.1t.1c.K(0));k.12.F.18.19=\'1n\'},dp:u(e){if(!e.A.48&&k.1x.5l.bD){if(e.A.3S)e.A.3S.1F(F);k(e).B(\'Y\',e.A.c4||e.A.4m);k(e).a4();k(k.1x.5l).dd(e)}k.1t.1c.4p(e.A.6p).3w(\'&7J;\');k.1t.7Y=U;D 4M=k.1t.1c.K(0).18;4M.19=\'1n\';k.1t.1c.dj(e);if(e.A.fx>0){k(e).7m(e.A.fx)}k(\'2e\').1R(k.1t.1c.K(0));D 86=[];D 8d=I;1Y(D i=0;i<k.1t.7V.1h;i++){D 1k=k.1x.3J[k.1t.7V[i]].K(0);D id=k.1p(1k,\'id\');D 8I=k.1t.8o(id);if(1k.1i.9Z!=8I.7U){1k.1i.9Z=8I.7U;if(8d==I&&1k.1i.2T){8d=1k.1i.2T}8I.id=id;86[86.
 1h]=8I}}k.1t.7V=[];if(8d!=I&&86.1h>0){8d(86)}},a3:u(e,o){if(!k.12.F)E;D 6i=I;D i=0;if(e.1i.el.1P()>0){1Y(i=e.1i.el.1P();i>0;i--){if(e.1i.el.K(i-1)!=k.12.F){if(!e.5V.bM){if((e.1i.el.K(i-1).1M.y+e.1i.el.K(i-1).1M.hb/2)>k.12.F.A.2r){6i=e.1i.el.K(i-1)}P{1r}}P{if((e.1i.el.K(i-1).1M.x+e.1i.el.K(i-1).1M.1D/2)>k.12.F.A.2x&&(e.1i.el.K(i-1).1M.y+e.1i.el.K(i-1).1M.hb/2)>k.12.F.A.2r){6i=e.1i.el.K(i-1)}}}}}if(6i&&k.1t.7Y!=6i){k.1t.7Y=6i;k(6i).k6(k.1t.1c.K(0))}P if(!6i&&(k.1t.7Y!=U||k.1t.1c.K(0).3e!=e)){k.1t.7Y=U;k(e).1R(k.1t.1c.K(0))}k.1t.1c.K(0).18.19=\'2E\'},c5:u(e){if(k.12.F==U){E}e.1i.el.1B(u(){q.1M=k.21(k.1a.82(q),k.1a.2R(q))})},8o:u(s){D i;D h=\'\';D o={};if(s){if(k.1t.5j[s]){o[s]=[];k(\'#\'+s+\' .\'+k.1t.5j[s]).1B(u(){if(h.1h>0){h+=\'&\'}h+=s+\'[]=\'+k.1p(q,\'id\');o[s][o[s].1h]=k.1p(q,\'id\')})}P{1Y(a in s){if(k.1t.5j[s[a]]){o[s[a]]=[];k(\'#\'+s[a]+\' .\'+k.1t.5j[s[a]]).1B(u(){if(h.1h>0){h+=\'&\'}h+=s[a]+\'[]=\'+k.1p(q,\'id\');o[s[a]][o[s[a]].1h]=k.1p(q,\'id\')})}}}}P{1Y(i in k.1
 t.5j){o[i]=[];k(\'#\'+i+\' .\'+k.1t.5j[i]).1B(u(){if(h.1h>0){h+=\'&\'}h+=i+\'[]=\'+k.1p(q,\'id\');o[i][o[i].1h]=k.1p(q,\'id\')})}}E{7U:h,o:o}},dc:u(e){if(!e.jJ){E}E q.1B(u(){if(!q.5V||!k(e).is(\'.\'+q.5V.3P))k(e).2Z(q.5V.3P);k(e).6Y(q.5V.A)})},58:u(){E q.1B(u(){k(\'.\'+q.5V.3P).a4();k(q).df();q.5V=U;q.dD=U})},2s:u(o){if(o.3P&&k.1a&&k.12&&k.1x){if(!k.1t.1c){k(\'2e\',1j).1R(\'<26 id="dt">&7J;</26>\');k.1t.1c=k(\'#dt\');k.1t.1c.K(0).18.19=\'1n\'}q.dO({3P:o.3P,a8:o.a8?o.a8:I,a7:o.a7?o.a7:I,4V:o.4V?o.4V:I,7T:o.7T||o.dN,7Q:o.7Q||o.dz,bD:1b,2T:o.2T||o.jL,fx:o.fx?o.fx:I,4j:o.4j?1b:I,6n:o.6n?o.6n:\'by\'});E q.1B(u(){D A={6o:o.6o?1b:I,dF:6x,1J:o.1J?2m(o.1J):I,6p:o.4V?o.4V:I,fx:o.fx?o.fx:I,48:1b,4j:o.4j?1b:I,3y:o.3y?o.3y:U,2o:o.2o?o.2o:U,4A:o.4A&&o.4A.1K==2C?o.4A:I,4x:o.4x&&o.4x.1K==2C?o.4x:I,3S:o.3S&&o.3S.1K==2C?o.3S:I,1N:/4i|4a/.43(o.1N)?o.1N:I,6m:o.6m?T(o.6m)||0:I,2S:o.2S?o.2S:I};k(\'.\'+o.3P,q).6Y(A);q.dD=1b;q.5V={3P:o.3P,6o:o.6o?1b:I,dF:6x,1J:o.1J?2m(o.1J):I,6p:o.4V?o.4V:I,fx:o.fx
 ?o.fx:I,48:1b,4j:o.4j?1b:I,3y:o.3y?o.3y:U,2o:o.2o?o.2o:U,bM:o.bM?1b:I,A:A}})}}};k.fn.21({jR:k.1t.2s,dd:k.1t.dc,jQ:k.1t.58});k.jN=k.1t.8o;k.3d={bG:1,f0:u(3u){D 3u=3u;E q.1B(u(){q.4r.6T.1B(u(a6){k.3d.59(q,3u[a6])})})},K:u(){D 3u=[];q.1B(u(bJ){if(q.bF){3u[bJ]=[];D C=q;D 1q=k.1a.2p(q);q.4r.6T.1B(u(a6){D x=q.8n;D y=q.8t;99=T(x*2b/(1q.w-q.4b));8a=T(y*2b/(1q.h-q.63));3u[bJ][a6]=[99||0,8a||0,x||0,y||0]})}});E 3u},bO:u(C){C.A.fK=C.A.24.w-C.A.1C.1D;C.A.fN=C.A.24.h-C.A.1C.hb;if(C.9P.4r.bE){a5=C.9P.4r.6T.K(C.bR+1);if(a5){C.A.24.w=(T(k(a5).B(\'O\'))||0)+C.A.1C.1D;C.A.24.h=(T(k(a5).B(\'Q\'))||0)+C.A.1C.hb}9X=C.9P.4r.6T.K(C.bR-1);if(9X){D bL=T(k(9X).B(\'O\'))||0;D bK=T(k(9X).B(\'O\'))||0;C.A.24.x+=bL;C.A.24.y+=bK;C.A.24.w-=bL;C.A.24.h-=bK}}C.A.fW=C.A.24.w-C.A.1C.1D;C.A.fV=C.A.24.h-C.A.1C.hb;if(C.A.2K){C.A.gx=((C.A.24.w-C.A.1C.1D)/C.A.2K)||1;C.A.gy=((C.A.24.h-C.A.1C.hb)/C.A.2K)||1;C.A.fY=C.A.fW/C.A.2K;C.A.fS=C.A.fV/C.A.2K}C.A.24.dx=C.A.24.x-C.A.2c.x;C.A.24.dy=C.A.24.y-C.A.2c.y;k.12.1c.B(\'9
 4\',\'aG\')},3z:u(C,x,y){if(C.A.2K){fZ=T(x/C.A.fY);99=fZ*2b/C.A.2K;fL=T(y/C.A.fS);8a=fL*2b/C.A.2K}P{99=T(x*2b/C.A.fK);8a=T(y*2b/C.A.fN)}C.A.bQ=[99||0,8a||0,x||0,y||0];if(C.A.3z)C.A.3z.1F(C,C.A.bQ)},g4:u(2l){3O=2l.7F||2l.7A||-1;3m(3O){1e 35:k.3d.59(q.3Z,[9W,9W]);1r;1e 36:k.3d.59(q.3Z,[-9W,-9W]);1r;1e 37:k.3d.59(q.3Z,[-q.3Z.A.gx||-1,0]);1r;1e 38:k.3d.59(q.3Z,[0,-q.3Z.A.gy||-1]);1r;1e 39:k.3d.59(q.3Z,[q.3Z.A.gx||1,0]);1r;1e 40:k.12.59(q.3Z,[0,q.3Z.A.gy||1]);1r}},59:u(C,Y){if(!C.A){E}C.A.1C=k.21(k.1a.2R(C),k.1a.2p(C));C.A.2c={x:T(k.B(C,\'O\'))||0,y:T(k.B(C,\'Q\'))||0};C.A.4m=k.B(C,\'Y\');if(C.A.4m!=\'2y\'&&C.A.4m!=\'1O\'){C.18.Y=\'2y\'}k.12.bP(C);k.3d.bO(C);dx=T(Y[0])||0;dy=T(Y[1])||0;2x=C.A.2c.x+dx;2r=C.A.2c.y+dy;if(C.A.2K){3q=k.12.bI.1F(C,[2x,2r,dx,dy]);if(3q.1K==7n){dx=3q.dx;dy=3q.dy}2x=C.A.2c.x+dx;2r=C.A.2c.y+dy}3q=k.12.bH.1F(C,[2x,2r,dx,dy]);if(3q&&3q.1K==7n){dx=3q.dx;dy=3q.dy}2x=C.A.2c.x+dx;2r=C.A.2c.y+dy;if(C.A.5i&&(C.A.3z||C.A.2T)){k.3d.3z(C,2x,2r)}2x=!C.A.1N||C.A.1N==\'
 4a\'?2x:C.A.2c.x||0;2r=!C.A.1N||C.A.1N==\'4i\'?2r:C.A.2c.y||0;C.18.O=2x+\'S\';C.18.Q=2r+\'S\'},2s:u(o){E q.1B(u(){if(q.bF==1b||!o.3P||!k.1a||!k.12||!k.1x){E}5N=k(o.3P,q);if(5N.1P()==0){E}D 4K={2o:\'96\',5i:1b,3z:o.3z&&o.3z.1K==2C?o.3z:U,2T:o.2T&&o.2T.1K==2C?o.2T:U,3y:q,1J:o.1J||I};if(o.2K&&T(o.2K)){4K.2K=T(o.2K)||1;4K.2K=4K.2K>0?4K.2K:1}if(5N.1P()==1)5N.6Y(4K);P{k(5N.K(0)).6Y(4K);4K.3y=U;5N.6Y(4K)}5N.7E(k.3d.g4);5N.1p(\'bG\',k.3d.bG++);q.bF=1b;q.4r={};q.4r.g6=4K.g6;q.4r.2K=4K.2K;q.4r.6T=5N;q.4r.bE=o.bE?1b:I;bS=q;bS.4r.6T.1B(u(2I){q.bR=2I;q.9P=bS});if(o.3u&&o.3u.1K==7b){1Y(i=o.3u.1h-1;i>=0;i--){if(o.3u[i].1K==7b&&o.3u[i].1h==2){el=q.4r.6T.K(i);if(el.4S){k.3d.59(el,o.3u[i])}}}}})}};k.fn.21({jV:k.3d.2s,k9:k.3d.f0,kb:k.3d.K});k.2t={6J:U,7c:I,9O:U,6D:u(e){k.2t.7c=1b;k.2t.22(e,q,1b)},bx:u(e){if(k.2t.6J!=q)E;k.2t.7c=I;k.2t.2G(e,q)},22:u(e,el,7c){if(k.2t.6J!=U)E;if(!el){el=q}k.2t.6J=el;1M=k.21(k.1a.2R(el),k.1a.2p(el));8G=k(el);45=8G.1p(\'45\');3f=8G.1p(\'3f\');if(45){k.2t.9O=45;8G.1
 p(\'45\',\'\');k(\'#fF\').3w(45);if(3f)k(\'#c9\').3w(3f.4v(\'k4://\',\'\'));P k(\'#c9\').3w(\'\');1c=k(\'#8V\');if(el.4T.3b){1c.K(0).3b=el.4T.3b}P{1c.K(0).3b=\'\'}c7=k.1a.2p(1c.K(0));fj=7c&&el.4T.Y==\'c3\'?\'4l\':el.4T.Y;3m(fj){1e\'Q\':2r=1M.y-c7.hb;2x=1M.x;1r;1e\'O\':2r=1M.y;2x=1M.x-c7.1D;1r;1e\'2N\':2r=1M.y;2x=1M.x+1M.1D;1r;1e\'c3\':k(\'2e\').1H(\'3H\',k.2t.3H);1s=k.1a.44(e);2r=1s.y+15;2x=1s.x+15;1r;aG:2r=1M.y+1M.hb;2x=1M.x;1r}1c.B({Q:2r+\'S\',O:2x+\'S\'});if(el.4T.53==I){1c.22()}P{1c.7m(el.4T.53)}if(el.4T.2U)el.4T.2U.1F(el);8G.1H(\'8q\',k.2t.2G).1H(\'5I\',k.2t.bx)}},3H:u(e){if(k.2t.6J==U){k(\'2e\').3p(\'3H\',k.2t.3H);E}1s=k.1a.44(e);k(\'#8V\').B({Q:1s.y+15+\'S\',O:1s.x+15+\'S\'})},2G:u(e,el){if(!el){el=q}if(k.2t.7c!=1b&&k.2t.6J==el){k.2t.6J=U;k(\'#8V\').7k(1);k(el).1p(\'45\',k.2t.9O).3p(\'8q\',k.2t.2G).3p(\'5I\',k.2t.bx);if(el.4T.3i)el.4T.3i.1F(el);k.2t.9O=U}},2s:u(M){if(!k.2t.1c){k(\'2e\').1R(\'<26 id="8V"><26 id="fF"></26><26 id="c9"></26></26>\');k(\'#8V\').B({Y:\'1O\'
 ,3B:6x,19:\'1n\'});k.2t.1c=1b}E q.1B(u(){if(k.1p(q,\'45\')){q.4T={Y:/Q|4l|O|2N|c3/.43(M.Y)?M.Y:\'4l\',3b:M.3b?M.3b:I,53:M.53?M.53:I,2U:M.2U&&M.2U.1K==2C?M.2U:I,3i:M.3i&&M.3i.1K==2C?M.3i:I};D el=k(q);el.1H(\'aV\',k.2t.22);el.1H(\'6D\',k.2t.6D)}})}};k.fn.k0=k.2t.2s;k.21({G:{bV:u(p,n,1W,1I,1m){E((-14.5v(p*14.2Q)/2)+0.5)*1I+1W},k2:u(p,n,1W,1I,1m){E 1I*(n/=1m)*n*n+1W},fG:u(p,n,1W,1I,1m){E-1I*((n=n/1m-1)*n*n*n-1)+1W},k1:u(p,n,1W,1I,1m){if((n/=1m/2)<1)E 1I/2*n*n*n*n+1W;E-1I/2*((n-=2)*n*n*n-2)+1W},9c:u(p,n,1W,1I,1m){if((n/=1m)<(1/2.75)){E 1I*(7.9N*n*n)+1W}P if(n<(2/2.75)){E 1I*(7.9N*(n-=(1.5/2.75))*n+.75)+1W}P if(n<(2.5/2.75)){E 1I*(7.9N*(n-=(2.25/2.75))*n+.jC)+1W}P{E 1I*(7.9N*(n-=(2.jB/2.75))*n+.jd)+1W}},bY:u(p,n,1W,1I,1m){if(k.G.9c)E 1I-k.G.9c(p,1m-n,0,1I,1m)+1W;E 1W+1I},jc:u(p,n,1W,1I,1m){if(k.G.bY&&k.G.9c)if(n<1m/2)E k.G.bY(p,n*2,0,1I,1m)*.5+1W;E k.G.9c(p,n*2-1m,0,1I,1m)*.5+1I*.5+1W;E 1W+1I},jb:u(p,n,1W,1I,1m){D a,s;if(n==0)E 1W;if((n/=1m)==1)E 1W+1I;a=1I*0.3;p=1m*.3;if(a<14.3R(
 1I)){a=1I;s=p/4}P{s=p/(2*14.2Q)*14.c0(1I/a)}E-(a*14.5Y(2,10*(n-=1))*14.98((n*1m-s)*(2*14.2Q)/p))+1W},je:u(p,n,1W,1I,1m){D a,s;if(n==0)E 1W;if((n/=1m/2)==2)E 1W+1I;a=1I*0.3;p=1m*.3;if(a<14.3R(1I)){a=1I;s=p/4}P{s=p/(2*14.2Q)*14.c0(1I/a)}E a*14.5Y(2,-10*n)*14.98((n*1m-s)*(2*14.2Q)/p)+1I+1W},jf:u(p,n,1W,1I,1m){D a,s;if(n==0)E 1W;if((n/=1m/2)==2)E 1W+1I;a=1I*0.3;p=1m*.3;if(a<14.3R(1I)){a=1I;s=p/4}P{s=p/(2*14.2Q)*14.c0(1I/a)}if(n<1){E-.5*(a*14.5Y(2,10*(n-=1))*14.98((n*1m-s)*(2*14.2Q)/p))+1W}E a*14.5Y(2,-10*(n-=1))*14.98((n*1m-s)*(2*14.2Q)/p)*.5+1I+1W}}});k.fn.21({fz:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.5W(q,H,J,\'4U\',G)})},fP:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.5W(q,H,J,\'4y\',G)})},j9:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.5W(q,H,J,\'f8\',G)})},j3:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.5W(q,H,J,\'O\',G)})},j2:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.5W(q,H,J,\'2N\',G)})},j1:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.5W(q,H,J,\'fh\',G)})}});k.fx.5W=u(e,H,J,2P,G){if(!k.4O(e)){k.2L(e,\'1o\');E I}D z=q;z.el
 =k(e);z.1P=k.1a.2p(e);z.G=2h J==\'5g\'?J:G||U;if(!e.4s)e.4s=z.el.B(\'19\');if(2P==\'f8\'){2P=z.el.B(\'19\')==\'1n\'?\'4y\':\'4U\'}P if(2P==\'fh\'){2P=z.el.B(\'19\')==\'1n\'?\'2N\':\'O\'}z.el.22();z.H=H;z.J=2h J==\'u\'?J:U;z.fx=k.fx.9h(e);z.2P=2P;z.23=u(){if(z.J&&z.J.1K==2C){z.J.1F(z.el.K(0))}if(z.2P==\'4y\'||z.2P==\'2N\'){z.el.B(\'19\',z.el.K(0).4s==\'1n\'?\'2E\':z.el.K(0).4s)}P{z.el.2G()}k.fx.9g(z.fx.3o.K(0),z.fx.W);k.2L(z.el.K(0),\'1o\')};3m(z.2P){1e\'4U\':6d=11 k.fx(z.fx.3o.K(0),k.H(z.H,z.G,z.23),\'V\');6d.1L(z.fx.W.1q.hb,0);1r;1e\'4y\':z.fx.3o.B(\'V\',\'83\');z.el.22();6d=11 k.fx(z.fx.3o.K(0),k.H(z.H,z.G,z.23),\'V\');6d.1L(0,z.fx.W.1q.hb);1r;1e\'O\':6d=11 k.fx(z.fx.3o.K(0),k.H(z.H,z.G,z.23),\'Z\');6d.1L(z.fx.W.1q.1D,0);1r;1e\'2N\':z.fx.3o.B(\'Z\',\'83\');z.el.22();6d=11 k.fx(z.fx.3o.K(0),k.H(z.H,z.G,z.23),\'Z\');6d.1L(0,z.fx.W.1q.1D);1r}};k.fn.kd=u(5w,J){E q.1w(\'1o\',u(){if(!k.4O(q)){k.2L(q,\'1o\');E I}D e=11 k.fx.fa(q,5w,J);e.bc()})};k.fx.fa=u(e,5w,J){D z=q;z.el=k(e);z
 .el.22();z.J=J;z.5w=T(5w)||40;z.W={};z.W.Y=z.el.B(\'Y\');z.W.Q=T(z.el.B(\'Q\'))||0;z.W.O=T(z.el.B(\'O\'))||0;if(z.W.Y!=\'2y\'&&z.W.Y!=\'1O\'){z.el.B(\'Y\',\'2y\')}z.41=5;z.5D=1;z.bc=u(){z.5D++;z.e=11 k.fx(z.el.K(0),{1m:j6,23:u(){z.e=11 k.fx(z.el.K(0),{1m:80,23:u(){z.5w=T(z.5w/2);if(z.5D<=z.41)z.bc();P{z.el.B(\'Y\',z.W.Y).B(\'Q\',z.W.Q+\'S\').B(\'O\',z.W.O+\'S\');k.2L(z.el.K(0),\'1o\');if(z.J&&z.J.1K==2C){z.J.1F(z.el.K(0))}}}},\'Q\');z.e.1L(z.W.Q-z.5w,z.W.Q)}},\'Q\');z.e.1L(z.W.Q,z.W.Q-z.5w)}};k.fn.21({ji:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'4y\',\'4d\',G)})},jj:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'4y\',\'in\',G)})},jw:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'4y\',\'3Y\',G)})},jv:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'4U\',\'4d\',G)})},ju:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'4U\',\'in\',G)})},jx:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'4U\',\'3Y\',G)})},jy:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'O\',\'4d\',G)})},jz:u(H,J,G){E 
 q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'O\',\'in\',G)})},jt:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'O\',\'3Y\',G)})},js:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'2N\',\'4d\',G)})},jm:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'2N\',\'in\',G)})},jl:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.4k(q,H,J,\'2N\',\'3Y\',G)})}});k.fx.4k=u(e,H,J,2P,1u,G){if(!k.4O(e)){k.2L(e,\'1o\');E I}D z=q;z.el=k(e);z.G=2h J==\'5g\'?J:G||U;z.W={};z.W.Y=z.el.B(\'Y\');z.W.Q=z.el.B(\'Q\');z.W.O=z.el.B(\'O\');if(!e.4s)e.4s=z.el.B(\'19\');if(1u==\'3Y\'){1u=z.el.B(\'19\')==\'1n\'?\'in\':\'4d\'}z.el.22();if(z.W.Y!=\'2y\'&&z.W.Y!=\'1O\'){z.el.B(\'Y\',\'2y\')}z.1u=1u;J=2h J==\'u\'?J:U;8y=1;3m(2P){1e\'4U\':z.e=11 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'Q\');z.68=2m(z.W.Q)||0;z.9L=z.fM;8y=-1;1r;1e\'4y\':z.e=11 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'Q\');z.68=2m(z.W.Q)||0;z.9L=z.fM;1r;1e\'2N\':z.e=11 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'O\');z.68=2m(z.W.O)||0;z.9L=z.f4;1r;1e\'O\':z.e=11 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'O\');z.68
 =2m(z.W.O)||0;z.9L=z.f4;8y=-1;1r}z.e2=11 k.fx(z.el.K(0),k.H(H,z.G,u(){z.el.B(z.W);if(z.1u==\'4d\'){z.el.B(\'19\',\'1n\')}P z.el.B(\'19\',z.el.K(0).4s==\'1n\'?\'2E\':z.el.K(0).4s);k.2L(z.el.K(0),\'1o\')}),\'1J\');if(1u==\'in\'){z.e.1L(z.68+2b*8y,z.68);z.e2.1L(0,1)}P{z.e.1L(z.68,z.68+2b*8y);z.e2.1L(1,0)}};k.fn.21({jn:u(H,V,J,G){E q.1w(\'1o\',u(){11 k.fx.9M(q,H,V,J,\'g7\',G)})},jo:u(H,V,J,G){E q.1w(\'1o\',u(){11 k.fx.9M(q,H,V,J,\'9Q\',G)})},jr:u(H,V,J,G){E q.1w(\'1o\',u(){11 k.fx.9M(q,H,V,J,\'3Y\',G)})}});k.fx.9M=u(e,H,V,J,1u,G){if(!k.4O(e)){k.2L(e,\'1o\');E I}D z=q;z.el=k(e);z.G=2h J==\'5g\'?J:G||U;z.J=2h J==\'u\'?J:U;if(1u==\'3Y\'){1u=z.el.B(\'19\')==\'1n\'?\'9Q\':\'g7\'}z.H=H;z.V=V&&V.1K==cR?V:20;z.fx=k.fx.9h(e);z.1u=1u;z.23=u(){if(z.J&&z.J.1K==2C){z.J.1F(z.el.K(0))}if(z.1u==\'9Q\'){z.el.22()}P{z.el.2G()}k.fx.9g(z.fx.3o.K(0),z.fx.W);k.2L(z.el.K(0),\'1o\')};if(z.1u==\'9Q\'){z.el.22();z.fx.3o.B(\'V\',z.V+\'S\').B(\'Z\',\'83\');z.ef=11 k.fx(z.fx.3o.K(0),k.H(z.H,z.G,u(){z.ef=11 
 k.fx(z.fx.3o.K(0),k.H(z.H,z.G,z.23),\'V\');z.ef.1L(z.V,z.fx.W.1q.hb)}),\'Z\');z.ef.1L(0,z.fx.W.1q.1D)}P{z.ef=11 k.fx(z.fx.3o.K(0),k.H(z.H,z.G,u(){z.ef=11 k.fx(z.fx.3o.K(0),k.H(z.H,z.G,z.23),\'Z\');z.ef.1L(z.fx.W.1q.1D,0)}),\'V\');z.ef.1L(z.fx.W.1q.hb,z.V)}};k.fn.21({jq:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.6z(q,H,1,2b,1b,J,\'f1\',G)})},jp:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.6z(q,H,2b,1,1b,J,\'d2\',G)})},kt:u(H,J,G){E q.1w(\'1o\',u(){D G=G||\'fG\';11 k.fx.6z(q,H,2b,fd,1b,J,\'6l\',G)})},6z:u(H,5d,4L,6E,J,G){E q.1w(\'1o\',u(){11 k.fx.6z(q,H,5d,4L,6E,J,\'6z\',G)})}});k.fx.6z=u(e,H,5d,4L,6E,J,1u,G){if(!k.4O(e)){k.2L(e,\'1o\');E I}D z=q;z.el=k(e);z.5d=T(5d)||2b;z.4L=T(4L)||2b;z.G=2h J==\'5g\'?J:G||U;z.J=2h J==\'u\'?J:U;z.1m=k.H(H).1m;z.6E=6E||U;z.2f=k.1a.2p(e);z.W={Z:z.el.B(\'Z\'),V:z.el.B(\'V\'),4w:z.el.B(\'4w\')||\'2b%\',Y:z.el.B(\'Y\'),19:z.el.B(\'19\'),Q:z.el.B(\'Q\'),O:z.el.B(\'O\'),2Y:z.el.B(\'2Y\'),4Z:z.el.B(\'4Z\'),6k:z.el.B(\'6k\'),6g:z.el.B(\'6g\'),5a:z.el.B(\'5a\'),66:z.el.
 B(\'66\'),6j:z.el.B(\'6j\'),5M:z.el.B(\'5M\'),4X:z.el.B(\'4X\')};z.Z=T(z.W.Z)||e.4b||0;z.V=T(z.W.V)||e.63||0;z.Q=T(z.W.Q)||0;z.O=T(z.W.O)||0;1q=[\'em\',\'S\',\'kJ\',\'%\'];1Y(i in 1q){if(z.W.4w.3F(1q[i])>0){z.fi=1q[i];z.4w=2m(z.W.4w)}if(z.W.4Z.3F(1q[i])>0){z.fw=1q[i];z.bt=2m(z.W.4Z)||0}if(z.W.6k.3F(1q[i])>0){z.fB=1q[i];z.bg=2m(z.W.6k)||0}if(z.W.6g.3F(1q[i])>0){z.fE=1q[i];z.bf=2m(z.W.6g)||0}if(z.W.5a.3F(1q[i])>0){z.fv=1q[i];z.be=2m(z.W.5a)||0}if(z.W.66.3F(1q[i])>0){z.fk=1q[i];z.bb=2m(z.W.66)||0}if(z.W.6j.3F(1q[i])>0){z.fs=1q[i];z.ba=2m(z.W.6j)||0}if(z.W.5M.3F(1q[i])>0){z.fb=1q[i];z.cJ=2m(z.W.5M)||0}if(z.W.4X.3F(1q[i])>0){z.fq=1q[i];z.cX=2m(z.W.4X)||0}}if(z.W.Y!=\'2y\'&&z.W.Y!=\'1O\'){z.el.B(\'Y\',\'2y\')}z.el.B(\'2Y\',\'2O\');z.1u=1u;3m(z.1u){1e\'f1\':z.4f=z.Q+z.2f.h/2;z.57=z.Q;z.4c=z.O+z.2f.w/2;z.4W=z.O;1r;1e\'d2\':z.57=z.Q+z.2f.h/2;z.4f=z.Q;z.4W=z.O+z.2f.w/2;z.4c=z.O;1r;1e\'6l\':z.57=z.Q-z.2f.h/4;z.4f=z.Q;z.4W=z.O-z.2f.w/4;z.4c=z.O;1r}z.bo=I;z.t=(11 72).71();z.4u=u(){6c(z.2
 H);z.2H=U};z.2D=u(){if(z.bo==I){z.el.22();z.bo=1b}D t=(11 72).71();D n=t-z.t;D p=n/z.1m;if(t>=z.1m+z.t){b1(u(){o=1;if(z.1u){t=z.57;l=z.4W;if(z.1u==\'6l\')o=0}z.bv(z.4L,l,t,1b,o)},13);z.4u()}P{o=1;if(!k.G||!k.G[z.G]){s=((-14.5v(p*14.2Q)/2)+0.5)*(z.4L-z.5d)+z.5d}P{s=k.G[z.G](p,n,z.5d,(z.4L-z.5d),z.1m)}if(z.1u){if(!k.G||!k.G[z.G]){t=((-14.5v(p*14.2Q)/2)+0.5)*(z.57-z.4f)+z.4f;l=((-14.5v(p*14.2Q)/2)+0.5)*(z.4W-z.4c)+z.4c;if(z.1u==\'6l\')o=((-14.5v(p*14.2Q)/2)+0.5)*(-0.9R)+0.9R}P{t=k.G[z.G](p,n,z.4f,(z.57-z.4f),z.1m);l=k.G[z.G](p,n,z.4c,(z.4W-z.4c),z.1m);if(z.1u==\'6l\')o=k.G[z.G](p,n,0.9R,-0.9R,z.1m)}}z.bv(s,l,t,I,o)}};z.2H=6I(u(){z.2D()},13);z.bv=u(4z,O,Q,fp,1J){z.el.B(\'V\',z.V*4z/2b+\'S\').B(\'Z\',z.Z*4z/2b+\'S\').B(\'O\',O+\'S\').B(\'Q\',Q+\'S\').B(\'4w\',z.4w*4z/2b+z.fi);if(z.bt)z.el.B(\'4Z\',z.bt*4z/2b+z.fw);if(z.bg)z.el.B(\'6k\',z.bg*4z/2b+z.fB);if(z.bf)z.el.B(\'6g\',z.bf*4z/2b+z.fE);if(z.be)z.el.B(\'5a\',z.be*4z/2b+z.fv);if(z.bb)z.el.B(\'66\',z.bb*4z/2b+z.fk);if(z.ba)z.el
 .B(\'6j\',z.ba*4z/2b+z.fs);if(z.cJ)z.el.B(\'5M\',z.cJ*4z/2b+z.fb);if(z.cX)z.el.B(\'4X\',z.cX*4z/2b+z.fq);if(z.1u==\'6l\'){if(1V.7a)z.el.K(0).18.69="9V(1J="+1J*2b+")";z.el.K(0).18.1J=1J}if(fp){if(z.6E){z.el.B(z.W)}if(z.1u==\'d2\'||z.1u==\'6l\'){z.el.B(\'19\',\'1n\');if(z.1u==\'6l\'){if(1V.7a)z.el.K(0).18.69="9V(1J="+2b+")";z.el.K(0).18.1J=1}}P z.el.B(\'19\',\'2E\');if(z.J)z.J.1F(z.el.K(0));k.2L(z.el.K(0),\'1o\')}}};k.fn.kL=u(H,4C,J,G){E q.1w(\'f6\',u(){q.73=k(q).1p("18")||\'\';G=2h J==\'5g\'?J:G||U;J=2h J==\'u\'?J:U;D 9U=k(q).B(\'7f\');D 87=q.3e;7o(9U==\'b7\'&&87){9U=k(87).B(\'7f\');87=87.3e}k(q).B(\'7f\',4C);if(2h q.73==\'8i\')q.73=q.73["9T"];k(q).5K({\'7f\':9U},H,G,u(){k.2L(q,\'f6\');if(2h k(q).1p("18")==\'8i\'){k(q).1p("18")["9T"]="";k(q).1p("18")["9T"]=q.73}P{k(q).1p("18",q.73)}if(J)J.1F(q)})})};k.fn.21({kg:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.5A(q,H,J,\'4i\',\'5P\',G)})},kq:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.5A(q,H,J,\'4a\',\'5P\',G)})},kr:u(H,J,G){E q.1w(\'1o\',u(){if(k.B
 (q,\'19\')==\'1n\'){11 k.fx.5A(q,H,J,\'4a\',\'7e\',G)}P{11 k.fx.5A(q,H,J,\'4a\',\'5P\',G)}})},kz:u(H,J,G){E q.1w(\'1o\',u(){if(k.B(q,\'19\')==\'1n\'){11 k.fx.5A(q,H,J,\'4i\',\'7e\',G)}P{11 k.fx.5A(q,H,J,\'4i\',\'5P\',G)}})},ky:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.5A(q,H,J,\'4i\',\'7e\',G)})},kx:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.5A(q,H,J,\'4a\',\'7e\',G)})}});k.fx.5A=u(e,H,J,2P,1u,G){if(!k.4O(e)){k.2L(e,\'1o\');E I}D z=q;D 5H=I;z.el=k(e);z.G=2h J==\'5g\'?J:G||U;z.J=2h J==\'u\'?J:U;z.1u=1u;z.H=H;z.2f=k.1a.2p(e);z.W={};z.W.Y=z.el.B(\'Y\');z.W.19=z.el.B(\'19\');if(z.W.19==\'1n\'){62=z.el.B(\'3j\');z.el.22();5H=1b}z.W.Q=z.el.B(\'Q\');z.W.O=z.el.B(\'O\');if(5H){z.el.2G();z.el.B(\'3j\',62)}z.W.Z=z.2f.w+\'S\';z.W.V=z.2f.h+\'S\';z.W.2Y=z.el.B(\'2Y\');z.2f.Q=T(z.W.Q)||0;z.2f.O=T(z.W.O)||0;if(z.W.Y!=\'2y\'&&z.W.Y!=\'1O\'){z.el.B(\'Y\',\'2y\')}z.el.B(\'2Y\',\'2O\').B(\'V\',1u==\'7e\'&&2P==\'4i\'?1:z.2f.h+\'S\').B(\'Z\',1u==\'7e\'&&2P==\'4a\'?1:z.2f.w+\'S\');z.23=u(){z.el.B(z.W);if(z.1u==
 \'5P\')z.el.2G();P z.el.22();k.2L(z.el.K(0),\'1o\')};3m(2P){1e\'4i\':z.eh=11 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'V\');z.et=11 k.fx(z.el.K(0),k.H(z.H,z.G,z.23),\'Q\');if(z.1u==\'5P\'){z.eh.1L(z.2f.h,0);z.et.1L(z.2f.Q,z.2f.Q+z.2f.h/2)}P{z.eh.1L(0,z.2f.h);z.et.1L(z.2f.Q+z.2f.h/2,z.2f.Q)}1r;1e\'4a\':z.eh=11 k.fx(z.el.K(0),k.H(H-15,z.G,J),\'Z\');z.et=11 k.fx(z.el.K(0),k.H(z.H,z.G,z.23),\'O\');if(z.1u==\'5P\'){z.eh.1L(z.2f.w,0);z.et.1L(z.2f.O,z.2f.O+z.2f.w/2)}P{z.eh.1L(0,z.2f.w);z.et.1L(z.2f.O+z.2f.w/2,z.2f.O)}1r}};k.fn.cr=u(H,41,J){E q.1w(\'1o\',u(){if(!k.4O(q)){k.2L(q,\'1o\');E I}D fx=11 k.fx.cr(q,H,41,J);fx.cm()})};k.fx.cr=u(el,H,41,J){D z=q;z.41=41;z.5D=1;z.el=el;z.H=H;z.J=J;k(z.el).22();z.cm=u(){z.5D++;z.e=11 k.fx(z.el,k.H(z.H,u(){z.ef=11 k.fx(z.el,k.H(z.H,u(){if(z.5D<=z.41)z.cm();P{k.2L(z.el,\'1o\');if(z.J&&z.J.1K==2C){z.J.1F(z.el)}}}),\'1J\');z.ef.1L(0,1)}),\'1J\');z.e.1L(1,0)}};k.fn.21({9S:u(H,1N,G){o=k.H(H);E q.1w(\'1o\',u(){11 k.fx.9S(q,o,1N,G)})},ks:u(H,1N,G){E q.1B(u(){k(
 \'a[@3f*="#"]\',q).5G(u(e){g8=q.3f.7h(\'#\');k(\'#\'+g8[1]).9S(H,1N,G);E I})})}});k.fx.9S=u(e,o,1N,G){D z=q;z.o=o;z.e=e;z.1N=/g3|g0/.43(1N)?1N:I;z.G=G;p=k.1a.2R(e);s=k.1a.6W();z.4u=u(){6c(z.2H);z.2H=U;k.2L(z.e,\'1o\')};z.t=(11 72).71();s.h=s.h>s.ih?(s.h-s.ih):s.h;s.w=s.w>s.iw?(s.w-s.iw):s.w;z.57=p.y>s.h?s.h:p.y;z.4W=p.x>s.w?s.w:p.x;z.4f=s.t;z.4c=s.l;z.2D=u(){D t=(11 72).71();D n=t-z.t;D p=n/z.o.1m;if(t>=z.o.1m+z.t){z.4u();b1(u(){z.cE(z.57,z.4W)},13)}P{if(!z.1N||z.1N==\'g3\'){if(!k.G||!k.G[z.G]){aa=((-14.5v(p*14.2Q)/2)+0.5)*(z.57-z.4f)+z.4f}P{aa=k.G[z.G](p,n,z.4f,(z.57-z.4f),z.o.1m)}}P{aa=z.4f}if(!z.1N||z.1N==\'g0\'){if(!k.G||!k.G[z.G]){a9=((-14.5v(p*14.2Q)/2)+0.5)*(z.4W-z.4c)+z.4c}P{a9=k.G[z.G](p,n,z.4c,(z.4W-z.4c),z.o.1m)}}P{a9=z.4c}z.cE(aa,a9)}};z.cE=u(t,l){1V.gN(l,t)};z.2H=6I(u(){z.2D()},13)};k.fn.cy=u(41,J){E q.1w(\'1o\',u(){if(!k.4O(q)){k.2L(q,\'1o\');E I}D e=11 k.fx.cy(q,41,J);e.cx()})};k.fx.cy=u(e,41,J){D z=q;z.el=k(e);z.el.22();z.41=T(41)||3;z.J=J;z.5D=1;z.W={};z.W.Y
 =z.el.B(\'Y\');z.W.Q=T(z.el.B(\'Q\'))||0;z.W.O=T(z.el.B(\'O\'))||0;if(z.W.Y!=\'2y\'&&z.W.Y!=\'1O\'){z.el.B(\'Y\',\'2y\')}z.cx=u(){z.5D++;z.e=11 k.fx(z.el.K(0),{1m:60,23:u(){z.e=11 k.fx(z.el.K(0),{1m:60,23:u(){z.e=11 k.fx(e,{1m:60,23:u(){if(z.5D<=z.41)z.cx();P{z.el.B(\'Y\',z.W.Y).B(\'Q\',z.W.Q+\'S\').B(\'O\',z.W.O+\'S\');k.2L(z.el.K(0),\'1o\');if(z.J&&z.J.1K==2C){z.J.1F(z.el.K(0))}}}},\'O\');z.e.1L(z.W.O-20,z.W.O)}},\'O\');z.e.1L(z.W.O+20,z.W.O-20)}},\'O\');z.e.1L(z.W.O,z.W.O+20)}};k.fn.21({g9:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.1z(q,H,J,\'4U\',\'in\',G)})},f3:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.1z(q,H,J,\'4U\',\'4d\',G)})},gM:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.1z(q,H,J,\'4U\',\'3Y\',G)})},gL:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.1z(q,H,J,\'4y\',\'in\',G)})},gK:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.1z(q,H,J,\'4y\',\'4d\',G)})},gS:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.1z(q,H,J,\'4y\',\'3Y\',G)})},gR:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.1z(q,H,J,\'O\',\'in\',G)})},gJ:u(H,J,G){E q.1w(\'1o\',
 u(){11 k.fx.1z(q,H,J,\'O\',\'4d\',G)})},gI:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.1z(q,H,J,\'O\',\'3Y\',G)})},gC:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.1z(q,H,J,\'2N\',\'in\',G)})},gB:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.1z(q,H,J,\'2N\',\'4d\',G)})},gU:u(H,J,G){E q.1w(\'1o\',u(){11 k.fx.1z(q,H,J,\'2N\',\'3Y\',G)})}});k.fx.1z=u(e,H,J,2P,1u,G){if(!k.4O(e)){k.2L(e,\'1o\');E I}D z=q;z.el=k(e);z.G=2h J==\'5g\'?J:G||U;z.J=2h J==\'u\'?J:U;if(1u==\'3Y\'){1u=z.el.B(\'19\')==\'1n\'?\'in\':\'4d\'}if(!e.4s)e.4s=z.el.B(\'19\');z.el.22();z.H=H;z.fx=k.fx.9h(e);z.1u=1u;z.2P=2P;z.23=u(){if(z.1u==\'4d\')z.el.B(\'3j\',\'2O\');k.fx.9g(z.fx.3o.K(0),z.fx.W);if(z.1u==\'in\'){z.el.B(\'19\',z.el.K(0).4s==\'1n\'?\'2E\':z.el.K(0).4s)}P{z.el.B(\'19\',\'1n\');z.el.B(\'3j\',\'dR\')}if(z.J&&z.J.1K==2C){z.J.1F(z.el.K(0))}k.2L(z.el.K(0),\'1o\')};3m(z.2P){1e\'4U\':z.ef=11 k.fx(z.el.K(0),k.H(z.H,z.G,z.23),\'Q\');z.7S=11 k.fx(z.fx.3o.K(0),k.H(z.H,z.G),\'V\');if(z.1u==\'in\'){z.ef.1L(-z.fx.W.1q.hb,0);z.7S.1L(0,z.fx.W.1q.
 hb)}P{z.ef.1L(0,-z.fx.W.1q.hb);z.7S.1L(z.fx.W.1q.hb,0)}1r;1e\'4y\':z.ef=11 k.fx(z.el.K(0),k.H(z.H,z.G,z.23),\'Q\');if(z.1u==\'in\'){z.ef.1L(z.fx.W.1q.hb,0)}P{z.ef.1L(0,z.fx.W.1q.hb)}1r;1e\'O\':z.ef=11 k.fx(z.el.K(0),k.H(z.H,z.G,z.23),\'O\');z.7S=11 k.fx(z.fx.3o.K(0),k.H(z.H,z.G),\'Z\');if(z.1u==\'in\'){z.ef.1L(-z.fx.W.1q.1D,0);z.7S.1L(0,z.fx.W.1q.1D)}P{z.ef.1L(0,-z.fx.W.1q.1D);z.7S.1L(z.fx.W.1q.1D,0)}1r;1e\'2N\':z.ef=11 k.fx(z.el.K(0),k.H(z.H,z.G,z.23),\'O\');if(z.1u==\'in\'){z.ef.1L(z.fx.W.1q.1D,0)}P{z.ef.1L(0,z.fx.W.1q.1D)}1r}};k.h2=U;k.fn.h1=u(o){E q.1B(u(){if(!o||!o.4L){E}D el=q;k(o.4L).1B(u(){11 k.fx.fu(el,q,o)})})};k.fx.fu=u(e,8s,o){D z=q;z.el=k(e);z.8s=8s;z.4e=1j.3t(\'26\');k(z.4e).B({Y:\'1O\'}).2Z(o.3b);if(!o.1m){o.1m=er}z.1m=o.1m;z.23=o.23;z.9i=0;z.9j=0;if(k.f5){z.9i=(T(k.3M(z.4e,\'5a\'))||0)+(T(k.3M(z.4e,\'6k\'))||0)+(T(k.3M(z.4e,\'4X\'))||0)+(T(k.3M(z.4e,\'6j\'))||0);z.9j=(T(k.3M(z.4e,\'4Z\'))||0)+(T(k.3M(z.4e,\'6g\'))||0)+(T(k.3M(z.4e,\'66\'))||0)+(T(k.3M(z.4e,\'
 5M\'))||0)}z.28=k.21(k.1a.2R(z.el.K(0)),k.1a.2p(z.el.K(0)));z.2X=k.21(k.1a.2R(z.8s),k.1a.2p(z.8s));z.28.1D-=z.9i;z.28.hb-=z.9j;z.2X.1D-=z.9i;z.2X.hb-=z.9j;z.J=o.23;k(\'2e\').1R(z.4e);k(z.4e).B(\'Z\',z.28.1D+\'S\').B(\'V\',z.28.hb+\'S\').B(\'Q\',z.28.y+\'S\').B(\'O\',z.28.x+\'S\').5K({Q:z.2X.y,O:z.2X.x,Z:z.2X.1D,V:z.2X.hb},z.1m,u(){k(z.4e).aB();if(z.23&&z.23.1K==2C){z.23.1F(z.el.K(0),[z.4L])}})};k.ak={2s:u(M){E q.1B(u(){D el=q;D 7x=2*14.2Q/eY;D aZ=2*14.2Q;if(k(el).B(\'Y\')!=\'2y\'&&k(el).B(\'Y\')!=\'1O\'){k(el).B(\'Y\',\'2y\')}el.1l={1S:k(M.1S,q),2F:M.2F,6M:M.6M,an:M.an,aZ:aZ,1P:k.1a.2p(q),Y:k.1a.2R(q),28:14.2Q/2,ct:M.ct,91:M.6R,6R:[],aY:I,7x:2*14.2Q/eY};el.1l.eZ=(el.1l.1P.w-el.1l.2F)/2;el.1l.7O=(el.1l.1P.h-el.1l.6M-el.1l.6M*el.1l.91)/2;el.1l.2D=2*14.2Q/el.1l.1S.1P();el.1l.cI=el.1l.1P.w/2;el.1l.cF=el.1l.1P.h/2-el.1l.6M*el.1l.91;D aS=1j.3t(\'26\');k(aS).B({Y:\'1O\',3B:1,Q:0,O:0});k(el).1R(aS);el.1l.1S.1B(u(2I){ab=k(\'1U\',q).K(0);V=T(el.1l.6M*el.1l.91);if(k.3h.4I){3N=1j.3t(\'1
 U\');k(3N).B(\'Y\',\'1O\');3N.2M=ab.2M;3N.18.69=\'iW aw:ax.ay.c1(1J=60, 18=1, iJ=0, i6=0, hz=0, hx=0)\'}P{3N=1j.3t(\'3N\');if(3N.ga){4H=3N.ga("2d");3N.18.Y=\'1O\';3N.18.V=V+\'S\';3N.18.Z=el.1l.2F+\'S\';3N.V=V;3N.Z=el.1l.2F;4H.i4();4H.i2(0,V);4H.hT(1,-1);4H.hJ(ab,0,0,el.1l.2F,V);4H.6E();4H.hN="hO-4d";D b0=4H.hQ(0,0,0,V);b0.g1(1,"fU(1X, 1X, 1X, 1)");b0.g1(0,"fU(1X, 1X, 1X, 0.6)");4H.hR=b0;if(iR.iv.3F(\'ix\')!=-1){4H.it()}P{4H.ir(0,0,el.1l.2F,V)}}}el.1l.6R[2I]=3N;k(aS).1R(3N)}).1H(\'aV\',u(e){el.1l.aY=1b;el.1l.H=el.1l.7x*0.1*el.1l.H/14.3R(el.1l.H);E I}).1H(\'8q\',u(e){el.1l.aY=I;E I});k.ak.7P(el);el.1l.H=el.1l.7x*0.2;el.1l.gm=1V.6I(u(){el.1l.28+=el.1l.H;if(el.1l.28>aZ)el.1l.28=0;k.ak.7P(el)},20);k(el).1H(\'8q\',u(){el.1l.H=el.1l.7x*0.2*el.1l.H/14.3R(el.1l.H)}).1H(\'3H\',u(e){if(el.1l.aY==I){1s=k.1a.44(e);fe=el.1l.1P.w-1s.x+el.1l.Y.x;el.1l.H=el.1l.ct*el.1l.7x*(el.1l.1P.w/2-fe)/(el.1l.1P.w/2)}})})},7P:u(el){el.1l.1S.1B(u(2I){ch=el.1l.28+2I*el.1l.2D;x=el.1l.eZ*14.5v(ch);y=el.1l.7O
 *14.98(ch);fm=T(2b*(el.1l.7O+y)/(2*el.1l.7O));fl=(el.1l.7O+y)/(2*el.1l.7O);Z=T((el.1l.2F-el.1l.an)*fl+el.1l.an);V=T(Z*el.1l.6M/el.1l.2F);q.18.Q=el.1l.cF+y-V/2+"S";q.18.O=el.1l.cI+x-Z/2+"S";q.18.Z=Z+"S";q.18.V=V+"S";q.18.3B=fm;el.1l.6R[2I].18.Q=T(el.1l.cF+y+V-1-V/2)+"S";el.1l.6R[2I].18.O=T(el.1l.cI+x-Z/2)+"S";el.1l.6R[2I].18.Z=Z+"S";el.1l.6R[2I].18.V=T(V*el.1l.91)+"S"})}};k.fn.h9=k.ak.2s;k.ff={2s:u(M){E q.1B(u(){if(!M.ae||!M.ad)E;D el=q;el.2j={ag:M.ag||bw,ae:M.ae,ad:M.ad,8r:M.8r||\'f7\',af:M.af||\'f7\',2U:M.2U&&2h M.2U==\'u\'?M.2U:I,3i:M.2U&&2h M.3i==\'u\'?M.3i:I,74:M.74&&2h M.74==\'u\'?M.74:I,ai:k(M.ae,q),8f:k(M.ad,q),H:M.H||8w,6e:M.6e||0};el.2j.8f.2G().B(\'V\',\'83\').eq(0).B({V:el.2j.ag+\'S\',19:\'2E\'}).2X();el.2j.ai.1B(u(2I){q.7d=2I}).h6(u(){k(q).2Z(el.2j.af)},u(){k(q).4p(el.2j.af)}).1H(\'5G\',u(e){if(el.2j.6e==q.7d)E;el.2j.ai.eq(el.2j.6e).4p(el.2j.8r).2X().eq(q.7d).2Z(el.2j.8r).2X();el.2j.8f.eq(el.2j.6e).5K({V:0},el.2j.H,u(){q.18.19=\'1n\';if(el.2j.3i){el.2j.3i.1F(el,[q
 ])}}).2X().eq(q.7d).22().5K({V:el.2j.ag},el.2j.H,u(){q.18.19=\'2E\';if(el.2j.2U){el.2j.2U.1F(el,[q])}}).2X();if(el.2j.74){el.2j.74.1F(el,[q,el.2j.8f.K(q.7d),el.2j.ai.K(el.2j.6e),el.2j.8f.K(el.2j.6e)])}el.2j.6e=q.7d}).eq(0).2Z(el.2j.8r).2X();k(q).B(\'V\',k(q).B(\'V\')).B(\'2Y\',\'2O\')})}};k.fn.h7=k.ff.2s;k.3L={1c:U,8u:u(){31=q.2v;if(!31)E;18={fg:k(q).B(\'fg\')||\'\',4w:k(q).B(\'4w\')||\'\',8h:k(q).B(\'8h\')||\'\',fI:k(q).B(\'fI\')||\'\',fJ:k(q).B(\'fJ\')||\'\',fT:k(q).B(\'fT\')||\'\',cH:k(q).B(\'cH\')||\'\',fc:k(q).B(\'fc\')||\'\'};k.3L.1c.B(18);3w=k.3L.g2(31);3w=3w.4v(11 cp("\\\\n","g"),"<br />");k.3L.1c.3w(\'km\');ck=k.3L.1c.K(0).4b;k.3L.1c.3w(3w);Z=k.3L.1c.K(0).4b+ck;if(q.6t.2J&&Z>q.6t.2J[0]){Z=q.6t.2J[0]}q.18.Z=Z+\'S\';if(q.4S==\'cQ\'){V=k.3L.1c.K(0).63+ck;if(q.6t.2J&&V>q.6t.2J[1]){V=q.6t.2J[1]}q.18.V=V+\'S\'}},g2:u(31){co={\'&\':\'&j0;\',\'<\':\'&kB;\',\'>\':\'&gt;\',\'"\':\'&kw;\'};1Y(i in co){31=31.4v(11 cp(i,\'g\'),co[i])}E 31},2s:u(2J){if(k.3L.1c==U){k(\'2e\',1j).1R
 (\'<26 id="fH" 18="Y: 1O; Q: 0; O: 0; 3j: 2O;"></26>\');k.3L.1c=k(\'#fH\')}E q.1B(u(){if(/cQ|bz/.43(q.4S)){if(q.4S==\'bz\'){f9=q.5n(\'1u\');if(!/31|kv/.43(f9)){E}}if(2J&&(2J.1K==cR||(2J.1K==7b&&2J.1h==2))){if(2J.1K==cR)2J=[2J,2J];P{2J[0]=T(2J[0])||8w;2J[1]=T(2J[1])||8w}q.6t={2J:2J}}k(q).5I(k.3L.8u).6S(k.3L.8u).fX(k.3L.8u);k.3L.8u.1F(q)}})}};k.fn.ke=k.3L.2s;k.N={1c:U,8S:U,3E:U,2H:U,4o:U,bp:U,1d:U,2g:U,1S:U,5t:u(){k.N.8S.5t();if(k.N.3E){k.N.3E.2G()}},4u:u(){k.N.1S=U;k.N.2g=U;k.N.4o=k.N.1d.2v;if(k.N.1c.B(\'19\')==\'2E\'){if(k.N.1d.1f.fx){3m(k.N.1d.1f.fx.1u){1e\'bB\':k.N.1c.7k(k.N.1d.1f.fx.1m,k.N.5t);1r;1e\'1z\':k.N.1c.f3(k.N.1d.1f.fx.1m,k.N.5t);1r;1e\'aT\':k.N.1c.fz(k.N.1d.1f.fx.1m,k.N.5t);1r}}P{k.N.1c.2G()}if(k.N.1d.1f.3i)k.N.1d.1f.3i.1F(k.N.1d,[k.N.1c,k.N.3E])}P{k.N.5t()}1V.c6(k.N.2H)},fy:u(){D 1d=k.N.1d;D 4g=k.N.ap(1d);if(1d&&4g.3k!=k.N.4o&&4g.3k.1h>=1d.1f.aL){k.N.4o=4g.3k;k.N.bp=4g.3k;79={2q:k(1d).1p(\'kP\')||\'2q\',2v:4g.3k};k.kN({1u:\'kG\',79:k.kI(79),kF:u(ft){1d.1f.4h=k(
 \'3k\',ft);1P=1d.1f.4h.1P();if(1P>0){D 5x=\'\';1d.1f.4h.1B(u(2I){5x+=\'<90 4G="\'+k(\'2v\',q).31()+\'" 8O="\'+2I+\'" 18="94: aG;">\'+k(\'31\',q).31()+\'</90>\'});if(1d.1f.aR){D 3G=k(\'2v\',1d.1f.4h.K(0)).31();1d.2v=4g.3l+3G+1d.1f.3K+4g.5Q;k.N.6G(1d,4g.3k.1h!=3G.1h?(4g.3l.1h+4g.3k.1h):3G.1h,4g.3k.1h!=3G.1h?(4g.3l.1h+3G.1h):3G.1h)}if(1P>0){k.N.b4(1d,5x)}P{k.N.4u()}}P{k.N.4u()}},6b:1d.1f.aM})}},b4:u(1d,5x){k.N.8S.3w(5x);k.N.1S=k(\'90\',k.N.8S.K(0));k.N.1S.aV(k.N.f2).1H(\'5G\',k.N.fO);D Y=k.1a.2R(1d);D 1P=k.1a.2p(1d);k.N.1c.B(\'Q\',Y.y+1P.hb+\'S\').B(\'O\',Y.x+\'S\').2Z(1d.1f.aK);if(k.N.3E){k.N.3E.B(\'19\',\'2E\').B(\'Q\',Y.y+1P.hb+\'S\').B(\'O\',Y.x+\'S\').B(\'Z\',k.N.1c.B(\'Z\')).B(\'V\',k.N.1c.B(\'V\'))}k.N.2g=0;k.N.1S.K(0).3b=1d.1f.70;k.N.8P(1d,1d.1f.4h.K(0),\'6Z\');if(k.N.1c.B(\'19\')==\'1n\'){if(1d.1f.bA){D bm=k.1a.aj(1d,1b);D bl=k.1a.6h(1d,1b);k.N.1c.B(\'Z\',1d.4b-(k.f5?(bm.l+bm.r+bl.l+bl.r):0)+\'S\')}if(1d.1f.fx){3m(1d.1f.fx.1u){1e\'bB\':k.N.1c.7m(1d.1f.fx.1m);1r;1e\'1z\
 ':k.N.1c.g9(1d.1f.fx.1m);1r;1e\'aT\':k.N.1c.fP(1d.1f.fx.1m);1r}}P{k.N.1c.22()}if(k.N.1d.1f.2U)k.N.1d.1f.2U.1F(k.N.1d,[k.N.1c,k.N.3E])}},fC:u(){D 1d=q;if(1d.1f.4h){k.N.4o=1d.2v;k.N.bp=1d.2v;D 5x=\'\';1d.1f.4h.1B(u(2I){2v=k(\'2v\',q).31().5Z();fR=1d.2v.5Z();if(2v.3F(fR)==0){5x+=\'<90 4G="\'+k(\'2v\',q).31()+\'" 8O="\'+2I+\'" 18="94: aG;">\'+k(\'31\',q).31()+\'</90>\'}});if(5x!=\'\'){k.N.b4(1d,5x);q.1f.aW=1b;E}}1d.1f.4h=U;q.1f.aW=I},6G:u(2q,28,2X){if(2q.aI){D 6K=2q.aI();6K.j8(1b);6K.fr("bW",28);6K.ja("bW",-2X+28);6K.8Z()}P if(2q.aU){2q.aU(28,2X)}P{if(2q.5B){2q.5B=28;2q.dq=2X}}2q.6D()},fD:u(2q){if(2q.5B)E 2q.5B;P if(2q.aI){D 6K=1j.6G.du();D fo=6K.jg();E 0-fo.fr(\'bW\',-jX)}},ap:u(2q){D 4F={2v:2q.2v,3l:\'\',5Q:\'\',3k:\'\'};if(2q.1f.aO){D 97=I;D 5B=k.N.fD(2q)||0;D 56=4F.2v.7h(2q.1f.3K);1Y(D i=0;i<56.1h;i++){if((4F.3l.1h+56[i].1h>=5B||5B==0)&&!97){if(4F.3l.1h<=5B)4F.3k=56[i];P 4F.5Q+=56[i]+(56[i]!=\'\'?2q.1f.3K:\'\');97=1b}P if(97){4F.5Q+=56[i]+(56[i]!=\'\'?2q.1f.3K:\'\')}if(!97){
 4F.3l+=56[i]+(56.1h>1?2q.1f.3K:\'\')}}}P{4F.3k=4F.2v}E 4F},bu:u(e){1V.c6(k.N.2H);D 1d=k.N.ap(q);D 3O=e.7F||e.7A||-1;if(/^13$|27$|35$|36$|38$|40$|^9$/.43(3O)&&k.N.1S){if(1V.2l){1V.2l.cj=1b;1V.2l.ci=I}P{e.al();e.am()}if(k.N.2g!=U)k.N.1S.K(k.N.2g||0).3b=\'\';P k.N.2g=-1;3m(3O){1e 9:1e 13:if(k.N.2g==-1)k.N.2g=0;D 2g=k.N.1S.K(k.N.2g||0);D 3G=2g.5n(\'4G\');q.2v=1d.3l+3G+q.1f.3K+1d.5Q;k.N.4o=1d.3k;k.N.6G(q,1d.3l.1h+3G.1h+q.1f.3K.1h,1d.3l.1h+3G.1h+q.1f.3K.1h);k.N.4u();if(q.1f.6a){4n=T(2g.5n(\'8O\'))||0;k.N.8P(q,q.1f.4h.K(4n),\'6a\')}if(q.76)q.76(I);E 3O!=13;1r;1e 27:q.2v=1d.3l+k.N.4o+q.1f.3K+1d.5Q;q.1f.4h=U;k.N.4u();if(q.76)q.76(I);E I;1r;1e 35:k.N.2g=k.N.1S.1P()-1;1r;1e 36:k.N.2g=0;1r;1e 38:k.N.2g--;if(k.N.2g<0)k.N.2g=k.N.1S.1P()-1;1r;1e 40:k.N.2g++;if(k.N.2g==k.N.1S.1P())k.N.2g=0;1r}k.N.8P(q,q.1f.4h.K(k.N.2g||0),\'6Z\');k.N.1S.K(k.N.2g||0).3b=q.1f.70;if(k.N.1S.K(k.N.2g||0).76)k.N.1S.K(k.N.2g||0).76(I);if(q.1f.aR){D aA=k.N.1S.K(k.N.2g||0).5n(\'4G\');q.2v=1d.3l+aA+q.1f.3K+1d.5Q;if(k
 .N.4o.1h!=aA.1h)k.N.6G(q,1d.3l.1h+k.N.4o.1h,1d.3l.1h+aA.1h)}E I}k.N.fC.1F(q);if(q.1f.aW==I){if(1d.3k!=k.N.4o&&1d.3k.1h>=q.1f.aL)k.N.2H=1V.b1(k.N.fy,q.1f.53);if(k.N.1S){k.N.4u()}}E 1b},8P:u(2q,3k,1u){if(2q.1f[1u]){D 79={};aE=3k.dU(\'*\');1Y(i=0;i<aE.1h;i++){79[aE[i].4S]=aE[i].77.k3}2q.1f[1u].1F(2q,[79])}},f2:u(e){if(k.N.1S){if(k.N.2g!=U)k.N.1S.K(k.N.2g||0).3b=\'\';k.N.1S.K(k.N.2g||0).3b=\'\';k.N.2g=T(q.5n(\'8O\'))||0;k.N.1S.K(k.N.2g||0).3b=k.N.1d.1f.70}},fO:u(2l){1V.c6(k.N.2H);2l=2l||k.2l.jH(1V.2l);2l.al();2l.am();D 1d=k.N.ap(k.N.1d);D 3G=q.5n(\'4G\');k.N.1d.2v=1d.3l+3G+k.N.1d.1f.3K+1d.5Q;k.N.4o=q.5n(\'4G\');k.N.6G(k.N.1d,1d.3l.1h+3G.1h+k.N.1d.1f.3K.1h,1d.3l.1h+3G.1h+k.N.1d.1f.3K.1h);k.N.4u();if(k.N.1d.1f.6a){4n=T(q.5n(\'8O\'))||0;k.N.8P(k.N.1d,k.N.1d.1f.4h.K(4n),\'6a\')}E I},gb:u(e){3O=e.7F||e.7A||-1;if(/13|27|35|36|38|40/.43(3O)&&k.N.1S){if(1V.2l){1V.2l.cj=1b;1V.2l.ci=I}P{e.al();e.am()}E I}},2s:u(M){if(!M.aM||!k.1a){E}if(!k.N.1c){if(k.3h.4I){k(\'2e\',1j).1R(\'<3E 18="19:1n;
 Y:1O;69:aw:ax.ay.c1(1J=0);" id="g5" 2M="ew:I;" ez="0" ey="bX"></3E>\');k.N.3E=k(\'#g5\')}k(\'2e\',1j).1R(\'<26 id="gc" 18="Y: 1O; Q: 0; O: 0; z-b2: jE; 19: 1n;"><aX 18="6X: 0;92: 0; jF-18: 1n; z-b2: jM;">&7J;</aX></26>\');k.N.1c=k(\'#gc\');k.N.8S=k(\'aX\',k.N.1c)}E q.1B(u(){if(q.4S!=\'bz\'&&q.5n(\'1u\')!=\'31\')E;q.1f={};q.1f.aM=M.aM;q.1f.aL=14.3R(T(M.aL)||1);q.1f.aK=M.aK?M.aK:\'\';q.1f.70=M.70?M.70:\'\';q.1f.6a=M.6a&&M.6a.1K==2C?M.6a:U;q.1f.2U=M.2U&&M.2U.1K==2C?M.2U:U;q.1f.3i=M.3i&&M.3i.1K==2C?M.3i:U;q.1f.6Z=M.6Z&&M.6Z.1K==2C?M.6Z:U;q.1f.bA=M.bA||I;q.1f.aO=M.aO||I;q.1f.3K=q.1f.aO?(M.3K||\', \'):\'\';q.1f.aR=M.aR?1b:I;q.1f.53=14.3R(T(M.53)||aF);if(M.fx&&M.fx.1K==7n){if(!M.fx.1u||!/bB|1z|aT/.43(M.fx.1u)){M.fx.1u=\'1z\'}if(M.fx.1u==\'1z\'&&!k.fx.1z)E;if(M.fx.1u==\'aT\'&&!k.fx.5W)E;M.fx.1m=14.3R(T(M.fx.1m)||8w);if(M.fx.1m>q.1f.53){M.fx.1m=q.1f.53-2b}q.1f.fx=M.fx}q.1f.4h=U;q.1f.aW=I;k(q).1p(\'bu\',\'fQ\').6D(u(){k.N.1d=q;k.N.4o=q.2v}).fX(k.N.gb).6S(k.N.bu).5I(u(){k.N.2H=1V.b1(k.
 N.4u,jP)})})}};k.fn.jO=k.N.2s;k.1y={2H:U,4E:U,29:U,2D:10,28:u(el,4P,2D,di){k.1y.4E=el;k.1y.29=4P;k.1y.2D=T(2D)||10;k.1y.2H=1V.6I(k.1y.db,T(di)||40)},db:u(){1Y(i=0;i<k.1y.29.1h;i++){if(!k.1y.29[i].30){k.1y.29[i].30=k.21(k.1a.bN(k.1y.29[i]),k.1a.82(k.1y.29[i]),k.1a.6W(k.1y.29[i]))}P{k.1y.29[i].30.t=k.1y.29[i].2V;k.1y.29[i].30.l=k.1y.29[i].3g}if(k.1y.4E.A&&k.1y.4E.A.7W==1b){6f={x:k.1y.4E.A.2x,y:k.1y.4E.A.2r,1D:k.1y.4E.A.1C.1D,hb:k.1y.4E.A.1C.hb}}P{6f=k.21(k.1a.bN(k.1y.4E),k.1a.82(k.1y.4E))}if(k.1y.29[i].30.t>0&&k.1y.29[i].30.y+k.1y.29[i].30.t>6f.y){k.1y.29[i].2V-=k.1y.2D}P if(k.1y.29[i].30.t<=k.1y.29[i].30.h&&k.1y.29[i].30.t+k.1y.29[i].30.hb<6f.y+6f.hb){k.1y.29[i].2V+=k.1y.2D}if(k.1y.29[i].30.l>0&&k.1y.29[i].30.x+k.1y.29[i].30.l>6f.x){k.1y.29[i].3g-=k.1y.2D}P if(k.1y.29[i].30.l<=k.1y.29[i].30.jT&&k.1y.29[i].30.l+k.1y.29[i].30.1D<6f.x+6f.1D){k.1y.29[i].3g+=k.1y.2D}}},8v:u(){1V.6c(k.1y.2H);k.1y.4E=U;k.1y.29=U;1Y(i in k.1y.29){k.1y.29[i].30=U}}};k.6y={2s:u(M){E q.1B(u(){D el=q;el.
 1G={1S:k(M.1S,q),1Z:k(M.1Z,q),1M:k.1a.2R(q),2F:M.2F,aN:M.aN,7R:M.7R,dw:M.dw,51:M.51,6q:M.6q};k.6y.aJ(el,0);k(1V).1H(\'jS\',u(){el.1G.1M=k.1a.2R(el);k.6y.aJ(el,0);k.6y.7P(el)});k.6y.7P(el);el.1G.1S.1H(\'aV\',u(){k(el.1G.aN,q).K(0).18.19=\'2E\'}).1H(\'8q\',u(){k(el.1G.aN,q).K(0).18.19=\'1n\'});k(1j).1H(\'3H\',u(e){D 1s=k.1a.44(e);D 5q=0;if(el.1G.51&&el.1G.51==\'b8\')D aQ=1s.x-el.1G.1M.x-(el.4b-el.1G.2F*el.1G.1S.1P())/2-el.1G.2F/2;P if(el.1G.51&&el.1G.51==\'2N\')D aQ=1s.x-el.1G.1M.x-el.4b+el.1G.2F*el.1G.1S.1P();P D aQ=1s.x-el.1G.1M.x;D dB=14.5Y(1s.y-el.1G.1M.y-el.63/2,2);el.1G.1S.1B(u(2I){46=14.dm(14.5Y(aQ-2I*el.1G.2F,2)+dB);46-=el.1G.2F/2;46=46<0?0:46;46=46>el.1G.7R?el.1G.7R:46;46=el.1G.7R-46;bC=el.1G.6q*46/el.1G.7R;q.18.Z=el.1G.2F+bC+\'S\';q.18.O=el.1G.2F*2I+5q+\'S\';5q+=bC});k.6y.aJ(el,5q)})})},aJ:u(el,5q){if(el.1G.51)if(el.1G.51==\'b8\')el.1G.1Z.K(0).18.O=(el.4b-el.1G.2F*el.1G.1S.1P())/2-5q/2+\'S\';P if(el.1G.51==\'O\')el.1G.1Z.K(0).18.O=-5q/el.1G.1S.1P()+\'S\';P if(el.1G.5
 1==\'2N\')el.1G.1Z.K(0).18.O=(el.4b-el.1G.2F*el.1G.1S.1P())-5q/2+\'S\';el.1G.1Z.K(0).18.Z=el.1G.2F*el.1G.1S.1P()+5q+\'S\'},7P:u(el){el.1G.1S.1B(u(2I){q.18.Z=el.1G.2F+\'S\';q.18.O=el.1G.2F*2I+\'S\'})}};k.fn.jD=k.6y.2s;k.1v={M:{2B:10,eV:\'1Q/jG.eF\',eT:\'<1U 2M="1Q/5P.eC" />\',eN:0.8,e3:\'jK ab\',e5:\'5d\',3V:8w},jI:I,jU:I,6r:U,9d:I,9e:I,ca:u(2l){if(!k.1v.9e||k.1v.9d)E;D 3O=2l.7F||2l.7A||-1;3m(3O){1e 35:if(k.1v.6r)k.1v.28(U,k(\'a[@4G=\'+k.1v.6r+\']:k7\').K(0));1r;1e 36:if(k.1v.6r)k.1v.28(U,k(\'a[@4G=\'+k.1v.6r+\']:k5\').K(0));1r;1e 37:1e 8:1e 33:1e 80:1e k8:D ar=k(\'#9a\');if(ar.K(0).52!=U){ar.K(0).52.1F(ar.K(0))}1r;1e 38:1r;1e 39:1e 34:1e 32:1e ka:1e 78:D aD=k(\'#9b\');if(aD.K(0).52!=U){aD.K(0).52.1F(aD.K(0))}1r;1e 40:1r;1e 27:k.1v.ah();1r}},7W:u(M){if(M)k.21(k.1v.M,M);if(1V.2l){k(\'2e\',1j).1H(\'6S\',k.1v.ca)}P{k(1j).1H(\'6S\',k.1v.ca)}k(\'a\').1B(u(){el=k(q);dQ=el.1p(\'4G\')||\'\';eA=el.1p(\'3f\')||\'\';eu=/\\.eC|\\.jY|\\.95|\\.eF|\\.jW/g;if(eA.5Z().bU(eu)!=U&&dQ.5Z().3F(\'
 eJ\')==0){el.1H(\'5G\',k.1v.28)}});if(k.3h.4I){3E=1j.3t(\'3E\');k(3E).1p({id:\'b6\',2M:\'ew:I;\',ez:\'bX\',ey:\'bX\'}).B({19:\'1n\',Y:\'1O\',Q:\'0\',O:\'0\',69:\'aw:ax.ay.c1(1J=0)\'});k(\'2e\').1R(3E)}8Q=1j.3t(\'26\');k(8Q).1p(\'id\',\'bk\').B({Y:\'1O\',19:\'1n\',Q:\'0\',O:\'0\',1J:0}).1R(1j.8F(\' \')).1H(\'5G\',k.1v.ah);6L=1j.3t(\'26\');k(6L).1p(\'id\',\'dZ\').B({4X:k.1v.M.2B+\'S\'}).1R(1j.8F(\' \'));bZ=1j.3t(\'26\');k(bZ).1p(\'id\',\'e1\').B({4X:k.1v.M.2B+\'S\',5M:k.1v.M.2B+\'S\'}).1R(1j.8F(\' \'));cc=1j.3t(\'a\');k(cc).1p({id:\'jh\',3f:\'#\'}).B({Y:\'1O\',2N:k.1v.M.2B+\'S\',Q:\'0\'}).1R(k.1v.M.eT).1H(\'5G\',k.1v.ah);7t=1j.3t(\'26\');k(7t).1p(\'id\',\'bh\').B({Y:\'2y\',b9:\'O\',6X:\'0 ao\',3B:1}).1R(6L).1R(bZ).1R(cc);2a=1j.3t(\'1U\');2a.2M=k.1v.M.eV;k(2a).1p(\'id\',\'ep\').B({Y:\'1O\'});4R=1j.3t(\'a\');k(4R).1p({id:\'9a\',3f:\'#\'}).B({Y:\'1O\',19:\'1n\',2Y:\'2O\',eQ:\'1n\'}).1R(1j.8F(\' \'));4Q=1j.3t(\'a\');k(4Q).1p({id:\'9b\',3f:\'#\'}).B({Y:\'1O\',2Y:\'2O\',eQ:\'1n\'}).
 1R(1j.8F(\' \'));1Z=1j.3t(\'26\');k(1Z).1p(\'id\',\'e0\').B({19:\'1n\',Y:\'2y\',2Y:\'2O\',b9:\'O\',6X:\'0 ao\',Q:\'0\',O:\'0\',3B:2}).1R([2a,4R,4Q]);6N=1j.3t(\'26\');k(6N).1p(\'id\',\'aq\').B({19:\'1n\',Y:\'1O\',2Y:\'2O\',Q:\'0\',O:\'0\',b9:\'b8\',7f:\'b7\',j7:\'0\'}).1R([1Z,7t]);k(\'2e\').1R(8Q).1R(6N)},28:u(e,C){el=C?k(C):k(q);at=el.1p(\'4G\');D 6P,4n,4R,4Q;if(at!=\'eJ\'){k.1v.6r=at;8N=k(\'a[@4G=\'+at+\']\');6P=8N.1P();4n=8N.b2(C?C:q);4R=8N.K(4n-1);4Q=8N.K(4n+1)}8H=el.1p(\'3f\');6L=el.1p(\'45\');3I=k.1a.6W();8Q=k(\'#bk\');if(!k.1v.9e){k.1v.9e=1b;if(k.3h.4I){k(\'#b6\').B(\'V\',14.3v(3I.ih,3I.h)+\'S\').B(\'Z\',14.3v(3I.iw,3I.w)+\'S\').22()}8Q.B(\'V\',14.3v(3I.ih,3I.h)+\'S\').B(\'Z\',14.3v(3I.iw,3I.w)+\'S\').22().eo(bw,k.1v.M.eN,u(){k.1v.bd(8H,6L,3I,6P,4n,4R,4Q)});k(\'#aq\').B(\'Z\',14.3v(3I.iw,3I.w)+\'S\')}P{k(\'#9a\').K(0).52=U;k(\'#9b\').K(0).52=U;k.1v.bd(8H,6L,3I,6P,4n,4R,4Q)}E I},bd:u(8H,jA,3I,6P,4n,4R,4Q){k(\'#bi\').aB();aC=k(\'#9a\');aC.2G();as=k(\'#9b\');as.2G();2a=k(
 \'#ep\');1Z=k(\'#e0\');6N=k(\'#aq\');7t=k(\'#bh\').B(\'3j\',\'2O\');k(\'#dZ\').3w(6L);k.1v.9d=1b;if(6P)k(\'#e1\').3w(k.1v.M.e3+\' \'+(4n+1)+\' \'+k.1v.M.e5+\' \'+6P);if(4R){aC.K(0).52=u(){q.5I();k.1v.28(U,4R);E I}}if(4Q){as.K(0).52=u(){q.5I();k.1v.28(U,4Q);E I}}2a.22();8E=k.1a.2p(1Z.K(0));5f=14.3v(8E.1D,2a.K(0).Z+k.1v.M.2B*2);5T=14.3v(8E.hb,2a.K(0).V+k.1v.M.2B*2);2a.B({O:(5f-2a.K(0).Z)/2+\'S\',Q:(5T-2a.K(0).V)/2+\'S\'});1Z.B({Z:5f+\'S\',V:5T+\'S\'}).22();e4=k.1a.bq();6N.B(\'Q\',3I.t+(e4.h/15)+\'S\');if(6N.B(\'19\')==\'1n\'){6N.22().7m(k.1v.M.3V)}6U=11 aH;k(6U).1p(\'id\',\'bi\').1H(\'jk\',u(){5f=6U.Z+k.1v.M.2B*2;5T=6U.V+k.1v.M.2B*2;2a.2G();1Z.5K({V:5T},8E.hb!=5T?k.1v.M.3V:1,u(){1Z.5K({Z:5f},8E.1D!=5f?k.1v.M.3V:1,u(){1Z.cA(6U);k(6U).B({Y:\'1O\',O:k.1v.M.2B+\'S\',Q:k.1v.M.2B+\'S\'}).7m(k.1v.M.3V,u(){dS=k.1a.2p(7t.K(0));if(4R){aC.B({O:k.1v.M.2B+\'S\',Q:k.1v.M.2B+\'S\',Z:5f/2-k.1v.M.2B*3+\'S\',V:5T-k.1v.M.2B*2+\'S\'}).22()}if(4Q){as.B({O:5f/2+k.1v.M.2B*2+\'S\',Q:k.1v.M.2B+\'S\',Z
 :5f/2-k.1v.M.2B*3+\'S\',V:5T-k.1v.M.2B*2+\'S\'}).22()}7t.B({Z:5f+\'S\',Q:-dS.hb+\'S\',3j:\'dR\'}).5K({Q:-1},k.1v.M.3V,u(){k.1v.9d=I})})})})});6U.2M=8H},ah:u(){k(\'#bi\').aB();k(\'#aq\').2G();k(\'#bh\').B(\'3j\',\'2O\');k(\'#bk\').eo(bw,0,u(){k(q).2G();if(k.3h.4I){k(\'#b6\').2G()}});k(\'#9a\').K(0).52=U;k(\'#9b\').K(0).52=U;k.1v.6r=U;k.1v.9e=I;k.1v.9d=I;E I}};k.2A={5E:[],eS:u(){q.5I();X=q.3e;id=k.1p(X,\'id\');if(k.2A.5E[id]!=U){1V.6c(k.2A.5E[id])}1z=X.L.3x+1;if(X.L.1Q.1h<1z){1z=1}1Q=k(\'1U\',X.L.5F);X.L.3x=1z;if(1Q.1P()>0){1Q.7k(X.L.3V,k.2A.8B)}},eG:u(){q.5I();X=q.3e;id=k.1p(X,\'id\');if(k.2A.5E[id]!=U){1V.6c(k.2A.5E[id])}1z=X.L.3x-1;1Q=k(\'1U\',X.L.5F);if(1z<1){1z=X.L.1Q.1h}X.L.3x=1z;if(1Q.1P()>0){1Q.7k(X.L.3V,k.2A.8B)}},2H:u(c){X=1j.cP(c);if(X.L.6w){1z=X.L.3x;7o(1z==X.L.3x){1z=1+T(14.6w()*X.L.1Q.1h)}}P{1z=X.L.3x+1;if(X.L.1Q.1h<1z){1z=1}}1Q=k(\'1U\',X.L.5F);X.L.3x=1z;if(1Q.1P()>0){1Q.7k(X.L.3V,k.2A.8B)}},go:u(o){D X;if(o&&o.1K==7n){if(o.2a){X=1j.cP(o.2a.X);6b=1V.kK.3f.7h("#"
 );o.2a.6B=U;if(6b.1h==2){1z=T(6b[1]);22=6b[1].4v(1z,\'\');if(k.1p(X,\'id\')!=22){1z=1}}P{1z=1}}if(o.8A){o.8A.5I();X=o.8A.3e.3e;id=k.1p(X,\'id\');if(k.2A.5E[id]!=U){1V.6c(k.2A.5E[id])}6b=o.8A.3f.7h("#");1z=T(6b[1]);22=6b[1].4v(1z,\'\');if(k.1p(X,\'id\')!=22){1z=1}}if(X.L.1Q.1h<1z||1z<1){1z=1}X.L.3x=1z;5h=k.1a.2p(X);e8=k.1a.aj(X);e9=k.1a.6h(X);if(X.L.3s){X.L.3s.o.B(\'19\',\'1n\')}if(X.L.3r){X.L.3r.o.B(\'19\',\'1n\')}if(X.L.2a){y=T(e8.t)+T(e9.t);if(X.L.1T){if(X.L.1T.5z==\'Q\'){y+=X.L.1T.4q.hb}P{5h.h-=X.L.1T.4q.hb}}if(X.L.2w){if(X.L.2w&&X.L.2w.6s==\'Q\'){y+=X.L.2w.4q.hb}P{5h.h-=X.L.2w.4q.hb}}if(!X.L.cV){X.L.eg=o.2a?o.2a.V:(T(X.L.2a.B(\'V\'))||0);X.L.cV=o.2a?o.2a.Z:(T(X.L.2a.B(\'Z\'))||0)}X.L.2a.B(\'Q\',y+(5h.h-X.L.eg)/2+\'S\');X.L.2a.B(\'O\',(5h.1D-X.L.cV)/2+\'S\');X.L.2a.B(\'19\',\'2E\')}1Q=k(\'1U\',X.L.5F);if(1Q.1P()>0){1Q.7k(X.L.3V,k.2A.8B)}P{aP=k(\'a\',X.L.1T.o).K(1z-1);k(aP).2Z(X.L.1T.64);D 1U=11 aH();1U.X=k.1p(X,\'id\');1U.1z=1z-1;1U.2M=X.L.1Q[X.L.3x-1].2M;if(1U.23){1U.6B=
 U;k.2A.19.1F(1U)}P{1U.6B=k.2A.19}if(X.L.2w){X.L.2w.o.3w(X.L.1Q[1z-1].6v)}}}},8B:u(){X=q.3e.3e;X.L.5F.B(\'19\',\'1n\');if(X.L.1T.64){aP=k(\'a\',X.L.1T.o).4p(X.L.1T.64).K(X.L.3x-1);k(aP).2Z(X.L.1T.64)}D 1U=11 aH();1U.X=k.1p(X,\'id\');1U.1z=X.L.3x-1;1U.2M=X.L.1Q[X.L.3x-1].2M;if(1U.23){1U.6B=U;k.2A.19.1F(1U)}P{1U.6B=k.2A.19}if(X.L.2w){X.L.2w.o.3w(X.L.1Q[X.L.3x-1].6v)}},19:u(){X=1j.cP(q.X);if(X.L.3s){X.L.3s.o.B(\'19\',\'1n\')}if(X.L.3r){X.L.3r.o.B(\'19\',\'1n\')}5h=k.1a.2p(X);y=0;if(X.L.1T){if(X.L.1T.5z==\'Q\'){y+=X.L.1T.4q.hb}P{5h.h-=X.L.1T.4q.hb}}if(X.L.2w){if(X.L.2w&&X.L.2w.6s==\'Q\'){y+=X.L.2w.4q.hb}P{5h.h-=X.L.2w.4q.hb}}kD=k(\'.cz\',X);y=y+(5h.h-q.V)/2;x=(5h.1D-q.Z)/2;X.L.5F.B(\'Q\',y+\'S\').B(\'O\',x+\'S\').3w(\'<1U 2M="\'+q.2M+\'" />\');X.L.5F.7m(X.L.3V);3r=X.L.3x+1;if(3r>X.L.1Q.1h){3r=1}3s=X.L.3x-1;if(3s<1){3s=X.L.1Q.1h}X.L.3r.o.B(\'19\',\'2E\').B(\'Q\',y+\'S\').B(\'O\',x+2*q.Z/3+\'S\').B(\'Z\',q.Z/3+\'S\').B(\'V\',q.V+\'S\').1p(\'45\',X.L.1Q[3r-1].6v);X.L.3r.o.K(0).3f=\'
 #\'+3r+k.1p(X,\'id\');X.L.3s.o.B(\'19\',\'2E\').B(\'Q\',y+\'S\').B(\'O\',x+\'S\').B(\'Z\',q.Z/3+\'S\').B(\'V\',q.V+\'S\').1p(\'45\',X.L.1Q[3s-1].6v);X.L.3s.o.K(0).3f=\'#\'+3s+k.1p(X,\'id\')},2s:u(o){if(!o||!o.1Z||k.2A.5E[o.1Z])E;D 1Z=k(\'#\'+o.1Z);D el=1Z.K(0);if(el.18.Y!=\'1O\'&&el.18.Y!=\'2y\'){el.18.Y=\'2y\'}el.18.2Y=\'2O\';if(1Z.1P()==0)E;el.L={};el.L.1Q=o.1Q?o.1Q:[];el.L.6w=o.6w&&o.6w==1b||I;8b=el.dU(\'kA\');1Y(i=0;i<8b.1h;i++){7I=el.L.1Q.1h;el.L.1Q[7I]={2M:8b[i].2M,6v:8b[i].45||8b[i].kC||\'\'}}if(el.L.1Q.1h==0){E}el.L.4m=k.21(k.1a.2R(el),k.1a.2p(el));el.L.d5=k.1a.aj(el);el.L.cL=k.1a.6h(el);t=T(el.L.d5.t)+T(el.L.cL.t);b=T(el.L.d5.b)+T(el.L.cL.b);k(\'1U\',el).aB();el.L.3V=o.3V?o.3V:er;if(o.5z||o.88||o.64){el.L.1T={};1Z.1R(\'<26 6A="eL"></26>\');el.L.1T.o=k(\'.eL\',el);if(o.88){el.L.1T.88=o.88;el.L.1T.o.2Z(o.88)}if(o.64){el.L.1T.64=o.64}el.L.1T.o.B(\'Y\',\'1O\').B(\'Z\',el.L.4m.w+\'S\');if(o.5z&&o.5z==\'Q\'){el.L.1T.5z=\'Q\';el.L.1T.o.B(\'Q\',t+\'S\')}P{el.L.1T.5z=\'4l\';
 el.L.1T.o.B(\'4l\',b+\'S\')}el.L.1T.au=o.au?o.au:\' \';1Y(D i=0;i<el.L.1Q.1h;i++){7I=T(i)+1;el.L.1T.o.1R(\'<a 3f="#\'+7I+o.1Z+\'" 6A="ku" 45="\'+el.L.1Q[i].6v+\'">\'+7I+\'</a>\'+(7I!=el.L.1Q.1h?el.L.1T.au:\'\'))}k(\'a\',el.L.1T.o).1H(\'5G\',u(){k.2A.go({8A:q})});el.L.1T.4q=k.1a.2p(el.L.1T.o.K(0))}if(o.6s||o.8l){el.L.2w={};1Z.1R(\'<26 6A="eK">&7J;</26>\');el.L.2w.o=k(\'.eK\',el);if(o.8l){el.L.2w.8l=o.8l;el.L.2w.o.2Z(o.8l)}el.L.2w.o.B(\'Y\',\'1O\').B(\'Z\',el.L.4m.w+\'S\');if(o.6s&&o.6s==\'Q\'){el.L.2w.6s=\'Q\';el.L.2w.o.B(\'Q\',(el.L.1T&&el.L.1T.5z==\'Q\'?el.L.1T.4q.hb+t:t)+\'S\')}P{el.L.2w.6s=\'4l\';el.L.2w.o.B(\'4l\',(el.L.1T&&el.L.1T.5z==\'4l\'?el.L.1T.4q.hb+b:b)+\'S\')}el.L.2w.4q=k.1a.2p(el.L.2w.o.K(0))}if(o.az){el.L.3r={az:o.az};1Z.1R(\'<a 3f="#2\'+o.1Z+\'" 6A="eR">&7J;</a>\');el.L.3r.o=k(\'.eR\',el);el.L.3r.o.B(\'Y\',\'1O\').B(\'19\',\'1n\').B(\'2Y\',\'2O\').B(\'4w\',\'eB\').2Z(el.L.3r.az);el.L.3r.o.1H(\'5G\',k.2A.eS)}if(o.av){el.L.3s={av:o.av};1Z.1R(\'<a 3f="#0\'+o.1Z+
 \'" 6A="ev">&7J;</a>\');el.L.3s.o=k(\'.ev\',el);el.L.3s.o.B(\'Y\',\'1O\').B(\'19\',\'1n\').B(\'2Y\',\'2O\').B(\'4w\',\'eB\').2Z(el.L.3s.av);el.L.3s.o.1H(\'5G\',k.2A.eG)}1Z.cA(\'<26 6A="cz"></26>\');el.L.5F=k(\'.cz\',el);el.L.5F.B(\'Y\',\'1O\').B(\'Q\',\'3c\').B(\'O\',\'3c\').B(\'19\',\'1n\');if(o.2a){1Z.cA(\'<26 6A="eD" 18="19: 1n;"><1U 2M="\'+o.2a+\'" /></26>\');el.L.2a=k(\'.eD\',el);el.L.2a.B(\'Y\',\'1O\');D 1U=11 aH();1U.X=o.1Z;1U.2M=o.2a;if(1U.23){1U.6B=U;k.2A.go({2a:1U})}P{1U.6B=u(){k.2A.go({2a:q})}}}P{k.2A.go({1Z:el})}if(o.cB){do=T(o.cB)*aF}k.2A.5E[o.1Z]=o.cB?1V.6I(\'k.2A.2H(\\\'\'+o.1Z+\'\\\')\',do):U}};k.X=k.2A.2s;k.8e={cN:u(e){3O=e.7F||e.7A||-1;if(3O==9){if(1V.2l){1V.2l.cj=1b;1V.2l.ci=I}P{e.al();e.am()}if(q.aI){1j.6G.du().31="\\t";q.dv=u(){q.6D();q.dv=U}}P if(q.aU){28=q.5B;2X=q.dq;q.2v=q.2v.iL(0,28)+"\\t"+q.2v.hm(2X);q.aU(28+1,28+1);q.6D()}E I}},58:u(){E q.1B(u(){if(q.7D&&q.7D==1b){k(q).3p(\'7E\',k.8e.cN);q.7D=I}})},2s:u(){E q.1B(u(){if(q.4S==\'cQ\'&&(!q.7D||q.7D==I
 )){k(q).1H(\'7E\',k.8e.cN);q.7D=1b}})}};k.fn.21({hS:k.8e.2s,hP:k.8e.58});',62,1292,'||||||||||||||||||||jQuery||||||this||||function||||||dragCfg|css|elm|var|return|dragged|easing|speed|false|callback|get|ss|options|iAuto|left|else|top|iResize|px|parseInt|null|height|oldStyle|slideshow|position|width||new|iDrag||Math||||style|display|iUtil|true|helper|subject|case|autoCFG|resizeOptions|length|dropCfg|document|iEL|carouselCfg|duration|none|interfaceFX|attr|sizes|break|pointer|iSort|type|ImageBox|queue|iDrop|iAutoscroller|slide|resizeElement|each|oC|wb|newSizes|apply|fisheyeCfg|bind|delta|opacity|constructor|custom|pos|axis|absolute|size|images|append|items|slideslinks|img|window|firstNum|255|for|container||extend|show|complete|cont||div||start|elsToScroll|loader|100|oR||body|oldP|selectedItem|typeof|elem|accordionCfg|props|event|parseFloat|newPosition|containment|getSize|field|ny|build|iTooltip|selectHelper|value|slideCaption|nx|relative|tp|islideshow|border|Function|step|blo
 ck|itemWidth|hide|timer|nr|limit|fractions|dequeue|src|right|hidden|direction|PI|getPosition|cursorAt|onChange|onShow|scrollTop|result|end|overflow|addClass|parentData|text|||||||||scr|className|0px|iSlider|parentNode|href|scrollLeft|browser|onHide|visibility|item|pre|switch|selectdrug|wrapper|unbind|newCoords|nextslide|prevslide|createElement|values|max|html|currentslide|handle|onSlide|margins|zIndex|wrs|min|iframe|indexOf|valueToAdd|mousemove|pageSize|zones|multipleSeparator|iExpander|curCSS|canvas|pressedKey|accept|resizeDirection|abs|onStop|diff|handlers|fadeDuration|highlighted|dhs|toggle|dragElem||times||test|getPointer|title|distance||so|vp|horizontally|offsetWidth|startLeft|out|transferEl|startTop|subjectValue|lastSuggestion|vertically|ghosting|DropOutDirectiont|bottom|oP|iteration|lastValue|removeClass|dimm|slideCfg|ifxFirstDisplay|currentPointer|clear|replace|fontSize|onDrag|down|percent|onStart|nWidth|color|ratio|elToScroll|fieldData|rel|context|msie|documentEleme
 nt|params|to|shs|dragHandle|fxCheckTag|els|nextImage|prevImage|tagName|tooltipCFG|up|helperclass|endLeft|paddingLeft|currentStyle|borderTopWidth||halign|onclick|delay|nodeEl||chunks|endTop|destroy|dragmoveBy|borderLeftWidth|mousedown|nHeight|from|dhe|containerW|string|slidePos|si|collected|marginLeft|overzone|marginBottom|getAttribute|marginTop|marginRight|toAdd|zonex|clonedEl|empty|newStyles|cos|hight|toWrite|zoney|linksPosition|OpenClose|selectionStart|clientScroll|cnt|slideshows|holder|click|restoreStyle|blur|onDragModifier|animate|elS|paddingBottom|toDrag|sw|close|post|animationHandler|styles|containerH|prop|sortCfg|BlindDirection|nmp|pow|toLowerCase||mouseup|oldVisibility|offsetHeight|activeLinkClass|old|paddingTop|grid|point|filter|onSelect|url|clearInterval|fxh|currentPanel|elementData|borderBottomWidth|getBorder|cur|paddingRight|borderRightWidth|puff|snapDistance|tolerance|revert|hpc|maxWidth|currentRel|captionPosition|Expander|orig|caption|random|3000|iFisheye|Scale
 |class|onload|wr|focus|restore|128|selection|parseColor|setInterval|current|selRange|captionText|itemHeight|outerContainer|newDimensions|totalImages|getHeight|reflections|keyup|sliders|imageEl|getWidth|getScroll|margin|Draggable|onHighlight|selectClass|getTime|Date|oldStyleAttr|onClick||scrollIntoView|firstChild||data|ActiveXObject|Array|focused|accordionPos|open|backgroundColor|zoneh|split|oD|zonew|fadeOut|user|fadeIn|Object|while|minLeft|nw|startDrag|minTop|captionEl|newTop|newLeft|frameClass|increment|F0|0x|keyCode|139|toInteger|hasTabsEnabled|keydown|charCode|cssRules|rule|indic|nbsp|rgb|np|oldDisplay|opera|radiusY|positionItems|onOut|proximity|efx|onHover|hash|changed|init|sc|inFrontOf|selectKeyHelper||selectCurrent|getSizeLite|1px|contBorders||ts|parentEl|linksClass|parentBorders|yproc|imgs|nRx|fnc|iTTabs|panels|insideParent|fontWeight|object|nRy|clientWidth|captionClass|namedColors|offsetLeft|serialize|cssSides|mouseout|activeClass|targetEl|offsetTop|expand|stop|400|p
 r|directionIncrement|clientHeight|link|showImage|move|sx|containerSize|createTextNode|jEl|imageSrc|ser|newPos|selectedone|minHeight|maxHeight|gallery|dir|applyOn|overlay|sh|content|maxRight|maxBottom|tooltipHelper|count|onselectstop|onselect|select|li|reflectionSize|padding|selectBorders|cursor|png|parent|finishedPre|sin|xproc|ImageBoxPrevImage|ImageBoxNextImage|bounceout|animationInProgress|opened|sy|destroyWrapper|buildWrapper|diffWidth|diffHeight|iIndex|diffX|diffY|prot|hidehelper|dEs|isDraggable|onDrop|minWidth|side|isDroppable|onActivate|dragstop|startTime|211|192|nodeName|self|oldPosition|exec|opt|getValues|styleSheets|sideEnd|borderColor|ne|handleEl|unit|DoFold|5625|oldTitle|SliderContainer|unfold|9999|ScrollTo|cssText|oldColor|alpha|2000|prev|selectKeyUp|os|selectKeyDown|selectcheck|dragEl|checkhover|DraggableDestroy|next|key|hoverclass|activeclass|sl|st|image||panelSelector|headerSelector|hoverClass|panelHeight|hideImage|headers|getPadding|iCarousel|preventDefault|s
 topPropagation|itemMinWidth|auto|getFieldValues|ImageBoxOuterContainer|prevEl|nextImageEl|linkRel|linksSeparator|prevslideClass|progid|DXImageTransform|Microsoft|nextslideClass|valToAdd|remove|prevImageEl|nextEl|childs|1000|default|Image|createTextRange|positionContainer|helperClass|minchars|source|itemsText|multiple|lnk|posx|autofill|reflexions|blind|setSelectionRange|mouseover|inCache|ul|protectRotation|maxRotation|gradient|setTimeout|index|elPosition|writeItems|String|ImageBoxIframe|transparent|center|textAlign|paddingRightSize|paddingTopSize|bounce|loadImage|borderLeftSize|borderBottomSize|borderRightSize|ImageBoxCaption|ImageBoxCurrentImage|moveDrag|ImageBoxOverlay|paddings|borders|idsa|firstStep|currentValue|getClient||stopDrag|borderTopSize|autocomplete|zoom|300|hidefocused|intersect|INPUT|inputWidth|fade|extraWidth|sortable|restricted|isSlider|tabindex|fitToContainer|snapToGrid|slider|prevTop|prevLeft|floats|getPositionLite|modifyContainer|getContainment|lastSi|Slide
 rIteration|sliderEl|selectstop|match|linear|character|no|bouncein|captionImages|asin|Alpha|Selectserialize|mouse|initialPosition|measure|clearTimeout|helperSize|getMargins|tooltipURL|keyPressed|applyOnHover|closeEl|10000|parentPos|sliderSize|sliderPos|angle|returnValue|cancelBubble|spacer|oldBorder|pulse|169|entities|RegExp|Color|Pulsate||rotationSpeed|parseStyle|stopAnim|cssSidesEnd|shake|Shake|slideshowHolder|prepend|autoplay|floatVal|borderWidth|scroll|paddingY|pValue|letterSpacing|paddingX|paddingBottomSize|pause|oBor|clnt|doTab|autoSize|getElementById|TEXTAREA|Number|traverseDOM|func|draginit|loaderWidth|scrollHeight|paddingLeftSize|scrollWidth|oneIsSortable|innerWidth|innerHeight|shrink|windowSize|unselectable|oPad|dragmove|oldFloat|cssProps|colorCssProps|107|doScroll|addItem|SortableAddItem||DroppableDestroy|fxe||interval|after|insertBefore||sqrt|cloneNode|time|check|selectionEnd|offsetParent|Width|sortHelper|createRange|onblur|valign|||onout|224|posy|wid|isSortable|1
 65|zindex|245|notColor|140|240|230|144|styleFloat|onhover|Droppable|emptyGIF|relAttr|visible|captionSize|dragstart|getElementsByTagName|listStyle|dragHelper|getHeightMinMax|onResize|ImageBoxCaptionText|ImageBoxContainer|ImageBoxCaptionImages||textImage|clientSize|textImageFrom|userSelect|onDragStop|slidePad|slideBor|highlight|shc|hlt|checkdrop|fit||loaderHeight||onDragStart|KhtmlUserSelect|remeasure|||on|fadeTo|ImageBoxLoader||500|||imageTypes|slideshowPrevslide|javascript|selectstopApply|scrolling|frameborder|hrefAttr|30px|jpg|slideshowLoader|selectedclass|gif|goprev|oldOverflow|isFunction|imagebox|slideshowCaption|slideshowLinks|directions|overlayOpacity|se|trim|textDecoration|slideshowNextSlide|gonext|closeHTML|selectcheckApply|loaderSRC|selectstart|isSelectable|360|radiusX|set|grow|hoverItem|SlideOutUp|leftUnit|boxModel|interfaceColorFX|fakeAccordionClass|togglever|elType|iBounce|paddingBottomUnit|wordSpacing|150|mousex|iAccordion|fontFamily|togglehor|fontUnit|filteredPo
 sition|paddingTopUnit|parte|itemZIndex||selRange2|finish|paddingLeftUnit|moveStart|paddingRightUnit|xml|itransferTo|borderLeftUnit|borderTopUnit||update|BlindUp||borderRightUnit|checkCache|getSelectionStart|borderBottomUnit|tooltipTitle|easeout|expanderHelper|fontStyle|fontStretch|containerMaxx|yfrac|topUnit|containerMaxy|clickItem|BlindDown|off|inputValue|fracH|fontVariant|rgba|maxy|maxx|keypress|fracW|xfrac|horizontal|addColorStop|htmlEntities|vertical|dragmoveByKey|autocompleteIframe|onslide|fold|parts|SlideInUp|getContext|protect|autocompleteHelper|olive|orange|pink|white|maroon|navy|magenta|203|193|rotationTimer|lightpink||red|lightyellow|182|lime||purple|silver|Top|||inset|outset|SlideOutRight|SlideInRight|ridge|groove|dashed|solid|double|SlideToggleLeft|SlideOutLeft|SlideOutDown|SlideInDown|SlideToggleUp|scrollTo|selectorText|rules|borderStyle|SlideInLeft|SlideToggleDown|dotted|SlideToggleRight|textIndent|borderBottomColor|borderLeftColor|borderRightColor|outlineWidth
 |outlineOffset|TransferTo|transferHelper|lineHeight|borderTopColor|outlineColor|hover|Accordion|isNaN|Carousel|stopAll|||Right|Bottom|Left|yellow|215|option|frameset|optgroup|meta|substr|frame|script|col|colgroup||th|header|removeChild|float|ol|finishx|fxWrapper|starty|table|form|w_|input|textarea|button|tfoot|thead|pageX|drawImage|clientX|pageY|clientY|globalCompositeOperation|destination|DisableTabs|createLinearGradient|fillStyle|EnableTabs|scale|nextSibling|prototype|tr|td|tbody|AlphaImageLoader|fixPNG|purgeEvents|translate|centerEl|save|cssFloat|startx|fuchsia|148|gold|green|indigo|darkviolet||122||204||darkred|darksalmon|233|130|khaki||lightcyan|lightgreen|238|fillRect||fill|216|appVersion||WebKit|lightblue|173|153|darkorchid|black|220|blue|brown|cyan|beige|azure|finishOpacity|appendChild|substring|aqua|darkblue|darkcyan|darkmagenta|darkolivegreen|navigator|darkorange|183|189|darkgrey|flipv|darkgreen|darkkhaki|lightgrey|amp|BlindToggleHorizontally|BlindRight|BlindLeft|R
 esizableDestroy|Resizable|120|lineHeigt|collapse|BlindToggleVertically|moveEnd|elasticin|bounceboth|984375|elasticout|elasticboth|duplicate|ImageBoxClose|DropOutDown|DropInDown|load|DropToggleRight|DropInRight|Fold|UnFold|Shrink|Grow|FoldToggle|DropOutRight|DropToggleLeft|DropInUp|DropOutUp|DropToggleDown|DropToggleUp|DropOutLeft|DropInLeft|captiontext|625|9375|Fisheye|30001|list|loading|fix|imageLoaded|childNodes|Showing|onchange|30002|SortSerialize|Autocomplete|200|SortableDestroy|Sortable|resize|wh|firstResize|Slider|bmp|100000|jpeg|Selectable|ToolTip|easeboth|easein|nodeValue|http|first|before|last|112|SliderSetValues|110|SliderGetValues|array|Bounce|Autoexpand|onselectstart|CloseVertically|mozUserSelect|fromHandler|ondragstart|MozUserSelect|number|pW|toUpperCase|khtml|find|CloseHorizontally|SwitchHorizontally|ScrollToAnchors|Puff|slideshowLink|password|quot|OpenHorizontally|OpenVertically|SwitchVertically|IMG|lt|alt|par|moz|success|POST|recallDroppables|param|pt|locatio
 n|Highlight|100000000|ajax|ondrop|name'.split('|'),0,{}))

Added: zc.async/trunk/htmldocs/1.5.0/_static/jquery.js
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_static/jquery.js	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_static/jquery.js	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,32 @@
+/*
+ * jQuery 1.2.3 - New Wave Javascript
+ *
+ * Copyright (c) 2008 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2008-02-06 00:21:25 -0500 (Wed, 06 Feb 2008) $
+ * $Rev: 4663 $
+ */
+(function(){if(window.jQuery)var _jQuery=window.jQuery;var jQuery=window.jQuery=function(selector,context){return new jQuery.prototype.init(selector,context);};if(window.$)var _$=window.$;window.$=jQuery;var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/;var isSimple=/^.[^:#\[\.]*$/;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}else if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem)if(elem.id!=match[3])return jQuery().find(selector);else{this[0]=elem;this.length=1;return this;}else
+selector=[];}}else
+return new jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return new jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(selector.constructor==Array&&selector||(selector.jquery||selector.length&&selector!=window&&!selector.nodeType&&selector[0]!=undefined&&selector[0].nodeType)&&jQuery.makeArray(selector)||[selector]);},jquery:"1.2.3",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;this.each(function(i){if(this==elem)ret=i;});return ret;},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value==undefined)return this.length&&jQuery[type||"attr"](this[0],name
 )||undefined;else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return t
 his.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createEleme
 nt("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else
+return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else
+selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return!selector?this:this.pushStack(jQuery.merge(this.get(),selector.constructor==String?jQuery(selector).get():selector.length!=undefined&&(!selector.nodeName||jQuery.nodeName(selector,"form"))?selector:[selector]));},is:function(selector){return selector?jQuery.multiFilter(selector,this).length>0:false;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.a
 ttributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else
+return(this[0].value||"").replace(/\r/g,"");}return undefined;}return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=value.constructor==Array?value:[value];jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else
+this.value=value;});},html:function(value){return value==undefined?(this.length?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value==null){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data==undefined&&this.length)data=jQuery.data(this[0],key);return data==null&&parts[1]?this.data(parts[0]):data;}else
+return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script")){scripts=scripts.add(elem);}else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.prototype.init.prototype=jQuery.prototype;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,asyn
 c:false,dataType:"script"});else
+jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==1){target=this;i=0;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){if(target===options[name])continue;if(deep&&options[name]&&typeof options[name]=="object"&&target[name]&&!options[name].nodeType)target[name]=jQuery.extend(target[name],options[name]);else if(options[name]!=undefined)target[name]=options[name];}return target;};var expando="jQuery"+(new Date()).getTime(),uuid=0,windowData={};var exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i;jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(
 fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/function/i.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else
+script.appendChild(document.createTextNode(data));head.appendChild(script);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!=undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){if(args){if(object.length==undefined){for(var name in object)if(callback.apply(object[name],args)===false)break;}else
+for(var i=0,length=object.length;i<length;i++)if(callback.apply(object[i],args)===false)break;}else{if(object.length==undefined){for(var name in object)if(callback.call(object[name],name,object[name])===false)break;}else
+for(var i=0,length=object.length,value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.st
 yle[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else
+jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret;function color(elem){if(!jQuery.browser.safari)return false;var ret=document.defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(elem.style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=elem.style.outline;elem.style.outline="0 solid black";elem.style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&elem.style&&elem.style[name])ret=elem.style[name];else if(document.defaultView&&document.defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var getComputedStyle=document.defaultView.getComputedStyle(elem,null);if(getComputedStyle&&!color(elem))ret=getComputedStyle.getPropertyValue(name);else{var swap=[],stack=[];for(var a=elem;a&&color(a)
 ;a=a.parentNode)stack.unshift(a);for(var i=0;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(getComputedStyle&&getComputedStyle.getPropertyValue(name))||"";for(var i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var style=elem.style.left,runtimeStyle=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;elem.style.left=ret||0;ret=elem.style.pixelLeft+"px";elem.style.left=style;elem.runtimeStyle.left=runtimeStyle;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]
 &&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem=elem.toString();if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery
 .browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else
+ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var fix=jQuery.isXMLDoc(elem)?{}:jQuery.props;if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(fix[name]){if(value!=undefined)elem[fix[name]]=value;return elem[fix[name]];}else if(jQuery.browser.msie&&name=="style")return jQuery.attr(elem.style,"cssText",value);else if(value==undefined&&jQuery.browser.msie&&jQuery.nodeName(elem,"form")&&(name=="action"||name=="method"))return elem.getAttributeNode(name).nodeValue;else if(elem.tagName){if(value!=undefined){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem.setAttribute(name,""+value);}if(jQuery.browser.msie&&/href|src/.test(name)&&!jQuery.isXMLDoc(elem))return elem.getAttribute(name,2);return elem.getAttribute(name);}else{if(name=="opacity"&&jQuery.browser.msie){if(value!=undefined){elem.zoom=1;elem.filter=(ele
 m.filter||"").replace(/alpha\([^)]*\)/,"")+(parseFloat(value).toString()=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100).toString():"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(value!=undefined)elem[name]=value;return elem[name];}},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(typeof array!="array")for(var i=0,length=array.length;i<length;i++)ret.push(array[i]);else
+ret=array.slice(0);return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]==elem)return i;return-1;},merge:function(first,second){if(jQuery.browser.msie){for(var i=0;second[i];i++)if(second[i].nodeType!=8)first.push(second[i]);}else
+for(var i=0;second[i];i++)first.push(second[i]);return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv&&callback(elems[i],i)||inv&&!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!==null&&value!=undefined){if(value.constructor!=Array)value=[value];ret=ret.concat(value);}}return ret;}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.tes
 t(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,innerHTML:"innerHTML",className:"className",value:"value",disabled:"disabled",checked:"checked",readonly:"readOnly",selected:"selected",maxlength:"maxLength",selectedIndex:"selectedIndex",defaultValue:"defaultValue",tagName:"tagName",nodeName:"nodeName"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},siblings:function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},children:funct
 ion(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(cla
 ssNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+nam
 e],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQue
 ry.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return
 "submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeNa
 me;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false;var re=quickChild;var m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.e
 xec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";
 var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"
 2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[];var cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==resu
 lt)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&(!elem||n!=elem))r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval!=undefined)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=function(){return fn.apply(this,arguments);};handler.data=data;handler.guid=fn.guid;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){var val;if(typeof jQuery=="undefined"||jQuery.event.triggered)return val;val=jQuery.event.handle.apply(arguments.callee.elem,arguments);return val;});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||
 jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else
+for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data||[]);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDe
 fault;if(event)data.unshift(this.fix({type:type,target:elem}));data[0].type=type;if(exclusive)data[0].exclusive=true;if(jQuery.isFunction(jQuery.data(elem,"handle")))val=jQuery.data(elem,"handle").apply(elem,data);if(!fn&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val;event=jQuery.event.fix(event||window.event||{});var parts=event.type.split(".");event.type=parts[0];var handlers=jQuery.data(this,"events")&&jQuery.data(this,"events")[event.type],args=Array.prototype.slice.call(arguments,1);args.unshift(event);for(var j in handlers){var handler=handlers[j];args[0].handler=handler;args[0].data=handler.data;if(!parts[1]&&!even
 t.exclusive||handler.type==parts[1]){var ret=handler.apply(this,args);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}if(jQuery.browser.msie)event.target=event.preventDefault=event.stopPropagation=event.handler=event.data=null;return val;},fix:function(event){var originalEvent=event;event=jQuery.extend({},originalEvent);event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=originalEvent.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX
 =event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;arguments[0].type="mouseenter";return jQuery.event.handle.apply(this
 ,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;arguments[0].type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){return this.each(function(){jQuery.event.add(this,type,function(event){jQuery(this).unbind(event);return(fn||data).apply(this,arguments);},fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,tr
 ue,fn);});},triggerHandler:function(type,data,fn){if(this[0])return jQuery.event.trigger(type,data,this[0],false,fn);return undefined;},toggle:function(){var args=arguments;return this.click(function(event){this.lastToggle=0==this.lastToggle?1:0;event.preventDefault();return args[this.lastToggle].apply(this,arguments)||false;});},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else
+jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.apply(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser
 .safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({load:function(url,params,callback){if(jQue
 ry.isFunction(url))return this.bind("load",url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(th
 is.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=(new Date).getTime();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,sett
 ings);},ajaxSettings:{global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){var jsonp,jsre=/=\?(&|$)/g,status,data;s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(s.type.toLowerCase()=="get"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.re
 place(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&s.type.toLowerCase()=="get"){var ts=(new Date()).getTime();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&s.type.toLowerCase()=="get"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");if((!s.url.indexOf("http")||!s.url.indexOf("//"))&&s.dataType=="script"&&s.type.toLowerCase()=="get"){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.r
 eadyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xml=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();xml.open(s.type,s.url,s.async,s.username,s.password);try{if(s.data)xml.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xml.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xml.setRequestHeader("X-Requested-With","XMLHttpRequest");xml.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend)s.beforeSend(xml);if(s.global)jQuery.event.trigger("ajaxSend",[xml,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xml&&(xml.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpS
 uccess(xml)&&"error"||s.ifModified&&jQuery.httpNotModified(xml,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xml,s.dataType);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xml.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else
+jQuery.handleError(s,xml,status);complete();if(s.async)xml=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xml){xml.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xml.send(s.data);}catch(e){jQuery.handleError(s,xml,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xml,s]);}function complete(){if(s.complete)s.complete(xml,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xml,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xml;},handleError:function(s,xml,status,e){if(s.error)s.error(xml,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xml,s,e]);},active:0,httpSuccess:function(r){try{return!r.status&&location.protocol=="file:"||(r.status>=200&&r.status<300)||r.status==304||r.status==1223||jQuery.browser.safari&&r.status==undefined;}catch(e){}return false;},httpNo
 tModified:function(xml,url){try{var xmlRes=xml.getResponseHeader("Last-Modified");return xml.status==304||xmlRes==jQuery.lastModified[url]||jQuery.browser.safari&&xml.status==undefined;}catch(e){}return false;},httpData:function(r,type){var ct=r.getResponseHeader("content-type");var xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0;var data=xml?r.responseXML:r.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else
+for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else
+s.push(encodeURIComponent(j)+"="+encodeURIComponent(a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle(fn,fn2):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(fun
 ction(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall);var hidden=jQuery(this).is(":hidden"),self=this;for(var p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return jQuery.isFunction(opt.complete)&&opt.complet
 e.apply(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else
+e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.apply(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(!elem)return undefined;type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",array?jQuery.makeArray(array):[]);return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].apply(this);});};jQuery.ex
 tend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:{slow:600,fast:200}[opt.duration])||400;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.apply(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.apply(this.elem,[this.now,this]);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.
 prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=(new Date()).getTime();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this
 .elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=(new Date()).getTime();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done&&jQuery.isFunction(this.options.complete))this.options.complete.apply(this.elem);return false;}else{var n=t-this.st
 artTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.fx.step={scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}};jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),fixed=jQuery.css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTo
 p,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&jQuery.css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(jQuery.css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&jQuery.css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||jQuery.css(offsetChild,"position")=="absolute"))||(mozilla&&jQuery.css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollL
 eft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l)||0;top+=parseInt(t)||0;}return results;};})();
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/_static/minus.png
===================================================================
(Binary files differ)


Property changes on: zc.async/trunk/htmldocs/1.5.0/_static/minus.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: zc.async/trunk/htmldocs/1.5.0/_static/navigation.png
===================================================================
(Binary files differ)


Property changes on: zc.async/trunk/htmldocs/1.5.0/_static/navigation.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: zc.async/trunk/htmldocs/1.5.0/_static/plus.png
===================================================================
(Binary files differ)


Property changes on: zc.async/trunk/htmldocs/1.5.0/_static/plus.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: zc.async/trunk/htmldocs/1.5.0/_static/pygments.css
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_static/pygments.css	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_static/pygments.css	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,61 @@
+.hll { background-color: #ffffcc }
+.c { color: #408090; font-style: italic } /* Comment */
+.err { border: 1px solid #FF0000 } /* Error */
+.k { color: #007020; font-weight: bold } /* Keyword */
+.o { color: #666666 } /* Operator */
+.cm { color: #408090; font-style: italic } /* Comment.Multiline */
+.cp { color: #007020 } /* Comment.Preproc */
+.c1 { color: #408090; font-style: italic } /* Comment.Single */
+.cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
+.gd { color: #A00000 } /* Generic.Deleted */
+.ge { font-style: italic } /* Generic.Emph */
+.gr { color: #FF0000 } /* Generic.Error */
+.gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.gi { color: #00A000 } /* Generic.Inserted */
+.go { color: #303030 } /* Generic.Output */
+.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
+.gs { font-weight: bold } /* Generic.Strong */
+.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.gt { color: #0040D0 } /* Generic.Traceback */
+.kc { color: #007020; font-weight: bold } /* Keyword.Constant */
+.kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
+.kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
+.kp { color: #007020 } /* Keyword.Pseudo */
+.kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
+.kt { color: #902000 } /* Keyword.Type */
+.m { color: #208050 } /* Literal.Number */
+.s { color: #4070a0 } /* Literal.String */
+.na { color: #4070a0 } /* Name.Attribute */
+.nb { color: #007020 } /* Name.Builtin */
+.nc { color: #0e84b5; font-weight: bold } /* Name.Class */
+.no { color: #60add5 } /* Name.Constant */
+.nd { color: #555555; font-weight: bold } /* Name.Decorator */
+.ni { color: #d55537; font-weight: bold } /* Name.Entity */
+.ne { color: #007020 } /* Name.Exception */
+.nf { color: #06287e } /* Name.Function */
+.nl { color: #002070; font-weight: bold } /* Name.Label */
+.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
+.nt { color: #062873; font-weight: bold } /* Name.Tag */
+.nv { color: #bb60d5 } /* Name.Variable */
+.ow { color: #007020; font-weight: bold } /* Operator.Word */
+.w { color: #bbbbbb } /* Text.Whitespace */
+.mf { color: #208050 } /* Literal.Number.Float */
+.mh { color: #208050 } /* Literal.Number.Hex */
+.mi { color: #208050 } /* Literal.Number.Integer */
+.mo { color: #208050 } /* Literal.Number.Oct */
+.sb { color: #4070a0 } /* Literal.String.Backtick */
+.sc { color: #4070a0 } /* Literal.String.Char */
+.sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
+.s2 { color: #4070a0 } /* Literal.String.Double */
+.se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
+.sh { color: #4070a0 } /* Literal.String.Heredoc */
+.si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
+.sx { color: #c65d09 } /* Literal.String.Other */
+.sr { color: #235388 } /* Literal.String.Regex */
+.s1 { color: #4070a0 } /* Literal.String.Single */
+.ss { color: #517918 } /* Literal.String.Symbol */
+.bp { color: #007020 } /* Name.Builtin.Pseudo */
+.vc { color: #bb60d5 } /* Name.Variable.Class */
+.vg { color: #bb60d5 } /* Name.Variable.Global */
+.vi { color: #bb60d5 } /* Name.Variable.Instance */
+.il { color: #208050 } /* Literal.Number.Integer.Long */
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/_static/rightsidebar.css
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_static/rightsidebar.css	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_static/rightsidebar.css	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,16 @@
+/**
+ * Sphinx Doc Design -- Right Side Bar Overrides
+ */
+
+
+div.sphinxsidebar {
+    float: right;
+}
+
+div.bodywrapper {
+    margin: 0 230px 0 0;
+}
+
+div.inlinecomments {
+    right: 250px;
+}

Added: zc.async/trunk/htmldocs/1.5.0/_static/searchtools.js
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_static/searchtools.js	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_static/searchtools.js	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,404 @@
+/**
+ * helper function to return a node containing the
+ * search summary for a given text. keywords is a list
+ * of stemmed words, hlwords is the list of normal, unstemmed
+ * words. the first one is used to find the occurance, the
+ * latter for highlighting it.
+ */
+jQuery.makeSearchSummary = function(text, keywords, hlwords) {
+    var textLower = text.toLowerCase();
+    var start = 0;
+    $.each(keywords, function() {
+            var i = textLower.indexOf(this.toLowerCase());
+            if (i > -1) {
+                start = i;
+            }
+        });
+    start = Math.max(start - 120, 0);
+    var excerpt = ((start > 0) ? '...' : '') +
+    $.trim(text.substr(start, 240)) +
+    ((start + 240 - text.length) ? '...' : '');
+    var rv = $('<div class="context"></div>').text(excerpt);
+    $.each(hlwords, function() {
+            rv = rv.highlightText(this, 'highlight');
+        });
+    return rv;
+}
+
+/**
+ * Porter Stemmer
+ */
+var PorterStemmer = function() {
+
+    var step2list = {
+        ational: 'ate',
+        tional: 'tion',
+        enci: 'ence',
+        anci: 'ance',
+        izer: 'ize',
+        bli: 'ble',
+        alli: 'al',
+        entli: 'ent',
+        eli: 'e',
+        ousli: 'ous',
+        ization: 'ize',
+        ation: 'ate',
+        ator: 'ate',
+        alism: 'al',
+        iveness: 'ive',
+        fulness: 'ful',
+        ousness: 'ous',
+        aliti: 'al',
+        iviti: 'ive',
+        biliti: 'ble',
+        logi: 'log'
+    };
+
+    var step3list = {
+        icate: 'ic',
+        ative: '',
+        alize: 'al',
+        iciti: 'ic',
+        ical: 'ic',
+        ful: '',
+        ness: ''
+    };
+
+    var c = "[^aeiou]";          // consonant
+    var v = "[aeiouy]";          // vowel
+    var C = c + "[^aeiouy]*";    // consonant sequence
+    var V = v + "[aeiou]*";      // vowel sequence
+
+    var mgr0 = "^(" + C + ")?" + V + C;                      // [C]VC... is m>0
+    var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$";    // [C]VC[V] is m=1
+    var mgr1 = "^(" + C + ")?" + V + C + V + C;              // [C]VCVC... is m>1
+    var s_v   = "^(" + C + ")?" + v;                        // vowel in stem
+
+    this.stemWord = function (w) {
+        var stem;
+        var suffix;
+        var firstch;
+        var origword = w;
+
+        if (w.length < 3) {
+            return w;
+        }
+
+        var re;
+        var re2;
+        var re3;
+        var re4;
+
+        firstch = w.substr(0,1);
+        if (firstch == "y") {
+            w = firstch.toUpperCase() + w.substr(1);
+        }
+
+        // Step 1a
+        re = /^(.+?)(ss|i)es$/;
+        re2 = /^(.+?)([^s])s$/;
+
+        if (re.test(w)) {
+            w = w.replace(re,"$1$2");
+        }
+        else if (re2.test(w)) {
+            w = w.replace(re2,"$1$2");
+        }
+
+        // Step 1b
+        re = /^(.+?)eed$/;
+        re2 = /^(.+?)(ed|ing)$/;
+        if (re.test(w)) {
+            var fp = re.exec(w);
+            re = new RegExp(mgr0);
+            if (re.test(fp[1])) {
+                re = /.$/;
+                w = w.replace(re,"");
+            }
+        }
+        else if (re2.test(w)) {
+            var fp = re2.exec(w);
+            stem = fp[1];
+            re2 = new RegExp(s_v);
+            if (re2.test(stem)) {
+                w = stem;
+                re2 = /(at|bl|iz)$/;
+                re3 = new RegExp("([^aeiouylsz])\\1$");
+                re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
+                if (re2.test(w)) {
+                    w = w + "e";
+                }
+                else if (re3.test(w)) {
+                    re = /.$/; w = w.replace(re,"");
+                }
+                else if (re4.test(w)) {
+                    w = w + "e";
+                }
+            }
+        }
+
+        // Step 1c
+        re = /^(.+?)y$/;
+        if (re.test(w)) {
+            var fp = re.exec(w);
+            stem = fp[1];
+            re = new RegExp(s_v);
+            if (re.test(stem)) { w = stem + "i"; }
+        }
+
+        // Step 2
+        re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
+        if (re.test(w)) {
+            var fp = re.exec(w);
+            stem = fp[1];
+            suffix = fp[2];
+            re = new RegExp(mgr0);
+            if (re.test(stem)) {
+                w = stem + step2list[suffix];
+            }
+        }
+
+        // Step 3
+        re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
+        if (re.test(w)) {
+            var fp = re.exec(w);
+            stem = fp[1];
+            suffix = fp[2];
+            re = new RegExp(mgr0);
+            if (re.test(stem)) {
+                w = stem + step3list[suffix];
+            }
+        }
+
+        // Step 4
+        re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
+        re2 = /^(.+?)(s|t)(ion)$/;
+        if (re.test(w)) {
+            var fp = re.exec(w);
+            stem = fp[1];
+            re = new RegExp(mgr1);
+            if (re.test(stem)) {
+                w = stem;
+            }
+        }
+        else if (re2.test(w)) {
+            var fp = re2.exec(w);
+            stem = fp[1] + fp[2];
+            re2 = new RegExp(mgr1);
+            if (re2.test(stem)) {
+                w = stem;
+            }
+        }
+
+        // Step 5
+        re = /^(.+?)e$/;
+        if (re.test(w)) {
+            var fp = re.exec(w);
+            stem = fp[1];
+            re = new RegExp(mgr1);
+            re2 = new RegExp(meq1);
+            re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
+            if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {
+                w = stem;
+            }
+        }
+        re = /ll$/;
+        re2 = new RegExp(mgr1);
+        if (re.test(w) && re2.test(w)) {
+            re = /.$/;
+            w = w.replace(re,"");
+        }
+
+        // and turn initial Y back to y
+        if (firstch == "y") {
+            w = firstch.toLowerCase() + w.substr(1);
+        }
+        return w;
+    }
+}
+
+
+
+/**
+ * Search Module
+ */
+var Search = {
+
+    init : function() {
+        var params = $.getQueryParameters();
+        if (params.q) {
+            var query = params.q[0];
+            $('input[@name="q"]')[0].value = query;
+            this.performSearch(query);
+        }
+    },
+
+    /**
+     * perform a search for something
+     */
+    performSearch : function(query) {
+        // create the required interface elements
+        var out = $('#search-results');
+        var title = $('<h2>Searching</h2>').appendTo(out);
+        var dots = $('<span></span>').appendTo(title);
+        var status = $('<p style="display: none"></p>').appendTo(out);
+        var output = $('<ul class="search"/>').appendTo(out);
+
+        // spawn a background runner for updating the dots
+        // until the search has finished
+        var pulseStatus = 0;
+        function pulse() {
+            pulseStatus = (pulseStatus + 1) % 4;
+            var dotString = '';
+            for (var i = 0; i < pulseStatus; i++) {
+                dotString += '.';
+            }
+            dots.text(dotString);
+            if (pulseStatus > -1) {
+                window.setTimeout(pulse, 500);
+            }
+        };
+        pulse();
+
+        // stem the searchwords and add them to the
+        // correct list
+        var stemmer = new PorterStemmer();
+        var searchwords = [];
+        var excluded = [];
+        var hlwords = [];
+        var tmp = query.split(/\s+/);
+        for (var i = 0; i < tmp.length; i++) {
+            // stem the word
+            var word = stemmer.stemWord(tmp[i]).toLowerCase();
+            // select the correct list
+            if (word[0] == '-') {
+                var toAppend = excluded;
+                word = word.substr(1);
+            }
+            else {
+                var toAppend = searchwords;
+                hlwords.push(tmp[i].toLowerCase());
+            }
+            // only add if not already in the list
+            if (!$.contains(toAppend, word)) {
+                toAppend.push(word);
+            }
+        };
+        var highlightstring = '?highlight=' + $.urlencode(hlwords.join(" "));
+
+        console.debug('SEARCH: searching for:');
+        console.info('required: ', searchwords);
+        console.info('excluded: ', excluded);
+
+        // fetch searchindex and perform search
+        $.getJSON('searchindex.json', function(data) {
+
+                // prepare search
+                var filenames = data[0];
+                var titles = data[1]
+                var words = data[2];
+                var fileMap = {};
+                var files = null;
+
+                // perform the search on the required words
+                for (var i = 0; i < searchwords.length; i++) {
+                    var word = searchwords[i];
+                    // no match but word was a required one
+                    if ((files = words[word]) == null) {
+                        break;
+                    }
+                    // create the mapping
+                    for (var j = 0; j < files.length; j++) {
+                        var file = files[j];
+                        if (file in fileMap) {
+                            fileMap[file].push(word);
+                        }
+                        else {
+                            fileMap[file] = [word];
+                        }
+                    }
+                }
+
+                // now check if the files are in the correct
+                // areas and if the don't contain excluded words
+                var results = [];
+                for (var file in fileMap) {
+                    var valid = true;
+
+                    // check if all requirements are matched
+                    if (fileMap[file].length != searchwords.length) {
+                        continue;
+                    }
+                    // ensure that none of the excluded words is in the
+                    // search result.
+                    for (var i = 0; i < excluded.length; i++) {
+                        if ($.contains(words[excluded[i]] || [], file)) {
+                            valid = false;
+                            break;
+                        }
+                    }
+
+                    // if we have still a valid result we can add it
+                    // to the result list
+                    if (valid) {
+                        results.push([filenames[file], titles[file]]);
+                    }
+                }
+
+                // delete unused variables in order to not waste
+                // memory until list is retrieved completely
+                delete filenames, titles, words, data;
+
+                // now sort the results by title
+                results.sort(function(a, b) {
+                        var left = a[1].toLowerCase();
+                        var right = b[1].toLowerCase();
+                        return (left > right) ? -1 : ((left < right) ? 1 : 0);
+                    });
+
+                // print the results
+                var resultCount = results.length;
+                function displayNextItem() {
+                    // results left, load the summary and display it
+                    if (results.length) {
+                        var item = results.pop();
+                        var listItem = $('<li style="display:none"></li>');
+                        listItem.append($('<a/>').attr(
+                            'href',
+                            item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
+                            highlightstring).html(item[1]));
+                        $.get('_sources/' + item[0] + '.txt', function(data) {
+                                listItem.append($.makeSearchSummary(data, searchwords, hlwords));
+                                output.append(listItem);
+                                listItem.slideDown(10, function() {
+                                        displayNextItem();
+                                    });
+                            });
+                    }
+                    // search finished, update title and status message
+                    else {
+                        pulseStatus = -1;
+                        title.text('Search Results');
+                        if (!resultCount) {
+                            status.text('Your search did not match any documents. ' +
+                                        'Please make sure that all words are spelled ' +
+                                        'correctly and that you\'ve selected enough ' +
+                                        'categories.');
+                        }
+                        else {
+                            status.text('Search finished, found ' + resultCount +
+                                        ' page' + (resultCount != 1 ? 's' : '') +
+                                        ' matching the search query.');
+                        }
+                        status.fadeIn(500);
+                    }
+                }
+                displayNextItem();
+            });
+    }
+
+}
+
+$(document).ready(function() {
+        Search.init();
+    });

Added: zc.async/trunk/htmldocs/1.5.0/_static/sphinxdoc.css
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_static/sphinxdoc.css	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_static/sphinxdoc.css	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,504 @@
+/**
+ * Alternate Sphinx design
+ * Originally created by Armin Ronacher for Werkzeug, adapted by Georg Brandl.
+ */
+
+body {
+    font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
+    font-size: 14px;
+    letter-spacing: -0.01em;
+    line-height: 150%;
+    text-align: center;
+    /*background-color: #AFC1C4; */
+    background-color: #BFD1D4;
+    color: black;
+    padding: 0;
+    border: 1px solid #aaa;
+
+    margin: 0px 80px 0px 80px;
+    min-width: 740px;
+}
+
+a {
+    color: #CA7900;
+    text-decoration: none;
+}
+
+a:hover {
+    color: #2491CF;
+}
+
+pre {
+    font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
+    font-size: 0.95em;
+    letter-spacing: 0.015em;
+    padding: 0.5em;
+    border: 1px solid #ccc;
+    background-color: #f8f8f8;
+}
+
+td.linenos pre {
+    padding: 0.5em 0;
+    border: 0;
+    background-color: transparent;
+    color: #aaa;
+}
+
+table.highlighttable {
+    margin-left: 0.5em;
+}
+
+table.highlighttable td {
+    padding: 0 0.5em 0 0.5em;
+}
+
+cite, code, tt {
+    font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
+    font-size: 0.95em;
+    letter-spacing: 0.01em;
+}
+
+hr {
+    border: 1px solid #abc;
+    margin: 2em;
+}
+
+tt {
+    background-color: #f2f2f2;
+    border-bottom: 1px solid #ddd;
+    color: #333;
+}
+
+tt.descname {
+    background-color: transparent;
+    font-weight: bold;
+    font-size: 1.2em;
+    border: 0;
+}
+
+tt.descclassname {
+    background-color: transparent;
+    border: 0;
+}
+
+tt.xref {
+    background-color: transparent;
+    font-weight: bold;
+    border: 0;
+}
+
+a tt {
+    background-color: transparent;
+    font-weight: bold;
+    border: 0;
+    color: #CA7900;
+}
+
+a tt:hover {
+    color: #2491CF;
+}
+
+.field-list ul {
+    margin: 0;
+    padding-left: 1em;
+}
+
+.field-list p {
+    margin: 0;
+}
+
+dl {
+    margin-bottom: 15px;
+}
+
+dd p {
+    margin-top: 0px;
+}
+
+dd ul, dd table {
+    margin-bottom: 10px;
+}
+
+dd {
+    margin-top: 3px;
+    margin-bottom: 10px;
+    margin-left: 30px;
+}
+
+.refcount {
+    color: #060;
+}
+
+dt:target,
+.highlight {
+    background-color: #fbe54e;
+}
+
+dl.glossary dt {
+    font-weight: bold;
+    font-size: 1.1em;
+}
+
+pre {
+    line-height: 120%;
+}
+
+pre a {
+    color: inherit;
+    text-decoration: underline;
+}
+
+.first {
+    margin-top: 0 !important;
+}
+
+div.document {
+    background-color: white;
+    text-align: left;
+    background-image: url(contents.png);
+    background-repeat: repeat-x;
+}
+
+/*
+div.documentwrapper {
+    width: 100%;
+}
+*/
+
+div.clearer {
+    clear: both;
+}
+
+div.related h3 {
+    display: none;
+}
+
+div.related ul {
+    background-image: url(navigation.png);
+    height: 2em;
+    list-style: none;
+    border-top: 1px solid #ddd;
+    border-bottom: 1px solid #ddd;
+    margin: 0;
+    padding-left: 10px;
+}
+
+div.related ul li {
+    margin: 0;
+    padding: 0;
+    height: 2em;
+    float: left;
+}
+
+div.related ul li.right {
+    float: right;
+    margin-right: 5px;
+}
+
+div.related ul li a {
+    margin: 0;
+    padding: 0 5px 0 5px;
+    line-height: 1.75em;
+    color: #EE9816;
+}
+
+div.related ul li a:hover {
+    color: #3CA8E7;
+}
+
+div.body {
+    margin: 0;
+    padding: 0.5em 20px 20px 20px;
+}
+
+div.bodywrapper {
+    margin: 0 240px 0 0;
+    border-right: 1px solid #ccc;
+}
+
+div.body a {
+    text-decoration: underline;
+}
+
+div.sphinxsidebar {
+    margin: 0;
+    padding: 0.5em 15px 15px 0;
+    width: 210px;
+    float: right;
+    text-align: left;
+/*    margin-left: -100%; */
+}
+
+div.sphinxsidebar h4, div.sphinxsidebar h3 {
+    margin: 1em 0 0.5em 0;
+    font-size: 0.9em;
+    padding: 0.1em 0 0.1em 0.5em;
+    color: white;
+    border: 1px solid #86989B;
+    background-color: #AFC1C4;
+}
+
+div.sphinxsidebar ul {
+    padding-left: 1.5em;
+    margin-top: 7px;
+    list-style: none;
+    padding: 0;
+    line-height: 130%;
+}
+
+div.sphinxsidebar ul ul {
+    list-style: square;
+    margin-left: 20px;
+}
+
+p {
+    margin: 0.8em 0 0.5em 0;
+}
+
+p.rubric {
+    font-weight: bold;
+}
+
+h1 {
+    margin: 0;
+    padding: 0.7em 0 0.3em 0;
+    font-size: 1.5em;
+    color: #11557C;
+}
+
+h2 {
+    margin: 1.3em 0 0.2em 0;
+    font-size: 1.35em;
+    padding: 0;
+}
+
+h3 {
+    margin: 1em 0 -0.3em 0;
+    font-size: 1.2em;
+}
+
+h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+    color: black!important;
+}
+
+h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
+    display: none;
+    margin: 0 0 0 0.3em;
+    padding: 0 0.2em 0 0.2em;
+    color: #aaa!important;
+}
+
+h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
+h5:hover a.anchor, h6:hover a.anchor {
+    display: inline;
+}
+
+h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
+h5 a.anchor:hover, h6 a.anchor:hover {
+    color: #777;
+    background-color: #eee;
+}
+
+table {
+    border-collapse: collapse;
+    margin: 0 -0.5em 0 -0.5em;
+}
+
+table td, table th {
+    padding: 0.2em 0.5em 0.2em 0.5em;
+}
+
+div.footer {
+    background-color: #E3EFF1;
+    color: #86989B;
+    padding: 3px 8px 3px 0;
+    clear: both;
+    font-size: 0.8em;
+    text-align: right;
+}
+
+div.footer a {
+    color: #86989B;
+    text-decoration: underline;
+}
+
+div.pagination {
+    margin-top: 2em;
+    padding-top: 0.5em;
+    border-top: 1px solid black;
+    text-align: center;
+}
+
+div.sphinxsidebar ul.toc {
+    margin: 1em 0 1em 0;
+    padding: 0 0 0 0.5em;
+    list-style: none;
+}
+
+div.sphinxsidebar ul.toc li {
+    margin: 0.5em 0 0.5em 0;
+    font-size: 0.9em;
+    line-height: 130%;
+}
+
+div.sphinxsidebar ul.toc li p {
+    margin: 0;
+    padding: 0;
+}
+
+div.sphinxsidebar ul.toc ul {
+    margin: 0.2em 0 0.2em 0;
+    padding: 0 0 0 1.8em;
+}
+
+div.sphinxsidebar ul.toc ul li {
+    padding: 0;
+}
+
+div.admonition, div.warning {
+    font-size: 0.9em;
+    margin: 1em 0 0 0;
+    border: 1px solid #86989B;
+    background-color: #f7f7f7;
+}
+
+div.admonition p, div.warning p {
+    margin: 0.5em 1em 0.5em 1em;
+    padding: 0;
+}
+
+div.admonition pre, div.warning pre {
+    margin: 0.4em 1em 0.4em 1em;
+}
+
+div.admonition p.admonition-title,
+div.warning p.admonition-title {
+    margin: 0;
+    padding: 0.1em 0 0.1em 0.5em;
+    color: white;
+    border-bottom: 1px solid #86989B;
+    font-weight: bold;
+    background-color: #AFC1C4;
+}
+
+div.warning {
+    border: 1px solid #940000;
+}
+
+div.warning p.admonition-title {
+    background-color: #CF0000;
+    border-bottom-color: #940000;
+}
+
+div.admonition ul, div.admonition ol,
+div.warning ul, div.warning ol {
+    margin: 0.1em 0.5em 0.5em 3em;
+    padding: 0;
+}
+
+div.versioninfo {
+    margin: 1em 0 0 0;
+    border: 1px solid #ccc;
+    background-color: #DDEAF0;
+    padding: 8px;
+    line-height: 1.3em;
+    font-size: 0.9em;
+}
+
+
+a.headerlink {
+    color: #c60f0f!important;
+    font-size: 1em;
+    margin-left: 6px;
+    padding: 0 4px 0 4px;
+    text-decoration: none!important;
+    visibility: hidden;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+    visibility: visible;
+}
+
+a.headerlink:hover {
+    background-color: #ccc;
+    color: white!important;
+}
+
+table.indextable td {
+    text-align: left;
+    vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+    height: 10px;
+}
+
+table.indextable tr.cap {
+    margin-top: 10px;
+    background-color: #f2f2f2;
+}
+
+img.toggler {
+    margin-right: 3px;
+    margin-top: 3px;
+    cursor: pointer;
+}
+
+form.pfform {
+    margin: 10px 0 20px 0;
+}
+
+table.contentstable {
+    width: 90%;
+}
+
+table.contentstable p.biglink {
+    line-height: 150%;
+}
+
+a.biglink {
+    font-size: 1.3em;
+}
+
+span.linkdescr {
+    font-style: italic;
+    padding-top: 5px;
+    font-size: 90%;
+}
+
+ul.search {
+    margin: 10px 0 0 20px;
+    padding: 0;
+}
+
+ul.search li {
+    padding: 5px 0 5px 20px;
+    background-image: url(file.png);
+    background-repeat: no-repeat;
+    background-position: 0 7px;
+}
+
+ul.search li a {
+    font-weight: bold;
+}
+
+ul.search li div.context {
+    color: #888;
+    margin: 2px 0 0 30px;
+    text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+    font-weight: bold;
+}

Added: zc.async/trunk/htmldocs/1.5.0/_static/stickysidebar.css
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_static/stickysidebar.css	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_static/stickysidebar.css	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,19 @@
+/**
+ * Sphinx Doc Design -- Sticky sidebar Overrides
+ */
+
+div.sphinxsidebar {
+    top: 30px;
+    left: 0px;
+    position: fixed;
+    margin: 0;
+    float: none;
+}
+
+div.related {
+    position: fixed;
+}
+
+div.documentwrapper {
+    margin-top: 30px;
+}

Added: zc.async/trunk/htmldocs/1.5.0/_static/traditional.css
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/_static/traditional.css	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/_static/traditional.css	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,700 @@
+/**
+ * Sphinx Doc Design -- traditional python.org style
+ */
+
+body {
+    color: #000;
+    margin: 0;
+    padding: 0;
+}
+
+/* :::: LAYOUT :::: */
+
+div.documentwrapper {
+    float: left;
+    width: 100%;
+}
+
+div.bodywrapper {
+    margin: 0 230px 0 0;
+}
+
+div.body {
+    background-color: white;
+    padding: 0 20px 30px 20px;
+}
+
+div.sphinxsidebarwrapper {
+    border: 1px solid #99ccff;
+    padding: 10px;
+    margin: 10px 15px 10px 0;
+}
+
+div.sphinxsidebar {
+    float: right;
+    margin-left: -100%;
+    width: 230px;
+}
+
+div.clearer {
+    clear: both;
+}
+
+div.footer {
+    clear: both;
+    width: 100%;
+    background-color: #99ccff;
+    padding: 9px 0 9px 0;
+    text-align: center;
+}
+
+div.related {
+    background-color: #99ccff;
+    color: #333;
+    width: 100%;
+    height: 30px;
+    line-height: 30px;
+    border-bottom: 5px solid white;
+}
+
+div.related h3 {
+    display: none;
+}
+
+div.related ul {
+    margin: 0;
+    padding: 0 0 0 10px;
+    list-style: none;
+}
+
+div.related li {
+    display: inline;
+    font-weight: bold;
+}
+
+div.related li.right {
+    float: right;
+    margin-right: 5px;
+}
+
+/* ::: SIDEBAR :::: */
+div.sphinxsidebar h3 {
+    margin: 0;
+}
+
+div.sphinxsidebar h4 {
+    margin: 5px 0 0 0;
+}
+
+div.sphinxsidebar p.topless {
+    margin: 5px 10px 10px 10px;
+}
+
+div.sphinxsidebar ul {
+    margin: 10px;
+    margin-left: 15px;
+    padding: 0;
+}
+
+div.sphinxsidebar ul ul {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+div.sphinxsidebar form {
+    margin-top: 10px;
+}
+
+
+/* :::: SEARCH :::: */
+ul.search {
+    margin: 10px 0 0 20px;
+    padding: 0;
+}
+
+ul.search li {
+    padding: 5px 0 5px 20px;
+    background-image: url(file.png);
+    background-repeat: no-repeat;
+    background-position: 0 7px;
+}
+
+ul.search li a {
+    font-weight: bold;
+}
+
+ul.search li div.context {
+    color: #888;
+    margin: 2px 0 0 30px;
+    text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+    font-weight: bold;
+}
+
+/* :::: COMMON FORM STYLES :::: */
+
+div.actions {
+    border-top: 1px solid #aaa;
+    background-color: #ddd;
+    margin: 10px 0 0 -20px;
+    padding: 5px 0 5px 20px;
+}
+
+form dl {
+    color: #333;
+}
+
+form dt {
+    clear: both;
+    float: left;
+    min-width: 110px;
+    margin-right: 10px;
+    padding-top: 2px;
+}
+
+input#homepage {
+    display: none;
+}
+
+div.error {
+    margin: 5px 20px 0 0;
+    padding: 5px;
+    border: 1px solid #d00;
+    /*border: 2px solid #05171e;
+    background-color: #092835;
+    color: white;*/
+    font-weight: bold;
+}
+
+/* :::: INLINE COMMENTS :::: */
+
+div.inlinecommentswrapper {
+    float: right;
+    max-width: 40%;
+}
+
+div.commentmarker {
+    float: right;
+    background-image: url(style/comment.png);
+    background-repeat: no-repeat;
+    width: 25px;
+    height: 25px;
+    text-align: center;
+    padding-top: 3px;
+}
+
+div.nocommentmarker {
+    float: right;
+    background-image: url(style/nocomment.png);
+    background-repeat: no-repeat;
+    width: 25px;
+    height: 25px;
+}
+
+div.inlinecomments {
+    margin-left: 10px;
+    margin-bottom: 5px;
+    background-color: #eee;
+    border: 1px solid #ccc;
+    padding: 5px;
+}
+
+div.inlinecomment {
+    border-top: 1px solid #ccc;
+    padding-top: 5px;
+    margin-top: 5px;
+}
+
+.inlinecomments p {
+    margin: 5px 0 5px 0;
+}
+
+.inlinecomments .head {
+    font-weight: bold;
+}
+
+.inlinecomments .meta {
+    font-style: italic;
+}
+
+
+/* :::: COMMENTS :::: */
+
+div#comments h3 {
+    border-top: 1px solid #aaa;
+    padding: 5px 20px 5px 20px;
+    margin: 20px -20px 20px -20px;
+    background-color: #ddd;
+}
+
+/*
+div#comments {
+    background-color: #ccc;
+    margin: 40px -20px -30px -20px;
+    padding: 0 0 1px 0;
+}
+
+div#comments h4 {
+    margin: 30px 0 20px 0;
+    background-color: #aaa;
+    border-bottom: 1px solid #09232e;
+    color: #333;
+}
+
+div#comments form {
+    display: block;
+    margin: 0 0 0 20px;
+}
+
+div#comments textarea {
+    width: 98%;
+    height: 160px;
+}
+
+div#comments div.help {
+    margin: 20px 20px 10px 0;
+    background-color: #ccc;
+    color: #333;
+}
+
+div#comments div.help p {
+    margin: 0;
+    padding: 0 0 10px 0;
+}
+
+div#comments input, div#comments textarea {
+    font-family: 'Bitstream Vera Sans', 'Arial', sans-serif;
+    font-size: 13px;
+    color: black;
+    background-color: #aaa;
+    border: 1px solid #092835;
+}
+
+div#comments input[type="reset"],
+div#comments input[type="submit"] {
+    cursor: pointer;
+    font-weight: bold;
+    padding: 2px;
+    margin: 5px 5px 5px 0;
+    background-color: #666;
+    color: white;
+}
+
+div#comments div.comment {
+    margin: 10px 10px 10px 20px;
+    padding: 10px;
+    border: 1px solid #0f3646;
+    background-color: #aaa;
+    color: #333;
+}
+
+div#comments div.comment p {
+    margin: 5px 0 5px 0;
+}
+
+div#comments div.comment p.meta {
+    font-style: italic;
+    color: #444;
+    text-align: right;
+    margin: -5px 0 -5px 0;
+}
+
+div#comments div.comment h4 {
+    margin: -10px -10px 5px -10px;
+    padding: 3px;
+    font-size: 15px;
+    background-color: #888;
+    color: white;
+    border: 0;
+}
+
+div#comments div.comment pre,
+div#comments div.comment tt {
+    background-color: #ddd;
+    color: #111;
+    border: none;
+}
+
+div#comments div.comment a {
+    color: #fff;
+    text-decoration: underline;
+}
+
+div#comments div.comment blockquote {
+    margin: 10px;
+    padding: 10px;
+    border-left: 1px solid #0f3646;
+    /*border: 1px solid #0f3646;
+    background-color: #071c25;*/
+}
+
+div#comments em.important {
+    color: #d00;
+    font-weight: bold;
+    font-style: normal;
+}*/
+
+/* :::: SUGGEST CHANGES :::: */
+div#suggest-changes-box input, div#suggest-changes-box textarea {
+    border: 1px solid #ccc;
+    background-color: white;
+    color: black;
+}
+
+div#suggest-changes-box textarea {
+    width: 99%;
+    height: 400px;
+}
+
+
+/* :::: PREVIEW :::: */
+div.preview {
+    background-image: url(style/preview.png);
+    padding: 0 20px 20px 20px;
+    margin-bottom: 30px;
+}
+
+
+/* :::: INDEX PAGE :::: */
+
+table.contentstable {
+    width: 90%;
+}
+
+table.contentstable p.biglink {
+    line-height: 150%;
+}
+
+a.biglink {
+    font-size: 1.5em;
+}
+
+span.linkdescr {
+    font-style: italic;
+    padding-top: 5px;
+}
+
+/* :::: GENINDEX STYLES :::: */
+
+table.indextable td {
+    text-align: left;
+    vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+    height: 10px;
+}
+
+table.indextable tr.cap {
+    margin-top: 10px;
+    background-color: #f2f2f2;
+}
+
+img.toggler {
+    margin-right: 3px;
+    margin-top: 3px;
+    cursor: pointer;
+}
+
+/* :::: GLOBAL STYLES :::: */
+
+p.subhead {
+    font-weight: bold;
+    margin-top: 20px;
+}
+
+a:link:active           { color: #ff0000; }
+a:link:hover            { background-color: #bbeeff; }
+a:visited:hover         { background-color: #bbeeff; }
+a:visited               { color: #551a8b; }
+a:link                  { color: #0000bb; }
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+    font-family: avantgarde, sans-serif;
+    font-weight: bold;
+}
+
+div.body h1 { font-size: 180%; }
+div.body h2 { font-size: 150%; }
+div.body h3 { font-size: 120%; }
+div.body h4 { font-size: 120%; }
+
+a.headerlink,
+a.headerlink,
+a.headerlink,
+a.headerlink,
+a.headerlink,
+a.headerlink {
+    color: #c60f0f;
+    font-size: 0.8em;
+    padding: 0 4px 0 4px;
+    text-decoration: none;
+    visibility: hidden;
+}
+
+*:hover > a.headerlink,
+*:hover > a.headerlink,
+*:hover > a.headerlink,
+*:hover > a.headerlink,
+*:hover > a.headerlink,
+*:hover > a.headerlink {
+    visibility: visible;
+}
+
+a.headerlink:hover,
+a.headerlink:hover,
+a.headerlink:hover,
+a.headerlink:hover,
+a.headerlink:hover,
+a.headerlink:hover {
+    background-color: #c60f0f;
+    color: white;
+}
+
+div.body p, div.body dd, div.body li {
+    text-align: justify;
+}
+
+div.body td {
+    text-align: left;
+}
+
+ul.fakelist {
+    list-style: none;
+    margin: 10px 0 10px 20px;
+    padding: 0;
+}
+
+/* "Footnotes" heading */
+p.rubric {
+    margin-top: 30px;
+    font-weight: bold;
+}
+
+/* "Topics" */
+
+div.topic {
+    background-color: #eee;
+    border: 1px solid #ccc;
+    padding: 0 7px 0 7px;
+    margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+    font-size: 1.1em;
+    font-weight: bold;
+    margin-top: 10px;
+}
+
+/* Admonitions */
+
+div.admonition {
+    margin-top: 10px;
+    margin-bottom: 10px;
+    padding: 7px;
+}
+
+div.admonition dt {
+    font-weight: bold;
+}
+
+div.admonition dd {
+    margin-bottom: 10px;
+}
+
+div.admonition dl {
+    margin-bottom: 0;
+}
+
+div.admonition p {
+    display: inline;
+}
+
+div.seealso {
+    background-color: #ffc;
+    border: 1px solid #ff6;
+}
+
+div.warning {
+    background-color: #ffe4e4;
+    border: 1px solid #f66;
+}
+
+div.note {
+    background-color: #eee;
+    border: 1px solid #ccc;
+}
+
+p.admonition-title {
+    margin: 0px 10px 5px 0px;
+    font-weight: bold;
+    display: inline;
+}
+
+p.admonition-title:after {
+    content: ":";
+}
+
+div.body p.centered {
+    text-align: center;
+    margin-top: 25px;
+}
+
+table.docutils {
+    border: 0;
+}
+
+table.docutils td, table.docutils th {
+    padding: 0 8px 2px 0;
+    border-top: 0;
+    border-left: 0;
+    border-right: 0;
+    border-bottom: 1px solid #aaa;
+}
+
+table.field-list td, table.field-list th {
+    border: 0 !important;
+}
+
+table.footnote td, table.footnote th {
+    border: 0 !important;
+}
+
+dl {
+    margin-bottom: 15px;
+    clear: both;
+}
+
+dd p {
+    margin-top: 0px;
+}
+
+dd ul, dd table {
+    margin-bottom: 10px;
+}
+
+dd {
+    margin-top: 3px;
+    margin-bottom: 10px;
+    margin-left: 30px;
+}
+
+dl.glossary dt {
+    font-weight: bold;
+    font-size: 1.1em;
+}
+
+.refcount {
+    color: #060;
+}
+
+th {
+    text-align: left;
+    padding-right: 5px;
+}
+
+pre {
+    font-family: monospace;
+    padding: 5px;
+    color: #00008b;
+    border-left: none;
+    border-right: none;
+}
+
+tt {
+    font-family: monospace;
+    background-color: #ecf0f3;
+    padding: 0 1px 0 1px;
+}
+
+tt.descname {
+    background-color: transparent;
+    font-weight: bold;
+    font-size: 1.2em;
+}
+
+tt.descclassname {
+    background-color: transparent;
+}
+
+tt.xref, a tt {
+    background-color: transparent;
+    font-weight: bold;
+}
+
+.footnote:target  { background-color: #ffa }
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+    background-color: transparent;
+}
+
+.optional {
+    font-size: 1.3em;
+}
+
+.versionmodified {
+    font-style: italic;
+}
+
+form.comment {
+    margin: 0;
+    padding: 10px 30px 10px 30px;
+    background-color: #eee;
+}
+
+form.comment h3 {
+    background-color: #326591;
+    color: white;
+    margin: -10px -30px 10px -30px;
+    padding: 5px;
+    font-size: 1.4em;
+}
+
+form.comment input,
+form.comment textarea {
+    border: 1px solid #ccc;
+    padding: 2px;
+    font-family: sans-serif;
+    font-size: 13px;
+}
+
+form.comment input[type="text"] {
+    width: 240px;
+}
+
+form.comment textarea {
+    width: 100%;
+    height: 200px;
+    margin-bottom: 10px;
+}
+
+/* :::: PRINT :::: */
+ at media print {
+    div.documentwrapper {
+        width: 100%;
+    }
+
+    div.body {
+        margin: 0;
+    }
+
+    div.sphinxsidebar,
+    div.related,
+    div.footer,
+    div#comments div.new-comment-box,
+    #top-link {
+        display: none;
+    }
+}

Added: zc.async/trunk/htmldocs/1.5.0/_static/zc_async.png
===================================================================
(Binary files differ)


Property changes on: zc.async/trunk/htmldocs/1.5.0/_static/zc_async.png
___________________________________________________________________
Name: svn:mime-type
   + image/png

Added: zc.async/trunk/htmldocs/1.5.0/catastrophes.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/catastrophes.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/catastrophes.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,946 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Recovering from Catastrophes &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="up" title="Tips and Tricks" href="tips.html" />
+    <link rel="next" title="Zope 3 General Tips and Tricks" href="z3.html" />
+    <link rel="prev" title="Tips and Tricks" href="tips.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="z3.html" title="Zope 3 General Tips and Tricks"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="tips.html" title="Tips and Tricks"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+          <li><a href="tips.html" accesskey="U">Tips and Tricks</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="id1">
+<span id="recovering-from-catastrophes"></span><h1 id="id1"><span id="recovering-from-catastrophes"></span>Recovering from Catastrophes<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h1>
+<div class="section" id="what-might-go-wrong">
+<h2 id="what-might-go-wrong">What Might Go Wrong?<a class="headerlink" href="#what-might-go-wrong" title="Permalink to this headline">¶</a></h2>
+<p>Sometimes bad things happen in the course of processing tasks. What might go
+wrong? How does zc.async handle these errors? What are your responsibilities?</p>
+<p>First, what might go wrong?</p>
+<ul class="simple">
+<li>zc.async could have a problem while polling for jobs.  We&#8217;ll call this a
+&#8220;polling exception.&#8221;</li>
+<li>zc.async could have a problem while performing a particular job.  We&#8217;ll call
+this a &#8220;job-related exception.&#8221;</li>
+</ul>
+<p>For the purpose of this discussion, we will omit the possibility that zc.async
+has a bug. That is certainly a possibility, but the recovery story is not
+predictable, and if we knew of a bug, we&#8217;d try to fix it, rather than discuss
+it here!</p>
+<p>We&#8217;ll discuss both polling exceptions and job related exceptions, then drill
+down into some specific scenarios. This will illuminate how your code and
+zc.async&#8217;s can work together to handle them.</p>
+<div class="section" id="polling-exceptions">
+<h3 id="polling-exceptions">Polling Exceptions<a class="headerlink" href="#polling-exceptions" title="Permalink to this headline">¶</a></h3>
+<p>Polling exceptions are, at least in theory, the least of your worries. You
+shouldn&#8217;t have to worry about them; and if you do, it is probably a basic
+configuration problem that you need to address, such as making sure that the
+dispatcher process has access to the needed databases and software; or making
+sure that the dispatcher process is run by a daemonizing software that will
+restart if needed, such as zdaemon (<a class="reference external" href="http://pypi.python.org/pypi/zdaemon">http://pypi.python.org/pypi/zdaemon</a>) or
+supervisor (<a class="reference external" href="http://supervisord.org/">http://supervisord.org/</a>).</p>
+<p>zc.async is largely responsible for dealing with polling exceptions. What does
+it have to handle?</p>
+<ul class="simple">
+<li>The process running the poll ends, perhaps in the middle of a poll.</li>
+<li>zc.async cannot commit a transaction during the poll, for instance because of
+a ConflictError, or because the database is unavailable.</li>
+</ul>
+<p>What needs to happen to handle these problems?</p>
+<div class="section" id="process-ends-while-polling">
+<h4 id="process-ends-while-polling">Process Ends while Polling<a class="headerlink" href="#process-ends-while-polling" title="Permalink to this headline">¶</a></h4>
+<p>If the process ends, your daemonizing front-end (zdaemon, supervisor, etc.)
+needs to restart it. The ZODB will discard incomplete transaction data, if any.</p>
+<p>The only thing a zc.async dispatcher needs to handle is clean up.</p>
+<ul class="simple">
+<li>Ideally it will be able to deactivate its record in the ZODB during the
+process shutdown.</li>
+<li>Instead, if it was a &#8220;hard crash&#8221; that didn&#8217;t allow deactivation, a sibling
+dispatcher will realize that the dispatcher is down and deactivate it.</li>
+<li>Or, finally, if it was a hard crash without a sibling, and the daemon
+restarts a process for the original dispatcher instance, the new process
+needs to realize that the old process is dead, not competing with it.</li>
+</ul>
+</div>
+<div class="section" id="transaction-error-while-polling">
+<h4 id="transaction-error-while-polling">Transaction Error while Polling<a class="headerlink" href="#transaction-error-while-polling" title="Permalink to this headline">¶</a></h4>
+<p>If the poll gets a conflict error, it should simply abort and retry the poll,
+forever, with a small back-off.</p>
+<p>If the database goes away (perhaps the ZEO server goes down for a bit, and the
+ZEO client to which the dispatcher is connected is trying to reconnect) it
+should gracefully try to wait for the database to return, and resume when it
+does.</p>
+<p>Other, more dramatic errors, such as POSKey errors, are generally considered to
+be out of zc.async&#8217;s domain and control. It should ideally continue to try to
+resume as long as the process is alive, in case somehow the situation improves,
+but this may be difficult and the expectations for zc.async&#8217;s recovery are
+lower than with ConflictErrors and ClientDisconnected errors.</p>
+</div>
+<div class="section" id="summary-of-polling-exceptions">
+<h4 id="summary-of-polling-exceptions">Summary of Polling Exceptions<a class="headerlink" href="#summary-of-polling-exceptions" title="Permalink to this headline">¶</a></h4>
+<p>To repeat, then, polling exceptions have two basic scenarios.</p>
+<p>If a dispatcher process ends, it needs to deactivate its record in the ZODB, or
+let another process know to deactivate it.</p>
+<p>If a ZODB.POSException.ConflictError occurs, retry forever with a small
+backoff; or if ZEO.Exceptions.ClientDisconnected occurs, retry forever with a
+small backoff, waiting for the database to come back.</p>
+<p>Most anything else will ideally keep zc.async attempting to re-poll, but it may
+not happen: expectations are lower.</p>
+</div>
+</div>
+<div class="section" id="job-related-exceptions">
+<h3 id="job-related-exceptions">Job-Related Exceptions<a class="headerlink" href="#job-related-exceptions" title="Permalink to this headline">¶</a></h3>
+<p>What about job-related exceptions? Responsibility for handling job-related
+exceptions is shared between your code and zc.async&#8217;s.  What might happen?</p>
+<ul class="simple">
+<li>Your job might fail internally.</li>
+<li>The process running your task ends before completing your task.</li>
+<li>zc.async cannot commit a transaction after your task completes, for instance
+because of a ConflictError, or because the database is unavailable.</li>
+</ul>
+<p>What should occur to handle these problems?</p>
+<div class="section" id="job-fails">
+<h4 id="job-fails">Job Fails<a class="headerlink" href="#job-fails" title="Permalink to this headline">¶</a></h4>
+<p>As discussed elsewhere, if your job fails in your own code, this is mostly
+your responsibility. You should handle possible errors both within your job&#8217;s
+code, and in callbacks, as appropriate.</p>
+<p>The other tool at your disposal for this situation, as with others below, is a
+retry policy. Retry policies let you determine what zc.async should do when
+your job fails. The default retry policy for job failures (as well as commit
+failures, below) is that transaction errors, such as conflict errors, are
+retried five times, and a ZEO ClientDisconnected error is retried forever with
+a backoff. You can customize these.</p>
+<p>Other than supporting these tools, zc.async&#8217;s only other responsibilities are
+to report.</p>
+<p>By default, zc.async will log a failure of a job entered in a queue at the
+&#8220;ERROR&#8221; level in the <tt class="docutils literal"><span class="pre">zc.async.events</span></tt> log, and it will log a failure of a
+callback or other internal job at the &#8220;CRITICAL&#8221; level. This can be controlled
+per-process and per-job, as we&#8217;ll see below. These tracebacks include
+information about the local and global variables for each frame in the stack,
+which can be useful to deduce the problem that occurred.</p>
+<p>zc.async also includes a <tt class="docutils literal"><span class="pre">Failure</span></tt> object on the job as a result, to let you
+react to the problem in a callback, and analyze it later.  This is discussed in
+detail in other documents.</p>
+</div>
+<div class="section" id="process-ends-during-job">
+<h4 id="process-ends-during-job">Process Ends During Job<a class="headerlink" href="#process-ends-during-job" title="Permalink to this headline">¶</a></h4>
+<p>If a process ends while it is performing a job, that is similar, in large part,
+to the possibility of the process ending the polling job: we need to restart
+the process, and realize that we had started the job. But should we restart the
+job, or abort it?</p>
+<p>Answering this question is a matter of policy, and requires knowing what each
+job does.</p>
+<p>Generally, if a job is fully transactional, such as writing something to the
+ZODB, and the job has not timed out yet, you&#8217;ll want to restart it. You might
+want to restart some reasonably large number of times, and then suspect that,
+since you can&#8217;t seem to finish the job, maybe the job is causing the process to
+die, and you should abort.  Or perhaps you want to restart for ever.</p>
+<p>If the job isn&#8217;t transactional, such as communicating with an external service,
+you might want to abort the job, and set up some callbacks to handle the
+fallout.</p>
+<p>As we&#8217;ll see below, zc.async defaults to guessing that jobs placed directly in
+a queue are transactional, and can be tried up to ten times; and that jobs
+used as callbacks are also transactional, and can be tried until they
+succeed.  The defaults can be changed and the behavior of an individual
+job can be changed.</p>
+<p>These settings are controlled with a RetryPolicy, discussed below.</p>
+</div>
+<div class="section" id="transaction-error-during-job">
+<h4 id="transaction-error-during-job">Transaction Error During Job<a class="headerlink" href="#transaction-error-during-job" title="Permalink to this headline">¶</a></h4>
+<p>Handling transaction errors after processing a job is also similar to the
+handling of transaction errors for polling exceptions. ConflictErrors and
+ClientDisconnected errors should often cause jobs to be aborted and restarted.
+However, if the job is not transactional, such as communicating with an
+external service, a simple abort and retry may be hazardous. Also, many jobs
+should be stopped if they retry on ConflictError more than some number of
+times&#8211;a heuristic bellweather&#8211;with the logic that they may be simply doing
+something too problematic, and they are blocking other tasks from starting. But
+other jobs should be retried until they complete.</p>
+<p>As mentioned above, zc.async defaults to guessing that jobs are transactional.
+Client Disconnected errors are retried forever, with a small backoff. Jobs
+placed in a queue retry transaction errors, such as ConflictErrors, four times,
+while callbacks retry them forever. The defaults can be changed and the
+behavior of an individual job can be changed, using the RetryPolicy described
+below.</p>
+</div>
+<div class="section" id="summary-of-job-related-exceptions">
+<h4 id="summary-of-job-related-exceptions">Summary of Job-Related Exceptions<a class="headerlink" href="#summary-of-job-related-exceptions" title="Permalink to this headline">¶</a></h4>
+<p>If an exception occurs in your job&#8217;s code, zc.async will log it as an ERROR
+if a main queue job and as CRITICAL if it is a callback; and it will make the
+result of the call a <tt class="docutils literal"><span class="pre">Failure</span></tt> with error information, as shown elsewhere.
+Everything else is your responsibility, to be handled with try:except or
+try:finally blocks in your code, callbacks, or custom RetryPolicies.</p>
+<p>Process death, conflict errors, and <tt class="docutils literal"><span class="pre">ClientDisconnected</span></tt> errors all may need
+to be handled differently for different jobs. zc.async has a default policy for
+jobs placed in a queue, and for callback jobs. The default policy, a
+RetryPolicy, can be changed and can be set explicitly per-job.</p>
+</div>
+</div>
+<div class="section" id="your-responsibilities">
+<h3 id="your-responsibilities">Your Responsibilities<a class="headerlink" href="#your-responsibilities" title="Permalink to this headline">¶</a></h3>
+<p>As the author of a zc.async job, your responsibilities, then, are to handle
+your own exceptions; and to make sure that the retry policy for each job is
+appropriate.  This is controlled with an IRetryPolicy, as shown below.</p>
+<p>As someone configuring a running dispatcher, you need to make sure that you
+give the dispatcher the necessary access to databases and software to perform
+your jobs, and you need to review (and rotate!) your logs.</p>
+</div>
+<div class="section" id="zc-async-s-responsibilities">
+<h3 id="zc-async-s-responsibilities">zc.async&#8217;s Responsibilities<a class="headerlink" href="#zc-async-s-responsibilities" title="Permalink to this headline">¶</a></h3>
+<p>zc.async needs to have polling robust in the face of restarts, ConflictErrors
+and ClientDisconnected errors. It needs to give your code a chance to decide
+what to do in these circumstances, and log your errors.</p>
+</div>
+<div class="section" id="retry-policies">
+<h3 id="retry-policies">Retry Policies<a class="headerlink" href="#retry-policies" title="Permalink to this headline">¶</a></h3>
+<p>The rest of the document uses scenarios to illustrate how zc.async handles
+errors, and how you might want to configure retry policies.</p>
+<p>What is a retry policy?  It is used in three circumstances.</p>
+<ul class="simple">
+<li>When the job starts but fails to complete because the system is interrupted,
+the job will try to call <tt class="docutils literal"><span class="pre">retry_policy.interrupted()</span></tt> to get a boolean as
+to whether the job should be retried.</li>
+<li>When the code the job ran fails, the job will try to call
+<tt class="docutils literal"><span class="pre">retry_policy.jobError(failure,</span> <span class="pre">data_cache)</span></tt> to get a boolean as to whether
+the job should be retried.</li>
+<li>When the commit fails, the job will try to call
+<tt class="docutils literal"><span class="pre">retry_policy.commitError(failure,</span> <span class="pre">data_cache)</span></tt> to get a boolean as to
+whether the job should be retried.</li>
+</ul>
+<p>Why does this need to be a policy?  Can&#8217;t it be a simpler arrangement?</p>
+<p>The heart of the problem is that different jobs need different error
+resolutions.</p>
+<p>In some cases, jobs may not be fully transactional.  For instance, the job
+may be communicating with an external system, such as a credit card system.
+The retry policy here should typically be &#8220;never&#8221;: perhaps a callback should be
+in charge of determining what to do next.</p>
+<p>If a job is fully transactional, it can be retried.  But even then the desired
+behavior may differ.</p>
+<ul class="simple">
+<li>In typical cases, some errors should simply cause a failure, while other
+errors, such as database conflict errors, should cause a limited number of
+retries.</li>
+<li>In some jobs, conflict errors should be retried forever, because the job must
+be run to completion or else the system should fall over. Callbacks that try
+to handle errors themselves may take this approach, for instance.</li>
+</ul>
+<p>zc.async currently ships with three retry policies.</p>
+<ol class="arabic simple">
+<li>The default, appropriate for most fully transactional jobs, is the
+zc.async.job.RetryCommonFourTimes.  This retries ZEO disconnects forever;
+and interrupts and transaction errors such as conflicts a set number of
+times.</li>
+<li>The other available (pre-written) option for transactional jobs is
+zc.async.job.RetryCommonForever. Callbacks will get this policy by
+default.  This retries ZEO disconnects, transaction errors such as conflict
+errors, interrupts, and <em>anything</em> that happens during the job&#8217;s commit,
+forever.</li>
+<li>The last retry policy is zc.async.job.NeverRetry.  This is appropriate for
+non-transactional jobs. You&#8217;ll still typically need to handle errors in
+your callbacks.</li>
+</ol>
+<p>If you look at these, you will see that it is trivial to write your own, if
+desired.</p>
+</div>
+<div class="section" id="scenarios">
+<h3 id="scenarios">Scenarios<a class="headerlink" href="#scenarios" title="Permalink to this headline">¶</a></h3>
+<p>We&#8217;ll examine polling error scenarios and job error scenarios.</p>
+<ul class="simple">
+<li>Polling errors<ul>
+<li>The system is polling and gets a ConflictError.</li>
+<li>The system is polling and gets a ClientDisconnected error.</li>
+</ul>
+</li>
+<li>Job errors<ul>
+<li>A worker process is working on a job with the default retry policy. The
+process dies gracefully and restarts.</li>
+<li>Like the previous scenario, a worker process is working on a job with the
+default retry policy. The process crashes hard (does not die gracefully)
+and restarts.</li>
+<li>Like the previous scenario, a worker process is working on a job with the
+default retry policy. The process crashes hard (does not die gracefully)
+and a sibling notices and takes over.</li>
+<li>A worker process is working on a job with the default retry policy and gets
+an error during the job or the commit.</li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+<div class="section" id="scenarios-polling-errors">
+<h2 id="scenarios-polling-errors">Scenarios: Polling Errors<a class="headerlink" href="#scenarios-polling-errors" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="conflicterror">
+<h3 id="conflicterror">ConflictError<a class="headerlink" href="#conflicterror" title="Permalink to this headline">¶</a></h3>
+<p>A common place for a conflict error is with two dispatchers trying to claim the
+same job from the queue.  This example will mimic that situation.</p>
+<p>Imagine we have a full set up with a dispatcher, agent, and queue. <a class="footnote-reference" href="#setup" id="id2">[1]</a>
+We&#8217;ll actually replace the agent&#8217;s chooser with one that behaves badly: it
+blocks, waiting for our lock.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">threading</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">lock1</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">lock2</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">lock1</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">lock2</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">acquireLockAndChooseFirst</span><span class="p">(</span><span class="n">agent</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">res</span> <span class="o">=</span> <span class="n">agent</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">()</span>
+<span class="gp">... </span>    <span class="k">if</span> <span class="n">res</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="n">lock2</span><span class="o">.</span><span class="n">release</span><span class="p">()</span>
+<span class="gp">... </span>        <span class="n">lock1</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">res</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.instanceuuid</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.testing</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.dispatcher</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">pprint</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">pprint</span><span class="o">.</span><span class="n">pprint</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">,</span> <span class="mf">0</span><span class="p">))</span>
+<span class="go">{&#39;&#39;: {&#39;main&#39;: {&#39;active jobs&#39;: [],</span>
+<span class="go">               &#39;error&#39;: None,</span>
+<span class="go">               &#39;len&#39;: 0,</span>
+<span class="go">               &#39;new jobs&#39;: [],</span>
+<span class="go">               &#39;size&#39;: 3}}}</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queues</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">KEY</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span> <span class="o">=</span> <span class="n">queues</span><span class="p">[</span><span class="s">&#39;&#39;</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">dispatchers</span><span class="p">[</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">instanceuuid</span><span class="o">.</span><span class="n">UUID</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span> <span class="o">=</span> <span class="n">da</span><span class="p">[</span><span class="s">&#39;main&#39;</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span><span class="o">.</span><span class="n">chooser</span> <span class="o">=</span> <span class="n">acquireLockAndChooseFirst</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">returnSomething</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="mf">42</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">returnSomething</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<p>Now, when the agent tries to get our job, we&#8217;ll start and commit another
+transaction that removes it from the queue.  This will generate a conflict
+error for the poll&#8217;s thread and transaction, because it cannot also remove the
+same job.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">lock2</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">pull</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">lock1</span><span class="o">.</span><span class="n">release</span><span class="p">()</span>
+</pre></div>
+<p>However, the ConflictError is handled, and polling continues.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.agent</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span><span class="o">.</span><span class="n">chooser</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">agent</span><span class="o">.</span><span class="n">chooseFirst</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.dispatcher</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.testing</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">pprint</span><span class="o">.</span><span class="n">pprint</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">))</span>
+<span class="go">{&#39;&#39;: {&#39;main&#39;: {&#39;active jobs&#39;: [],</span>
+<span class="go">               &#39;error&#39;: None,</span>
+<span class="go">               &#39;len&#39;: 0,</span>
+<span class="go">               &#39;new jobs&#39;: [],</span>
+<span class="go">               &#39;size&#39;: 3}}}</span>
+</pre></div>
+<p>And if we put the job back, it will be performed.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="ow">is</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_result</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">42</span>
+</pre></div>
+</div>
+<div class="section" id="client-disconnected">
+<h3 id="client-disconnected">Client Disconnected<a class="headerlink" href="#client-disconnected" title="Permalink to this headline">¶</a></h3>
+<p>The story is very similar if the ZEO connection goes away for a while.  We&#8217;ll
+mimic a ZEO ClientDisconnected error by monkeypatching
+transaction.TranasctionManager.commit.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">lock1</span><span class="o">.</span><span class="n">locked</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">lock2</span><span class="o">.</span><span class="n">locked</span><span class="p">()</span>
+<span class="go">True</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span><span class="o">.</span><span class="n">chooser</span> <span class="o">=</span> <span class="n">acquireLockAndChooseFirst</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">returnSomething</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">lock2</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">ZEO.Exceptions</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">commit</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">raise</span> <span class="n">ZEO</span><span class="o">.</span><span class="n">Exceptions</span><span class="o">.</span><span class="n">ClientDisconnected</span><span class="p">()</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">old_commit</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">TransactionManager</span><span class="o">.</span><span class="n">commit</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">TransactionManager</span><span class="o">.</span><span class="n">commit</span> <span class="o">=</span> <span class="n">commit</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">time</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">sleep_requests</span> <span class="o">=</span> <span class="p">[]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">sleep</span><span class="p">(</span><span class="n">i</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">sleep_requests</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">old_sleep</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">sleep</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">time</span><span class="o">.</span><span class="n">sleep</span> <span class="o">=</span> <span class="n">sleep</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span><span class="o">.</span><span class="n">chooser</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">agent</span><span class="o">.</span><span class="n">chooseFirst</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">lock1</span><span class="o">.</span><span class="n">release</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">info</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">)[</span><span class="s">&#39;&#39;</span><span class="p">][</span><span class="s">&#39;main&#39;</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">info</span><span class="p">[</span><span class="s">&#39;active jobs&#39;</span><span class="p">]</span> <span class="o">+</span> <span class="n">info</span><span class="p">[</span><span class="s">&#39;new jobs&#39;</span><span class="p">])</span>
+<span class="go">1</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">TransactionManager</span><span class="o">.</span><span class="n">commit</span> <span class="o">=</span> <span class="n">old_commit</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_result</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">42</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">sleep_requests</span><span class="p">)</span>
+<span class="go">True</span>
+</pre></div>
+</blockquote>
+<p>Here&#8217;s another variant that mimics being unable to read the storage during a
+poll, and then recuperating.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">error_raised</span> <span class="o">=</span> <span class="bp">False</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">raiseDisconnectedThenChooseFirst</span><span class="p">(</span><span class="n">agent</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">global</span> <span class="n">error_raised</span>
+<span class="gp">... </span>    <span class="k">if</span> <span class="ow">not</span> <span class="n">error_raised</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="n">error_raised</span> <span class="o">=</span> <span class="bp">True</span>
+<span class="gp">... </span>        <span class="k">raise</span> <span class="n">ZEO</span><span class="o">.</span><span class="n">Exceptions</span><span class="o">.</span><span class="n">ClientDisconnected</span><span class="p">()</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">agent</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">claim</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">agent</span><span class="o">.</span><span class="n">chooser</span> <span class="o">=</span> <span class="n">raiseDisconnectedThenChooseFirst</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">returnSomething</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="mf">42</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">returnSomething</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">pprint</span><span class="o">.</span><span class="n">pprint</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">))</span> <span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">{&#39;&#39;: {&#39;main&#39;: {&#39;active jobs&#39;: [],</span>
+<span class="go">               &#39;error&#39;: &lt;zc.twist.Failure ...ClientDisconnected&gt;,</span>
+<span class="go">               &#39;len&#39;: 0,</span>
+<span class="go">               &#39;new jobs&#39;: [],</span>
+<span class="go">               &#39;size&#39;: 3}}}</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_result</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">42</span>
+</pre></div>
+</div>
+</div>
+<div class="section" id="scenarios-job-related-errors">
+<h2 id="scenarios-job-related-errors">Scenarios: Job-Related Errors<a class="headerlink" href="#scenarios-job-related-errors" title="Permalink to this headline">¶</a></h2>
+<div class="section" id="graceful-shutdown-during-job">
+<h3 id="graceful-shutdown-during-job">Graceful Shutdown During Job<a class="headerlink" href="#graceful-shutdown-during-job" title="Permalink to this headline">¶</a></h3>
+<p>First let&#8217;s consider how a failed job with a callback or two is handled when
+the dispatcher dies.</p>
+<p>Here we start a job.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.component</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.testing</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.dispatcher</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">KEY</span><span class="p">][</span><span class="s">&#39;&#39;</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">lock</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">lock</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">fail_flag</span> <span class="o">=</span> <span class="bp">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">wait_for_me</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">global</span> <span class="n">fail_flag</span>
+<span class="gp">... </span>    <span class="k">if</span> <span class="n">fail_flag</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="n">fail_flag</span> <span class="o">=</span> <span class="bp">False</span>
+<span class="gp">... </span>        <span class="n">lock</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
+<span class="gp">... </span>        <span class="n">lock</span><span class="o">.</span><span class="n">release</span><span class="p">()</span> <span class="c"># so we can use the same lock again later</span>
+<span class="gp">... </span>        <span class="k">raise</span> <span class="ne">SystemExit</span><span class="p">()</span> <span class="c"># this will cause the worker thread to exit</span>
+<span class="gp">... </span>    <span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="k">return</span> <span class="mf">42</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">handle_result</span><span class="p">(</span><span class="n">result</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="s">&#39;I got result </span><span class="si">%r</span><span class="s">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">result</span><span class="p">,)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">wait_for_me</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback_job</span> <span class="o">=</span> <span class="n">job</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">handle_result</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">poll</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_start</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+</pre></div>
+</blockquote>
+<p>In this scenario, <tt class="docutils literal"><span class="pre">wait_for_me</span></tt> is a job that, the first time it is run, will
+&#8220;unexpectedly&#8221; be lost while the dispatcher stops working. <tt class="docutils literal"><span class="pre">handle_result</span></tt>
+will simply show us that callbacks will be called successfully.</p>
+<p>The job has started. Now, the dispatcher suddenly dies without the thread
+performing <tt class="docutils literal"><span class="pre">wait_for_me</span></tt> getting a chance to finish. For our first example,
+let&#8217;s give the dispatcher a graceful exit. The dispatcher gets a chance to
+clean up its dispatcher agents, and job.handleInterrupt() goes into the queue.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">callFromThread</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">stop</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_deactivation</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">ACTIVE</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
+<span class="go">1</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">interrupt_job</span> <span class="o">=</span> <span class="n">queue</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">interrupt_job</span> <span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">&lt;zc.async.job.Job ... ``zc.async.job.Job ... :handleInterrupt()``&gt;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span><span class="o">.</span><span class="n">callable</span> <span class="c"># doctest: +ELLIPSIS</span>
+<span class="go">&lt;bound method Job.handleInterrupt of &lt;...Job ... ``...wait_for_me()``&gt;&gt;</span>
+</pre></div>
+<p>Now when the process starts back up again, <tt class="docutils literal"><span class="pre">handleInterrupt</span></tt> checks with the
+default retry policy as to what should be done. It requests that the job be
+retried. It&#8217;s put back in the queue, and it is called again normally.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">old_dispatcher</span> <span class="o">=</span> <span class="n">dispatcher</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">clear</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">subscribers</span><span class="o">.</span><span class="n">ThreadedDispatcherInstaller</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="n">poll_interval</span><span class="o">=</span><span class="mf">0.1</span><span class="p">)(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">DatabaseOpened</span><span class="p">(</span><span class="n">db</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_result</span><span class="p">(</span><span class="n">interrupt_job</span><span class="p">)</span>
+</pre></div>
+<p>Now we need to wait for the job.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_result</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">42</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback_job</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">COMPLETED</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback_job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&#39;I got result 42&#39;</span>
+</pre></div>
+<p>The job now has a retry policy with some currently non-interface values that
+are still worth showing here.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">policy</span> <span class="o">=</span> <span class="n">job</span><span class="o">.</span><span class="n">getRetryPolicy</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">policy</span><span class="o">.</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;interruptions&#39;</span><span class="p">)</span>
+<span class="go">1</span>
+</pre></div>
+<p>This shows that the policy registered one interruption. <a class="footnote-reference" href="#cleanup1" id="id3">[2]</a></p>
+</div>
+<div class="section" id="hard-crash-during-job">
+<h3 id="hard-crash-during-job">Hard Crash During Job<a class="headerlink" href="#hard-crash-during-job" title="Permalink to this headline">¶</a></h3>
+<p>Our next catastrophe only changes one aspect to the previous one: the
+dispatcher does not stop gracefully, and does not have a chance to clean up its
+active jobs.  It is a &#8220;hard&#8221; crash.</p>
+<p>To show this, we will start a job, simulate the dispatcher dying &#8220;hard,&#8221; and
+restart it so it clean up.</p>
+<p>So, first we start a long-running job in the dispatcher.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">lock</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">fail_flag</span> <span class="o">=</span> <span class="bp">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">wait_for_me</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback_job</span> <span class="o">=</span> <span class="n">job</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">handle_result</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">poll</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_start</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+</pre></div>
+<p>Now we&#8217;ll &#8220;crash&#8221; the dispatcher.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">activated</span> <span class="o">=</span> <span class="bp">False</span> <span class="c"># this will make polling stop, without</span>
+<span class="gp">... </span>                             <span class="c"># cleanup</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">callFromThread</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">crash</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">thread</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="mf">3</span><span class="p">)</span>
+</pre></div>
+<p>Hard crashes can be detected because the dispatchers write datetimes to the
+database every few polls. A given dispatcher instance does this for each queue
+on a <tt class="docutils literal"><span class="pre">DispatcherAgents</span></tt> object available in <tt class="docutils literal"><span class="pre">queue.dispatchers[UUID]</span></tt>,
+where <tt class="docutils literal"><span class="pre">UUID</span></tt> is the uuid of that dispatcher.</p>
+<p>The <tt class="docutils literal"><span class="pre">DispatcherAgents</span></tt> object has four pertinent attributes:
+<tt class="docutils literal"><span class="pre">ping_interval</span></tt>, <tt class="docutils literal"><span class="pre">ping_death_interval</span></tt>, <tt class="docutils literal"><span class="pre">last_ping.value</span></tt>, and <tt class="docutils literal"><span class="pre">dead</span></tt>.
+About every <tt class="docutils literal"><span class="pre">ping_interval</span></tt> (a <tt class="docutils literal"><span class="pre">datetime.timedelta</span></tt>), the dispatcher is
+supposed to write a <tt class="docutils literal"><span class="pre">datetime</span></tt> to <tt class="docutils literal"><span class="pre">last_ping.value</span></tt>. If the
+<tt class="docutils literal"><span class="pre">last_ping.value</span></tt> plus the <tt class="docutils literal"><span class="pre">ping_death_interval</span></tt> (also a <tt class="docutils literal"><span class="pre">timedelta</span></tt>) is
+older than now, the dispatcher is considered to be <tt class="docutils literal"><span class="pre">dead</span></tt>, and old jobs
+should be cleaned up.</p>
+<p>The <tt class="docutils literal"><span class="pre">ping_interval</span></tt> defaults to 30 seconds, and the <tt class="docutils literal"><span class="pre">ping_death_interval</span></tt>
+defaults to 60 seconds. Generally, the <tt class="docutils literal"><span class="pre">ping_death_interval</span></tt> should be at
+least two or three poll intervals (<tt class="docutils literal"><span class="pre">zc.async.dispatcher.get().poll_interval</span></tt>)
+greater than the <tt class="docutils literal"><span class="pre">ping_interval</span></tt>.</p>
+<p>The ping hasn&#8217;t timed out yet, so the dispatcher isn&#8217;t considered dead yet.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.instanceuuid</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">dispatchers</span><span class="p">[</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">instanceuuid</span><span class="o">.</span><span class="n">UUID</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">ping_death_interval</span>
+<span class="go">datetime.timedelta(0, 60)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">ping_interval</span>
+<span class="go">datetime.timedelta(0, 30)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">da</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">dead</span>
+<span class="go">False</span>
+</pre></div>
+<p>Therefore, the job is still sitting around in the dispatcher&#8217;s pile in the
+database (the <tt class="docutils literal"><span class="pre">main</span></tt> key is for the <tt class="docutils literal"><span class="pre">main</span></tt> agent installed in this
+dispatcher in the set up for these examples).</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="ow">in</span> <span class="n">da</span><span class="p">[</span><span class="s">&#39;main&#39;</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">ACTIVE</span>
+<span class="go">True</span>
+</pre></div>
+<p>Let&#8217;s start our dispatcher up again.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">old_dispatcher</span> <span class="o">=</span> <span class="n">dispatcher</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">clear</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">subscribers</span><span class="o">.</span><span class="n">ThreadedDispatcherInstaller</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="n">poll_interval</span><span class="o">=</span><span class="mf">0.1</span><span class="p">)(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">DatabaseOpened</span><span class="p">(</span><span class="n">db</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+</pre></div>
+<p>Initially, it&#8217;s going to be a bit confused, because it sees that the
+DispatcherAgents object is <tt class="docutils literal"><span class="pre">activated</span></tt>, and not <tt class="docutils literal"><span class="pre">dead</span></tt>. It can&#8217;t tell if
+there&#8217;s another process using its same UUID, or if it is looking at the result
+of a hard crash.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_result</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">seconds</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="go">AssertionError: job never completed</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">,</span> <span class="n">seconds</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
+<span class="go">{&#39;&#39;: None}</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">reversed</span><span class="p">(</span><span class="n">event_logs</span><span class="o">.</span><span class="n">records</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">if</span> <span class="n">r</span><span class="o">.</span><span class="n">levelname</span> <span class="o">==</span> <span class="s">&#39;ERROR&#39;</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="k">break</span>
+<span class="gp">... </span><span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>    <span class="k">assert</span> <span class="bp">False</span><span class="p">,</span> <span class="s">&#39;did not find log&#39;</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">r</span><span class="o">.</span><span class="n">getMessage</span><span class="p">()</span> <span class="c"># doctest: +ELLIPSIS +NORMALIZE_WHITESPACE</span>
+<span class="go">UUID ... already activated in queue  (oid 4): another process?</span>
+<span class="go">(To stop poll attempts in this process, set</span>
+<span class="go">``zc.async.dispatcher.get().activated = False``.  To stop polls</span>
+<span class="go">permanently, don&#39;t start a zc.async.dispatcher!)</span>
+</pre></div>
+<p>To speed up the realization of our dispatcher that the previous activation is
+<tt class="docutils literal"><span class="pre">dead</span></tt>, we&#8217;ll set the ping_death_interval to just one second.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">dead</span>
+<span class="go">False</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="ow">in</span> <span class="n">da</span><span class="p">[</span><span class="s">&#39;main&#39;</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">len</span><span class="p">(</span><span class="n">queue</span><span class="p">)</span>
+<span class="go">0</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">datetime</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">ping_death_interval</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_death</span><span class="p">(</span><span class="n">da</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">da</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">True</span>
+</pre></div>
+<p>After the next poll, the dispatcher will have cleaned up its old tasks in the
+same way we saw in the previous example. The job&#8217;s <tt class="docutils literal"><span class="pre">handleInterrupt</span></tt> method
+will be called, and the job will be put back in the queue to be retried. The
+DispatcherAgents object is no longer dead, because it is tied to the new
+instance of the dispatcher.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">poll</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">ping_death_interval</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="mf">60</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">zc.async.testing</span> <span class="kn">import</span> <span class="n">time_sleep</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">wait_for_pending</span><span class="p">(</span><span class="n">job</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mf">600</span><span class="p">):</span>
+<span class="gp">... </span>        <span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">... </span>        <span class="k">if</span> <span class="n">job</span><span class="o">.</span><span class="n">status</span> <span class="ow">in</span> <span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">PENDING</span><span class="p">):</span>
+<span class="gp">... </span>            <span class="k">break</span>
+<span class="gp">... </span>        <span class="n">time_sleep</span><span class="p">(</span><span class="mf">0.01</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="k">else</span><span class="p">:</span>
+<span class="gp">... </span>        <span class="k">assert</span> <span class="bp">False</span><span class="p">,</span> <span class="s">&#39;job never pending: &#39;</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">job</span><span class="o">.</span><span class="n">status</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">wait_for_pending</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="ow">in</span> <span class="n">da</span><span class="p">[</span><span class="s">&#39;main&#39;</span><span class="p">]</span>
+<span class="go">False</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">da</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">dead</span>
+<span class="go">False</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span> <span class="ow">is</span> <span class="n">job</span>
+<span class="go">True</span>
+</pre></div>
+<p>Now we need to wait for the job.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_result</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">42</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback_job</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">COMPLETED</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback_job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&#39;I got result 42&#39;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">policy</span> <span class="o">=</span> <span class="n">job</span><span class="o">.</span><span class="n">getRetryPolicy</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">policy</span><span class="o">.</span><span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">&#39;interruptions&#39;</span><span class="p">)</span>
+<span class="go">1</span>
+</pre></div>
+<p>The dispatcher cleaned up its own &#8220;hard&#8221; crash.</p>
+<p><a class="footnote-reference" href="#cleanup1" id="id4">[2]</a></p>
+</div>
+<div class="section" id="hard-crash-during-job-with-sibling-recovery">
+<span id="hard-crash-with-sibling-recovery"></span><h3 id="hard-crash-during-job-with-sibling-recovery"><span id="hard-crash-with-sibling-recovery"></span>Hard Crash During Job with Sibling Recovery<a class="headerlink" href="#hard-crash-during-job-with-sibling-recovery" title="Permalink to this headline">¶</a></h3>
+<p>Our next catastrophe is the same as the one before, except, after one
+dispatcher&#8217;s hard crash, another dispatcher is around to clean up the dead
+jobs.</p>
+<p>To show this, we will start a job, start a second dispatcher, simulate the
+first dispatcher dying &#8220;hard,&#8221; and watch the second dispatcher clean up
+after the first one.</p>
+<p>So, first we start a long-running job in the dispatcher as before.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">lock</span><span class="o">.</span><span class="n">acquire</span><span class="p">()</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">fail_flag</span> <span class="o">=</span> <span class="bp">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">wait_for_me</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback_job</span> <span class="o">=</span> <span class="n">job</span><span class="o">.</span><span class="n">addCallback</span><span class="p">(</span><span class="n">handle_result</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">poll</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_start</span><span class="p">(</span><span class="n">job</span><span class="p">,</span> <span class="n">seconds</span><span class="o">=</span><span class="mf">30</span><span class="p">)</span> <span class="c"># XXX not sure why so long</span>
+</pre></div>
+<p>Now we&#8217;ll start up an alternate dispatcher.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">uuid</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">alt_uuid</span> <span class="o">=</span> <span class="n">uuid</span><span class="o">.</span><span class="n">uuid1</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">subscribers</span><span class="o">.</span><span class="n">ThreadedDispatcherInstaller</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">poll_interval</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">uuid</span><span class="o">=</span><span class="n">alt_uuid</span><span class="p">)(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">DatabaseOpened</span><span class="p">(</span><span class="n">db</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">alt_dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">alt_uuid</span><span class="p">)</span>
+</pre></div>
+<p>Now we&#8217;ll &#8220;crash&#8221; the dispatcher.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">activated</span> <span class="o">=</span> <span class="bp">False</span> <span class="c"># this will make polling stop, without</span>
+<span class="gp">... </span>                             <span class="c"># cleanup</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">callFromThread</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">crash</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">thread</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="mf">3</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">thread</span><span class="o">.</span><span class="n">isAlive</span><span class="p">()</span>
+<span class="go">False</span>
+</pre></div>
+<p>As discussed in the previous example, the polling hasn&#8217;t timed out yet, so the
+alternate dispatcher can&#8217;t know that the first one is dead. Therefore, the job
+is still sitting around in the old dispatcher&#8217;s pile in the database.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">da</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">dead</span>
+<span class="go">False</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">ACTIVE</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">alt_poll_1</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">alt_dispatcher</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="ow">in</span> <span class="n">da</span><span class="p">[</span><span class="s">&#39;main&#39;</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">da</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">dead</span>
+<span class="go">False</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">alt_poll_2</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">alt_dispatcher</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="ow">in</span> <span class="n">da</span><span class="p">[</span><span class="s">&#39;main&#39;</span><span class="p">]</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">da</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">dead</span>
+<span class="go">False</span>
+</pre></div>
+<p>Above, the ping_death_interval was returned to the default of 60 seconds. To
+speed up the realization of our second dispatcher that the first one is dead,
+we&#8217;ll set the ping_death_interval back down to just one second.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">da</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">ping_death_interval</span>
+<span class="go">datetime.timedelta(0, 60)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">datetime</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">ping_death_interval</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_death</span><span class="p">(</span><span class="n">da</span><span class="p">)</span>
+</pre></div>
+<p>After the second dispatcher gets a poll&#8211;a chance to notice&#8211;it will have
+cleaned up the first dispatcher&#8217;s old tasks in the same way we saw in the
+previous example.  The job&#8217;s <tt class="docutils literal"><span class="pre">handleInterrupt</span></tt> method will be called, which
+in this case will put it back in the queue to be claimed and performed.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">alt_poll_3</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">alt_dispatcher</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="ow">in</span> <span class="n">da</span><span class="p">[</span><span class="s">&#39;main&#39;</span><span class="p">]</span>
+<span class="go">False</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">da</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">False</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">da</span><span class="o">.</span><span class="n">dead</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">wait_for_pending</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span> <span class="ow">is</span> <span class="n">job</span>
+<span class="go">True</span>
+</pre></div>
+<p>Now we need to wait for the job.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_result</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="go">42</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback_job</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">COMPLETED</span>
+<span class="go">True</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">callback_job</span><span class="o">.</span><span class="n">result</span>
+<span class="go">&#39;I got result 42&#39;</span>
+</pre></div>
+<p>The sibling, then, was able to clean up the mess left by the &#8220;hard&#8221; crash of
+the first dispatcher.</p>
+<p><a class="footnote-reference" href="#cleanup2" id="id5">[3]</a></p>
+</div>
+<div class="section" id="other-job-related-errors">
+<h3 id="other-job-related-errors">Other Job-Related Errors<a class="headerlink" href="#other-job-related-errors" title="Permalink to this headline">¶</a></h3>
+<p>Other problems&#8211;errors when performing or committing jobs&#8211;are handled within
+jobs, getting the decisions from retry policies as described above.  These
+are demonstrated in the job.txt document.</p>
+<p class="rubric">Footnotes</p>
+<table class="docutils footnote" frame="void" id="setup" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id2">[1]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">ZODB.FileStorage</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">storage</span> <span class="o">=</span> <span class="n">ZODB</span><span class="o">.</span><span class="n">FileStorage</span><span class="o">.</span><span class="n">FileStorage</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="s">&#39;main.fs&#39;</span><span class="p">,</span> <span class="n">create</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">ZODB.DB</span> <span class="kn">import</span> <span class="n">DB</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">db</span> <span class="o">=</span> <span class="n">DB</span><span class="p">(</span><span class="n">storage</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">root</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.configure</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">base</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.subscribers</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.component</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideHandler</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">subscribers</span><span class="o">.</span><span class="n">queue_installer</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideHandler</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">subscribers</span><span class="o">.</span><span class="n">ThreadedDispatcherInstaller</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="n">poll_interval</span><span class="o">=</span><span class="mf">0.1</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideHandler</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">subscribers</span><span class="o">.</span><span class="n">agent_installer</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.event</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">notify</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">DatabaseOpened</span><span class="p">(</span><span class="n">db</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="cleanup1" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label">[2]</td><td><em>(<a class="fn-backref" href="#id3">1</a>, <a class="fn-backref" href="#id4">2</a>)</em> <div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">lock</span><span class="o">.</span><span class="n">release</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">tear_down_dispatcher</span><span class="p">(</span><span class="n">old_dispatcher</span><span class="p">)</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="cleanup2" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id5">[3]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">lock</span><span class="o">.</span><span class="n">release</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">tear_down_dispatcher</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">tear_down_dispatcher</span><span class="p">(</span><span class="n">alt_dispatcher</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">time</span><span class="o">.</span><span class="n">sleep</span> <span class="o">=</span> <span class="n">old_sleep</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+</div>
+</div>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Table Of Contents</h3>
+            <ul>
+<li><a class="reference external" href="">Recovering from Catastrophes</a><ul>
+<li><a class="reference external" href="#what-might-go-wrong">What Might Go Wrong?</a><ul>
+<li><a class="reference external" href="#polling-exceptions">Polling Exceptions</a><ul>
+<li><a class="reference external" href="#process-ends-while-polling">Process Ends while Polling</a></li>
+<li><a class="reference external" href="#transaction-error-while-polling">Transaction Error while Polling</a></li>
+<li><a class="reference external" href="#summary-of-polling-exceptions">Summary of Polling Exceptions</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#job-related-exceptions">Job-Related Exceptions</a><ul>
+<li><a class="reference external" href="#job-fails">Job Fails</a></li>
+<li><a class="reference external" href="#process-ends-during-job">Process Ends During Job</a></li>
+<li><a class="reference external" href="#transaction-error-during-job">Transaction Error During Job</a></li>
+<li><a class="reference external" href="#summary-of-job-related-exceptions">Summary of Job-Related Exceptions</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#your-responsibilities">Your Responsibilities</a></li>
+<li><a class="reference external" href="#zc-async-s-responsibilities">zc.async&#8217;s Responsibilities</a></li>
+<li><a class="reference external" href="#retry-policies">Retry Policies</a></li>
+<li><a class="reference external" href="#scenarios">Scenarios</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#scenarios-polling-errors">Scenarios: Polling Errors</a><ul>
+<li><a class="reference external" href="#conflicterror">ConflictError</a></li>
+<li><a class="reference external" href="#client-disconnected">Client Disconnected</a></li>
+</ul>
+</li>
+<li><a class="reference external" href="#scenarios-job-related-errors">Scenarios: Job-Related Errors</a><ul>
+<li><a class="reference external" href="#graceful-shutdown-during-job">Graceful Shutdown During Job</a></li>
+<li><a class="reference external" href="#hard-crash-during-job">Hard Crash During Job</a></li>
+<li><a class="reference external" href="#hard-crash-during-job-with-sibling-recovery">Hard Crash During Job with Sibling Recovery</a></li>
+<li><a class="reference external" href="#other-job-related-errors">Other Job-Related Errors</a></li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="tips.html" title="previous chapter">Tips and Tricks</a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="z3.html" title="next chapter">Zope 3 General Tips and Tricks</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/catastrophes.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="z3.html" title="Zope 3 General Tips and Tricks"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="tips.html" title="Tips and Tricks"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+          <li><a href="tips.html" accesskey="U">Tips and Tricks</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/ftesting.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/ftesting.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/ftesting.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,347 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Zope 3 Testing Tips and Tricks &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="up" title="Tips and Tricks" href="tips.html" />
+    <link rel="next" title="Changes" href="CHANGES.html" />
+    <link rel="prev" title="Zope 3 General Tips and Tricks" href="z3.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="CHANGES.html" title="Changes"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="z3.html" title="Zope 3 General Tips and Tricks"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+          <li><a href="tips.html" accesskey="U">Tips and Tricks</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="zope-3-testing-tips-and-tricks">
+<h1 id="zope-3-testing-tips-and-tricks">Zope 3 Testing Tips and Tricks<a class="headerlink" href="#zope-3-testing-tips-and-tricks" title="Permalink to this headline">¶</a></h1>
+<div class="section" id="summary">
+<h2 id="summary">Summary<a class="headerlink" href="#summary" title="Permalink to this headline">¶</a></h2>
+<ul>
+<li><p class="first">Make sure you are using zope.app.testing version 3.4.2 or newer, or else
+ftests may intermittently raise spurious errors having to do with a missing
+<tt class="docutils literal"><span class="pre">_result</span></tt> attribute on a request&#8217;s response.</p>
+</li>
+<li><p class="first">The Zope 3 tests use DemoStorage, which does not use MVCC.  This can lead
+to your tests having occasional ConflictErrors that will not occur in
+production. In common cases for ftests, you won&#8217;t notice these because of
+Zope&#8217;s usual retry policy.  Unit or integration tests may show these
+problems.</p>
+</li>
+<li><p class="first">Set up the basic configuration in zcml or Python (see examples below), but
+you need to make sure that ftests do not use dispatchers started in the
+application. Start up ftest (or integration test) dispatchers separately,
+using <tt class="docutils literal"><span class="pre">zc.async.ftesting.setUp</span></tt>, and then tear down after the tests are
+done with <tt class="docutils literal"><span class="pre">zc.async.ftesting.tearDown</span></tt>.  The <tt class="docutils literal"><span class="pre">tearDown</span></tt> feature tries
+to shut down all threads, and tries to let you know what job was running in
+threads that couldn&#8217;t be cleanly stopped.</p>
+<p>The ftest dispatcher polls every tenth of a second, so you shouldn&#8217;t need to
+wait long for you job to get started in your tests.</p>
+</li>
+<li><p class="first">General zc.async testing tools such as <tt class="docutils literal"><span class="pre">zc.async.dispatcher.get</span></tt>,
+<tt class="docutils literal"><span class="pre">zc.async.testing.get_poll</span></tt> and <tt class="docutils literal"><span class="pre">zc.async.testing.wait_for_result</span></tt> can
+still be useful for in-depth zc.async tests.</p>
+</li>
+<li><p class="first">If you don&#8217;t want to dig into guts in your functional tests to use the tools
+described in the previous point, consider making a view to check on job
+status using a data structure like JSON, and looking at that in your tests.
+Alternatively, investigate the tools in monitordb.py&#8211;although the tools
+were created for zc.monitor, they can still be used effectively in Python.</p>
+</li>
+<li><p class="first">The <tt class="docutils literal"><span class="pre">setUp</span></tt> code by default sends critical log messages to __stdout__ so it
+can help diagnose why a callback might never complete.</p>
+</li>
+</ul>
+</div>
+<div class="section" id="discussion">
+<h2 id="discussion">Discussion<a class="headerlink" href="#discussion" title="Permalink to this headline">¶</a></h2>
+<p>Normally, in a Zope 3 configuration that uses zc.async, you configure it
+when you start your application.  For instance, you might include a zc.async
+zcml file like basic_dispatcher_policy.zcml that performs the necessary set up.</p>
+<p>However, the Zope 3 ftesting layer database dance doesn&#8217;t play well with
+zc.async unless you take a bit of extra care.</p>
+<p>This is because zc.async will be started with the ftests&#8217; underlying database,
+and then the test will be run with a DemoStorage wrapper. The zc.async
+dispatcher will run, then, but it will never see the changes that you make in
+the wrapper DemoStorage that your test manipulates.  This can be mystifying
+and frustrating.</p>
+<p>Because of this, when you write a Zope 3 app that wants to use both layered
+ftests and zc.async, you have to set things up in a mildly inconvenient way.</p>
+<p>When you start your application normally, use configuration (zcml or grok or
+whatever) to register subscribers like the ones in subscribers.py: adding
+queues, starting dispatchers, and adding agents.</p>
+<p>But don&#8217;t have this configuration registered for your ftests. Instead, bypass
+that part of your site&#8217;s configuration in your ftesting layer, and use the
+<tt class="docutils literal"><span class="pre">zc.async.ftesting.setUp</span></tt> function to set zc.async up in tests when you need
+it, in a footnote of your test or in a similar spot.</p>
+<p>You&#8217;ll still want the basic adapters registered, as found in zc.async&#8217;s
+configure.zcml or configure.py files; and maybe the
+zc.async.queue.getDefaultQueue adapter too. This can be registered in
+ftesting.zcml with this snippet:</p>
+<pre>&lt;include package="zc.async" /&gt;
+&lt;adapter factory="zc.async.queue.getDefaultQueue" /&gt;</pre>
+<p>Or in Python, you might want to do something like this:</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.configure</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">base</span><span class="p">()</span> <span class="c"># or, more likely, ``minimal`` for Zope 3</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.component</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.queue</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">provideAdapter</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">queue</span><span class="o">.</span><span class="n">getDefaultQueue</span><span class="p">)</span>
+</pre></div>
+<p>Don&#8217;t forget to call <tt class="docutils literal"><span class="pre">tearDown</span></tt> (see below) at the end of your test!</p>
+<p>Here&#8217;s a usage example.</p>
+<p>As mentioned above, <tt class="docutils literal"><span class="pre">setUp</span></tt> does expect the necessary basic adapters to
+already be installed.</p>
+<p>Zope 3 ftests generally have a <tt class="docutils literal"><span class="pre">getRootObject</span></tt> hanging around to give you the
+root object in the Zope application (but not in the ZODB). Therefore, this
+function tries to be helpful, for better and worse, and muck around in the
+locals to find it. If you want it to leave your locals alone, pass it a
+database connection.</p>
+<p>So, here&#8217;s some set up.  We create a database and make our stub
+<tt class="docutils literal"><span class="pre">getRootFolder</span></tt> function in the globals.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">BTrees</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">ZODB.FileStorage</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">storage</span> <span class="o">=</span> <span class="n">ZODB</span><span class="o">.</span><span class="n">FileStorage</span><span class="o">.</span><span class="n">FileStorage</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="s">&#39;zc_async.fs&#39;</span><span class="p">,</span> <span class="n">create</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">ZODB.DB</span> <span class="kn">import</span> <span class="n">DB</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">db</span> <span class="o">=</span> <span class="n">DB</span><span class="p">(</span><span class="n">storage</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">root</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">PseudoZopeRoot</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="s">&#39;Application&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">BTrees</span><span class="o">.</span><span class="n">family32</span><span class="o">.</span><span class="n">OO</span><span class="o">.</span><span class="n">BTree</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">_getRootObject</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">return</span> <span class="n">PseudoZopeRoot</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">globals</span><span class="p">()[</span><span class="s">&#39;getRootFolder&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">_getRootObject</span>
+</pre></div>
+<p>Notice we are using a real FileStorage, and not a DemoStorage, as is usually
+used in ftests. The fact that DemoStorage does not have MVCC can sometimes lead
+standard ftests to raise spurious ReadConflictErrors that will not actually
+occur in production. The ConflictErrors will generally be retried, so your
+tests should usually pass, even though you might see some &#8220;complaints&#8221;.</p>
+<p>Now we can call <tt class="docutils literal"><span class="pre">setUp</span></tt> as if we were in a functional test.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.ftesting</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">ftesting</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
+</pre></div>
+<p>Now the dispatcher is activated and the polls are running. The function sets up
+a dispatcher that polls much more frequently than usual&#8211;every 0.1 seconds
+rather than every 5, so that tests might run faster&#8211;but otherwise uses typical
+zc.async default values.</p>
+<p>It&#8217;s worth noting a few tricks that are particularly useful for tests here.
+We&#8217;ll also use a couple of them to verify that <tt class="docutils literal"><span class="pre">setUp</span></tt> did its work.</p>
+<p><tt class="docutils literal"><span class="pre">zc.async.dispatcher.get()</span></tt> returns the currently installed dispatcher. This
+can let you check if it is activated and polling and use its simple statistical
+methods, if you want.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.dispatcher</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+</pre></div>
+<p>For now, we&#8217;ll just see that the dispatcher is activated.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">True</span>
+</pre></div>
+<p>See the dispatcher.txt for information on information you can get from the
+dispatcher object.</p>
+<p>zc.async.testing has a number of helpful functions for testing. <tt class="docutils literal"><span class="pre">get_poll</span></tt> is
+the most pertinent here: given a dispatcher, it will give you the next poll.
+This is a good way to make sure that a job you just put in has had a chance to
+be claimed by a dispatcher.  It&#8217;s also a reasonable way to verify that the
+dispatcher has started.  <tt class="docutils literal"><span class="pre">setUp</span></tt> already gets the first two polls, so
+it&#8217;s definitely all started.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.testing</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">pprint</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">pprint</span><span class="o">.</span><span class="n">pprint</span><span class="p">(</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">get_poll</span><span class="p">(</span><span class="n">dispatcher</span><span class="p">))</span>
+<span class="go">{&#39;&#39;: {&#39;main&#39;: {&#39;active jobs&#39;: [],</span>
+<span class="go">               &#39;error&#39;: None,</span>
+<span class="go">               &#39;len&#39;: 0,</span>
+<span class="go">               &#39;new jobs&#39;: [],</span>
+<span class="go">               &#39;size&#39;: 3}}}</span>
+</pre></div>
+<p>Other useful testing functions are <tt class="docutils literal"><span class="pre">zc.async.testing.wait_for_result</span></tt>, which
+waits for the result on a give job and returns it; and
+<tt class="docutils literal"><span class="pre">zc.async.testing.wait_for_annotation</span></tt>, which waits for a given annotation
+on a given job.  These are demonstrated in various doctests in this package,
+but should also be reasonably simple and self-explanatory.</p>
+<p>Callbacks will retry some errors forever, by default.  The logic is that
+callbacks are often the &#8220;cleanup&#8221; and must be run.  This can lead to confusion
+in debugging tests, though, because the retry warnings are sent to the log,
+and the log is not usually monitored in functional tests.</p>
+<p><tt class="docutils literal"><span class="pre">setUp</span></tt> tries to help with this by adding logging of <tt class="docutils literal"><span class="pre">CRITICAL</span></tt> log
+messages in the &#8220;zc.async&#8221; logger to stdout.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">logging</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s">&#39;zc.async.event&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">critical</span><span class="p">(</span><span class="s">&#39;Foo!&#39;</span><span class="p">)</span>
+<span class="go">Foo!</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s">&#39;zc.async.event&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s">&#39;Bar!&#39;</span><span class="p">)</span>
+</pre></div>
+<p>Once you have finished your tests, make sure to shut down your dispatcher, or
+the testing framework will complain about an unstopped daemon thread.
+zc.async.ftesting.tearDown will do the trick.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">ftesting</span><span class="o">.</span><span class="n">tearDown</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">activated</span>
+<span class="go">False</span>
+</pre></div>
+<p>You can then start another async-enabled functional test up again later in the
+same layer, of course.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">ftesting</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="nb">bool</span><span class="p">(</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">activated</span><span class="p">)</span>
+<span class="go">True</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">ftesting</span><span class="o">.</span><span class="n">tearDown</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">activated</span>
+<span class="go">False</span>
+</pre></div>
+</blockquote>
+<p>ftesting.tearDown attempts to join all threads in the dispatchers&#8217; queues, but
+will raise an error if a job or dispatcher fails to shut down.</p>
+<p>If the thread is performing a job, the error informs you what job is being
+performed.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">ftesting</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">_</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">begin</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">queue</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">KEY</span><span class="p">][</span><span class="s">&#39;&#39;</span><span class="p">]</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">bad_job</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">time_sleep</span><span class="p">(</span><span class="mf">4</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">job</span> <span class="o">=</span> <span class="n">queue</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">bad_job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_start</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">ftesting</span><span class="o">.</span><span class="n">tearDown</span><span class="p">()</span> <span class="c"># doctest: +ELLIPSIS +NORMALIZE_WHITESPACE</span>
+<span class="gp">...</span>
+<span class="go">TearDownDispatcherError:</span>
+<span class="go">Job in pool &#39;main&#39; failed to stop:</span>
+<span class="go">  &lt;zc.async.job.Job (oid ..., db ...) ``zc.async.doctest_test.bad_job()``&gt;</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">wait_for_result</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
+</pre></div>
+<p>If the dispatcher isn&#8217;t shutting down for some reason, the UUID is given.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">ftesting</span><span class="o">.</span><span class="n">tearDown</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">ftesting</span><span class="o">.</span><span class="n">setUp</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">dispatcher</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">noop</span><span class="p">(</span><span class="o">*</span><span class="n">kw</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">pass</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">original_stop</span> <span class="o">=</span> <span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">stop</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">stop</span> <span class="o">=</span> <span class="n">noop</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">ftesting</span><span class="o">.</span><span class="n">tearDown</span><span class="p">()</span> <span class="c"># doctest: +ELLIPSIS +NORMALIZE_WHITESPACE</span>
+<span class="gp">...</span>
+<span class="go">TearDownDispatcherError:</span>
+<span class="go">Dispatcher (..., ...) failed to stop.</span>
+</pre></div>
+<p>Let&#8217;s restore the original reactor.stop method and call tearDown again, which
+will work this time.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">dispatcher</span><span class="o">.</span><span class="n">reactor</span><span class="o">.</span><span class="n">stop</span> <span class="o">=</span> <span class="n">original_stop</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">ftesting</span><span class="o">.</span><span class="n">tearDown</span><span class="p">()</span>
+</pre></div>
+<p>Also worth noting, as mentioned in the summary, is that you should use
+zope.app.testing version 3.4.2 or higher to avoid getting spurious,
+intermittent bug reports from ftests that use zc.async.</p>
+<p>In your test or your test&#8217;s tearDown, if you used a FileStorage, as we did
+here, you&#8217;ll need to clean up as well.  We normally do this in our tests&#8217;
+tearDowns, but we do it here, now, to make the point.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">db</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">storage</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">storage</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">del</span> <span class="n">storage</span> <span class="c"># we just do this to not confuse our own tearDown code.</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">del</span> <span class="nb">globals</span><span class="p">()[</span><span class="s">&#39;getRootFolder&#39;</span><span class="p">]</span> <span class="c"># clean up globals; probably unnecessary</span>
+</pre></div>
+</blockquote>
+</div>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Table Of Contents</h3>
+            <ul>
+<li><a class="reference external" href="">Zope 3 Testing Tips and Tricks</a><ul>
+<li><a class="reference external" href="#summary">Summary</a></li>
+<li><a class="reference external" href="#discussion">Discussion</a></li>
+</ul>
+</li>
+</ul>
+
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="z3.html" title="previous chapter">Zope 3 General Tips and Tricks</a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="CHANGES.html" title="next chapter">Changes</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/ftesting.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="CHANGES.html" title="Changes"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="z3.html" title="Zope 3 General Tips and Tricks"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+          <li><a href="tips.html" accesskey="U">Tips and Tricks</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/genindex.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/genindex.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/genindex.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,89 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Index &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+
+   <h1 id="index">Index</h1>
+
+   
+
+   <hr />
+
+   
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+
+
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/index.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/index.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/index.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,258 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>zc.async &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="" />
+    <link rel="next" title="Quickstart with virtualenv" href="QUICKSTART_1_VIRTUALENV.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="QUICKSTART_1_VIRTUALENV.html" title="Quickstart with virtualenv"
+             accesskey="N">next</a> |</li>
+        <li><a href="">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="async">
+<h1 id="async"><tt class="docutils literal"><span class="pre">zc.async</span></tt><a class="headerlink" href="#async" title="Permalink to this headline">¶</a></h1>
+<div class="section" id="what-is-it">
+<h2 id="what-is-it">What is it?<a class="headerlink" href="#what-is-it" title="Permalink to this headline">¶</a></h2>
+<p>The <a class="reference external" href="http://pypi.python.org/pypi/zc.async"><tt class="docutils literal"><span class="pre">zc.async</span></tt></a> package provides <strong>an easy-to-use Python tool that schedules
+durable tasks across multiple processes and machines.</strong></p>
+<p>For instance...</p>
+<ul>
+<li><p class="first"><em>Web apps</em>:</p>
+<p>maybe your web application lets users request the creation of a large PDF, or
+some other expensive task.</p>
+</li>
+<li><p class="first"><em>Postponed work</em>:</p>
+<p>maybe you have a job that needs to be done at a certain time, not right now.</p>
+</li>
+<li><p class="first"><em>Parallel processing</em>:</p>
+<p>maybe you have a long-running problem that can be made to complete faster by
+splitting it up into discrete parts, each performed in parallel, across
+multiple machines.</p>
+</li>
+<li><p class="first"><em>Serial processing</em>:</p>
+<p>maybe you want to decompose and serialize a job.</p>
+</li>
+</ul>
+<p>High-level features include the following.</p>
+<ul>
+<li><p class="first"><strong>Easy to use.</strong></p>
+<p>At its simplest, put a function in a <tt class="docutils literal"><span class="pre">zc.async</span></tt> queue and commit a
+transaction.  See the quick-starts for examples.</p>
+</li>
+<li><p class="first"><strong>Flexible configuration, changeable dynamically in production.</strong></p>
+<p>Add and remove worker processes on the fly, with configurable policy on how
+to handle interrupts.  Let processes decide how many of which tasks to
+perform.  Configuration for each process is stored in the database so no
+restarts are needed and a change can happen for any process from any
+database client.</p>
+</li>
+<li><p class="first"><strong>Reliable and fault tolerant, supporting high availability.</strong></p>
+<p>Configurable policy lets <tt class="docutils literal"><span class="pre">zc.async</span></tt> know when, how, and under what circumstances
+to retry jobs that encounter problems.  Multiple processes and machines can
+be available to work on jobs, and a machine or process that suddenly dies
+lets siblings decide what to do with incomplete jobs, with policy on a
+per-job basis.  The central <a class="reference external" href="http://pypi.python.org/pypi/ZODB3">ZODB</a> database server can be replicated with
+commercial tools (<a class="reference external" href="http://www.zope.com/products/zope_replication_services.html">ZRS</a>) or open-source tools (<a class="reference external" href="http://wiki.zope.org/ZODB/RelStorage">RelStorage</a> plus, for instance
+PostgreSQL and slony; or <a class="reference external" href="http://pypi.python.org/pypi/gocept.zeoraid">gocept.zeoraid</a>).</p>
+</li>
+<li><p class="first"><strong>Good debugging tools.</strong></p>
+<p>Exceptions generate persistent <tt class="docutils literal"><span class="pre">Failure</span></tt> objects (from the <a class="reference external" href="http://pypi.python.org/pypi/Twisted">Twisted</a>
+project) for analysis, and verbose log messages.</p>
+</li>
+<li><p class="first"><strong>Well-tested.</strong></p>
+<p>The package has good automated tests and is in use in mission-critical
+applications for large software deployments.</p>
+</li>
+<li><p class="first"><strong>Friendly to testing.</strong></p>
+<p>The package exposes testing helpers for a variety of circumstances, to make
+writing automated tests for zc.async-enabled software fairly painless.</p>
+</li>
+</ul>
+<p>While developed as part of the Zope project, zc.async can be used stand-alone,
+as seen in the quick-starts and the majority of the tests.</p>
+</div>
+<div class="section" id="how-does-it-work">
+<h2 id="how-does-it-work">How does it work?<a class="headerlink" href="#how-does-it-work" title="Permalink to this headline">¶</a></h2>
+<p>The system uses the Zope Object Database (<a class="reference external" href="http://pypi.python.org/pypi/ZODB3">ZODB</a>), a transactional, pickle-based
+Python object database, for communication and coordination among participating
+processes.</p>
+<p><tt class="docutils literal"><span class="pre">zc.async</span></tt> participants can each run in their own process, or share a process
+(run in threads) with other code.</p>
+<p>The <a class="reference external" href="http://pypi.python.org/pypi/Twisted">Twisted</a> framework supplies some code (failures and reactor
+implementations, primarily) and some concepts to the package.</p>
+</div>
+<div class="section" id="quick-starts">
+<h2 id="quick-starts">Quick starts<a class="headerlink" href="#quick-starts" title="Permalink to this headline">¶</a></h2>
+<p>These quick-starts can help you get a feel for the package.  <strong>Please note:
+the Grok quickstart is only just begun, and should be regarded mostly
+as a placeholder.</strong></p>
+<ul>
+<li><a class="reference external" href="QUICKSTART_1_VIRTUALENV.html">Quickstart with <tt class="docutils literal"><span class="pre">virtualenv</span></tt></a></li>
+</ul>
+<ul>
+<li><a class="reference external" href="QUICKSTART_2_GROK.html">Quickstart with Grok</a></li>
+</ul>
+</div>
+<div class="section" id="documentation">
+<h2 id="documentation">Documentation<a class="headerlink" href="#documentation" title="Permalink to this headline">¶</a></h2>
+<p>Contents:</p>
+<ul>
+<li><a class="reference external" href="README.html">Introduction</a><ul>
+<li><a class="reference external" href="README.html#goals">Goals</a></li>
+<li><a class="reference external" href="README.html#history">History</a></li>
+<li><a class="reference external" href="README.html#design-overview">Design Overview</a></li>
+</ul>
+</li>
+</ul>
+<ul>
+<li><a class="reference external" href="README_1.html">Usage</a><ul>
+<li><a class="reference external" href="README_1.html#overview-and-basics">Overview and Basics</a></li>
+<li><a class="reference external" href="README_1.html#jobs">Jobs</a></li>
+<li><a class="reference external" href="README_1.html#advanced-techniques-and-tools">Advanced Techniques and Tools</a></li>
+<li><a class="reference external" href="README_1.html#conclusion">Conclusion</a></li>
+</ul>
+</li>
+</ul>
+<ul>
+<li><a class="reference external" href="README_2.html">Configuration (without Zope 3)</a><ul>
+<li><a class="reference external" href="README_2.html#configuring-a-client-process">Configuring a Client Process</a></li>
+<li><a class="reference external" href="README_2.html#configuring-a-client-server-process">Configuring a Client/Server Process</a></li>
+</ul>
+</li>
+</ul>
+<ul>
+<li><a class="reference external" href="README_3.html">Configuration with Zope 3</a><ul>
+<li><a class="reference external" href="README_3.html#client-set-up">Client Set Up</a></li>
+<li><a class="reference external" href="README_3.html#client-server-set-up">Client/Server Set Up</a></li>
+</ul>
+</li>
+</ul>
+<ul>
+<li><a class="reference external" href="tips.html">Tips and Tricks</a><ul>
+<li><a class="reference external" href="tips.html#general-tips-and-tricks">General Tips and Tricks</a></li>
+<li><a class="reference external" href="tips.html#testing-tips-and-tricks">Testing Tips and Tricks</a></li>
+<li><a class="reference external" href="tips.html#more-tips-and-tricks">More Tips and Tricks</a></li>
+</ul>
+</li>
+</ul>
+<ul>
+<li><a class="reference external" href="CHANGES.html">Changes</a><ul>
+<li><a class="reference external" href="CHANGES.html#id1">1.5.0 (2008-09-21)</a></li>
+<li><a class="reference external" href="CHANGES.html#id2">1.4.1 (2008-07-30)</a></li>
+<li><a class="reference external" href="CHANGES.html#id3">1.4.0 (2008-07-30)</a></li>
+<li><a class="reference external" href="CHANGES.html#id4">1.3 (2008-07-04)</a></li>
+<li><a class="reference external" href="CHANGES.html#id5">1.2 (2008-06-20)</a></li>
+<li><a class="reference external" href="CHANGES.html#id6">1.1.1 (2008-05-14)</a></li>
+<li><a class="reference external" href="CHANGES.html#id7">1.1 (2008-04-24)</a></li>
+<li><a class="reference external" href="CHANGES.html#id8">1.0 (2008-04-09)</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+<div class="section" id="indices-and-tables">
+<h1 id="indices-and-tables">Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this headline">¶</a></h1>
+<ul class="simple">
+<li><a class="reference external" href="genindex.html"><em>Index</em></a></li>
+<li><a class="reference external" href="modindex.html"><em>Module Index</em></a></li>
+<li><a class="reference external" href="search.html"><em>Search Page</em></a></li>
+</ul>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Table Of Contents</h3>
+            <ul>
+<li><a class="reference external" href=""><tt class="docutils literal"><span class="pre">zc.async</span></tt></a><ul>
+<li><a class="reference external" href="#what-is-it">What is it?</a></li>
+<li><a class="reference external" href="#how-does-it-work">How does it work?</a></li>
+<li><a class="reference external" href="#quick-starts">Quick starts</a><ul>
+</ul>
+</li>
+<li><a class="reference external" href="#documentation">Documentation</a><ul>
+</ul>
+</li>
+</ul>
+</li>
+<li><a class="reference external" href="#indices-and-tables">Indices and tables</a></li>
+</ul>
+
+            <h4>Next topic</h4>
+            <p class="topless"><a href="QUICKSTART_1_VIRTUALENV.html" title="next chapter">Quickstart with <tt class="docutils literal"><span class="pre">virtualenv</span></tt></a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/index.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="QUICKSTART_1_VIRTUALENV.html" title="Quickstart with virtualenv"
+             accesskey="N">next</a> |</li>
+        <li><a href="">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/modindex.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/modindex.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/modindex.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,89 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Global Module Index &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+
+
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+
+   <h1 id="global-module-index">Global Module Index</h1>
+
+
+   <hr/>
+
+   <table width="100%" class="indextable" cellspacing="0" cellpadding="2">
+   </table>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/search.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/search.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/search.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,89 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Search &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <script type="text/javascript" src="_static/searchtools.js"></script>
+
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  <h1 id="search-documentation">Search</h1>
+  <p>
+    From here you can search these documents. Enter your search
+    words into the box below and click "search". Note that the search
+    function will automatically search for all of the words. Pages
+    containing less words won't appear in the result list.
+  </p>
+  <form action="" method="get">
+    <input type="text" name="q" value="" />
+    <input type="submit" value="search" />
+  </form>
+  
+  <div id="search-results">
+  
+  </div>
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/searchindex.json
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/searchindex.json	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/searchindex.json	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1 @@
+[["index","ftesting","CHANGES","catastrophes","z3","QUICKSTART_1_VIRTUALENV","README_3","README_2","README_1","README","tips","README_3b","README_3a","QUICKSTART_2_GROK"],["<tt class=\"docutils literal\"><span class=\"pre\">zc.async</span></tt>","Zope 3 Testing Tips and Tricks","Changes","Recovering from Catastrophes","Zope 3 General Tips and Tricks","Quickstart with <tt class=\"docutils literal\"><span class=\"pre\">virtualenv</span></tt>","Configuration with Zope 3","Configuration (without Zope 3)","Usage","Introduction","Tips and Tricks","Two Database Set Up","Shared Single Database Set Up","Quickstart with Grok"],{"four":[7,3],"prefix":[13],"sleep":[12,3,8],"dirnam":[12,11],"databaseopen":[11,12,7,3],"forget":[1,10],"wait_for_annot":[1,2,10,8],"lgpl":[5],"pprint":[1,12,3,8,11],"under":[9,0,5,8],"intermediate_valu":[8],"worth":[1,3,7,8,13],"retry_polici":[3],"everi":[1,5,3,10],"runzeo":[5],"jack":[4],"affect":[10,2,7,8],"stubsit":[4],"handout":[],"zlib":[13],"naiv":[9],"d
 irect":[5,2],"second":[1,2,5,6,3,8,9,10,12],"aggreg":[5,7,8],"zasyncdispatch":[9],"illumin":[3],"even":[1,5,6,7,8,9,11,3],"deferredimport":[5,7],"neg":[2],"asid":[13],"blur":[9],"new":[1,2,4,5,3,9,11,12,13],"net":[2],"ever":[5,3,8],"quickstart_grok":[],"elimin":[2,8],"behavior":[2,3,8],"never":[1,3,8,10],"functiontyp":[7],"here":[1,5,3,8,9,13,12,7],"met":[9],"generatesampl":[],"readme_3":[2,7],"debugg":[13],"path":[2,5,12,11,13],"interpret":[5,2,13],"precis":[8],"datetim":[5,2,3,8,4],"ziad":[9],"reportoncontext":[4],"propag":[12,11],"time_pass":[8],"brought":[5],"unix":[5],"1386666666666665":[5],"txt":[1,2,5,7,9,11,12,3],"unit":[1],"uuid_hex":[7],"describ":[1,2,5,6,7,8,10,11,12,3],"would":[2,5,7,8,9,4,12,11],"chooser":[5,2,3],"stubpersistentauth":[4],"call":[1,5,3,8,9,13,2,7],"recommend":[13],"type":[9,5,2,7,13],"until":[10,5,3,8],"relat":[10,3],"notic":[1,10,7,8,3],"warn":[1,2,8],"ware":[2],"unpack":[2],"must":[1,7,8,9,2,3],"join":[1,2,5,7,8,11,12,3],"restor":[1],"setup":[1
 ,5,6,7,4,2,13],"work":[0,1,2,3,4,5,7,8,9,10,11,12,13],"mv":[5],"root":[1,4,7,8,9,11,12,3],"unnam":[8],"overrid":[7],"defer":[9,7,8],"give":[1,5,7,8,9,10,2,3],"flung":[6],"indic":[0,5,2],"unavail":[3],"want":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"addcallback":[5,2,3,8],"mypypi":[13],"end":[1,5,6,7,8,9,10,4,2,3],"turn":[5,8],"how":[0,5,3,8,9,13,2,7],"recoveri":[3],"answer":[3],"verifi":[1,5,10],"perspect":[9,8],"collaps":[8],"recogn":[5,2],"timedelta":[5,2,3,8],"mess":[3],"after":[1,5,3,8,9,10,4,2,13],"befor":[11,5,3,8,9,10,13,2,7],"wrong":[9,10,3,8],"retrycommonforev":[10,5,2,3],"parallel":[9,0,5,2,8],"demonstr":[1,5,7,8,10,3],"attempt":[1,3,8],"third":[8],"sitemanagercontain":[4],"obsolet":[2],"maintain":[4],"environ":[5,12,7,11],"incorpor":[2],"enter":[5,3],"lambda":[5,8],"order":[5,8],"origin":[9,1,3,8],"verg":[8],"softwar":[0,5,3,9,13,7],"diagnos":[1,10],"over":[3,7,8,9,2,11],"failur":[0,5,3,8,10,2,4],"zeo":[13,5,3,9,11,2,7],"fifo":[7,8],"flexibl":[0],"waitforparallel":[8],"2
 57657":[2],"uuid":[1,2,5,7,8,9,11,12,3],"fix":[9,2,3],"__class__":[4],"better":[1,5],"persist":[0,5,6,7,8,9,4,12,11],"hidden":[2],"easier":[9,7],"then":[1,4,5,3,8,9,10,13,2,7],"them":[1,5,7,8,9,10,2,3],"thei":[1,5,3,8,9,13,2,7],"safe":[8],"break":[12,3,8,11],"band":[9],"callwhenrun":[5],"getdefaultqueu":[1,5,7],"interrupt":[0,5,2,3],"choic":[2],"l":[13],"accommod":[7],"timeout":[8],"each":[0,5,3,9,13,2,7],"debug":[0,1,2,5,13,12,11],"went":[8],"complet":[0,1,5,7,8,9,2,3],"side":[7],"luck":[11],"resum":[3],"calllat":[5],"spider":[9],"hjdgjhrhnulzzwqhrgoxu2k":[5],"whip":[2],"network":[9,5,7,8],"goe":[9,10,3,8],"5000000":[5],"content":[9,0,5,7,13],"re":[5,12,7,8,3],"ijob":[7,8],"adapt":[1,2,5,6,7,8,12,11],"reader":[12,8],"rm":[13],"wait_repeatedli":[8],"worthwhil":[9],"last_p":[2,3],"situat":[9,10,2,3,8],"free":[9],"standard":[9,1,5,7],"reconfigur":[5],"traceback":[1,2,7,8,3],"openssl":[5],"filter":[2,7,8],"unabl":[3],"subtl":[7],"confus":[1,3,13],"rang":[5,12,3,8,11],"hook":[10
 ,5,2,7,4],"instruct":[11,12,13],"naughti":[2],"messag":[0,1,2,10,8],"wasn":[9],"2008c":[5],"primari":[6,7,8,13],"their":[0,2,5,7,8,9,12,13],"top":[5,2,8],"sometim":[1,5,3,8,10,4],"downsid":[7],"toi":[13],"too":[1,5,7,8,12,3],"similarli":[5],"demostorag":[1,10],"namespac":[12,11],"tool":[0,1,2,5,7,8,11,12,3],"setuptool":[5,7,13],"took":[5],"zeoraid":[0],"somewhat":[8,13],"alreadi":[1,5,6,7,8,2,3],"unind":[],"silli":[5],"target":[5,2,7,8],"keyword":[5,8],"provid":[0,5,7,8,9,2],"tree":[13],"project":[0,5,7,13],"matter":[5,3],"minut":[5,2,8],"beginn":[13],"ran":[5,3],"pass":[1,12,5,8,4,2,11],"mind":[8],"multidatabas":[7],"manner":[5],"increment":[2],"__main__":[5],"seen":[0,5,11],"seem":[3,7,13],"incompat":[2],"recreat":[5],"that":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"raisedisconnectedthenchoosefirst":[3],"simplifi":[2],"though":[1,5,7,8,9,11],"object":[0,1,5,7,8,9,10,4,2,3],"what":[0,1,5,3,8,9,10,13,2,7],"regular":[9,8],"monkeypatch":[3,8],"bsd":[5],"the":[0,1,2,3,4,5,6,7,8,9,10,1
 1,12,13],"simplic":[13],"don":[1,5,7,10,2,3],"doc":[2,8],"lift":[7],"doe":[0,1,7,8,9,10,4,2,3],"declar":[],"section":[5,6,7,8,10,2],"explan":[5],"introspect":[10,5,2,8],"reactor":[0,1,2,5,7,8,9,11,12,3],"abl":[9,5,7,8,3],"random":[5,12,11],"subtli":[9],"satchit":[2],"protocol":[2],"involv":[9,10],"absolut":[9],"install_ag":[5],"acquir":[3],"resumecallback":[2],"configur":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"stanza":[12,11],"sugar":[8],"ldap":[9],"tzinfo":[8],"do":[0,1,2,3,5,6,7,8,9,10,4,12,13],"di":[0,3],"stop":[1,2,5,7,8,11,12,3],"da":[12,3,8],"dy":[3],"report":[9,1,2,3],"callback2":[8],"old_sleep":[3],"bar":[1,8],"monitordb":[1,5,2,7],"changeabl":[0],"patch":[],"cleanli":[1,7,8],"bad":[7,3],"gaug":[9],"respond":[],"mkdtemp":[12,11],"result":[1,2,5,7,8,9,10,11,12,3],"respons":[1,5,3,8,9,2],"fail":[1,2,5,7,8,12,3],"best":[5,2],"awar":[10,5,7],"01":[3],"06":[0,2],"07":[0,2],"04":[0,2],"05":[0,5,2],"databas":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"09":[0,2],"sigint":[12],"figur":[7],
 "mvcc":[1,5,7,8,10,2],"mul":[4,12,7,11],"awai":[10,2,3,8],"approach":[11,5,3,8,9,13,2,7],"attribut":[1,5,7,8,4,2,3],"we":[1,2,3,4,5,6,7,8,11,12,13],"extend":[9,8],"were":[9,1,2,7,8],"provideutil":[5,7],"extens":[9,2,7],"getsit":[4],"functool":[5],"toler":[0],"protect":[5,2],"mildli":[1],"fault":[0],"howev":[1,3,7,8,9,4],"setdaemon":[7],"logic":[1,7,3],"and":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"ez_setup":[13],"sai":[5,7],"trickiest":[7],"tarek":[9],"guid":[5,13],"assum":[5,12,8,13],"n2003":[],"duplic":[2],"three":[9,5,7,8,3],"been":[12,5,7,8,2,4],"muck":[1],"much":[2,1,5,6,10],"basic":[0,1,5,7,8,9,2,3],"quickli":[9,2,6,7],"0017f2c49bdd":[5],"xxx":[3,13],"worker":[0,5,7,8,9,2,3],"telnet":[5,12],"argument":[5,2,8],"pytz":[5,7,8],"child":[9,7],"catch":[5,8],"ugli":[13],"ident":[7],"servic":[2,7,8,3],"properti":[2],"commerci":[0],"frobnitz":[7],"n":[12],"aim":[13],"calcul":[5],"seven":[9,5],"p64":[2],"systemexit":[3],"is":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"it":[0,1,2,3,4,5,6,7,8,9,
 10,11,12,13],"conf":[9,6,5,12,11],"in":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"printtraceback":[10,8],"id":[8,4],"sever":[9,5,2,13],"if":[1,2,3,4,5,6,7,8,9,10,11,12,13],"filestorag":[1,5,7,8,10,11,12,3],"credit":[9,3],"perform":[0,1,2,5,7,8,9,11,12,3],"suggest":[10,7,13],"make":[0,1,3,5,6,7,8,9,10,2,13],"complex":[8,13],"split":[0],"getliveannot":[5,8],"transacton":[],"hang":[1],"hand":[2,12,8],"fairli":[0,5,7],"rais":[1,5,3,8,12,11],"portal":[13],"methodtyp":[7],"redefin":[2],"scenario":[10,4,7,3],"inherit":[5,2,7],"client":[0,5,6,7,9,2,3],"thi":[1,2,3,4,5,6,7,8,9,10,11,12,13],"everyth":[7,8,3],"left":[2,12,3],"_result":[1],"identifi":[9,5,7,8,4],"just":[0,1,3,5,6,7,8,10,2,13],"queue_pool":[8],"callback1":[8],"yet":[9,5,6,3,8],"easi":[0,5,6,7,13,2,11],"had":[1,3,8,9,10,2],"spread":[9],"els":[1,5,3,8,12,11],"save":[5],"explanatori":[1,5,10],"opt":[13],"applic":[0,1,5,6,7,9,11,12,13],"afd1e0d0":[5],"supervisord":[5,7,3],"mayb":[0,1,5,3,8],"pseudozoperoot":[1],"database_nam":[7],"d
 aemon":[1,5,7,3],"specif":[5,3,8,9,10,13,2,7],"arbitrari":[5,7],"54":[8],"56":[8],"manual":[13],"52":[8],"reassign":[2,8],"install_requir":[6],"sebastian":[2],"unnecessari":[1,2,13],"underli":[1,7],"www":[5,13],"right":[0,7],"old":[5,2,7,3],"deal":[3,8],"interv":[12,3],"somehow":[3],"dead":[2,3],"9999":[5],"intern":[3],"hypot":[5],"9991":[5],"mappingstorag":[2,8],"indirect":[7],"successfulli":[3],"total":[5],"cooper":[8],"for":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"subclass":[5,2,7,8,4],"active_end":[5],"condit":[2],"foo":[1,8,4],"localhost":[5],"getgwd":[5],"core":[9,5,8],"plu":[9,0,3,7,13],"async_storag":[7],"postgresql":[0,13],"slightli":[2,8],"unfortun":[5,6,4,13],"telnetlib":[12],"commit":[0,1,2,4,5,7,8,10,11,12,3],"produc":[5,2,13],"unrespons":[9],"bforest":[5,2,7],"curiou":[5],"float":[8],"bound":[3],"down":[1,2,5,7,8,4,12,3],"wrap":[8],"opportun":[5,7],"storag":[1,2,5,7,8,10,11,12,3],"neverretri":[10,5,3],"wai":[1,2,5,3,8,9,10,13,12,7],"frustrat":[1],"support":[0,5,7,8,9
 ,10,2,3],"class":[0,5,7,8,2,4],"avail":[0,11,5,3,8,13,12,7],"getrootfold":[1],"overhead":[5],"analysi":[0],"genrsa":[5],"offer":[5,8],"forc":[9],"iobjectev":[2],"quickstart_1_virtualenv":[2],"true":[1,11,2,4,5,3,8,13,12,7],"bugfix":[2],"maximum":[2],"reiter":[8],"emit":[10],"featur":[0,1,5,7,8,10,2],"classic":[7],"processsampl":[],"backoff":[2,3],"exist":[5,7],"glanc":[5,7],"ship":[3],"check":[1,5,3,8,10,2],"assembl":[8],"9992":[5],"no":[0,2,5,7,8,9,11,12,3],"when":[0,1,5,7,8,9,4,2,3],"refactor":[7],"role":[9],"keyrefer":[6,12,7,11],"test":[0,1,2,3,4,5,7,8,9,10,11,12,13],"49151":[12,11],"roll":[7],"intend":[5,2,7],"consid":[1,2,3,8,13],"print_log":[2],"src":[13],"mathew":[],"longer":[9,2,3],"bullet":[2],"stubmodul":[],"1216179006":[5],"ignor":[12,5,7,8,2,11],"time":[0,1,2,3,5,7,8,9,10,12,13],"push":[2],"backward":[9,2],"wait_for_death":[3],"zope3":[2],"concept":[0,6],"chain":[5,8],"macosx":[5],"focus":[5,7],"signific":[9],"nuxeo":[9],"decid":[0,5,2,7,3],"middl":[3,8],"depend
 ":[2,5,6,7,8],"zone":[7],"intermedi":[9,2],"environment":[5,6,7],"0":[0,1,2,5,7,8,10,11,12,3],"certainli":[2,3],"decis":[3,7,8,13],"isinst":[8],"sourc":[0,7,13],"string":[2,7,8],"cook":[8],"cool":[5],"builtin_function_or_method":[2],"level":[0,2,3,5,7,12,11],"tear":[1,5,2,4],"die":[10,3],"dig":[1],"iter":[9],"item":[7],"team":[8],"quick":[0,5,7,8,9,10,2,13],"dir":[12,11],"prevent":[7,8],"brave":[5],"slower":[5,13],"pickleabl":[9,5,8],"performspid":[9],"discover":[9],"btree":[1,7],"port":[5,12,11,13],"repli":[5],"havoc":[10],"current":[1,4,5,3,8,9,13,2,7],"wors":[1],"suspect":[3],"deriv":[7],"gener":[0,1,4,5,3,8,9,10,13,2,7],"satisfi":[4],"normalize_whitespac":[1,12,3,8],"explicitli":[5,2,7,3],"address":[9,2,3],"along":[5,6,8],"wait":[1,5,7,8,10,11,12,3],"box":[13],"my":[5,13],"queue":[0,1,2,5,6,7,8,9,10,11,12,3],"choosefirst":[2,3],"behav":[5,2,3],"ourselv":[5,7],"extra":[2,1,5,6,13],"commiterror":[3],"tweak":[2],"modul":[0,5,7,8,10,2],"prefer":[12,5,7,8,2,11],"moduletyp":[5
 ],"wreak":[10],"visibl":[9],"instal":[1,5,3,8,9,13,2,7],"memori":[2,8],"univers":[9],"live":[7],"handler":[7],"completestartedjobargu":[2],"examin":[2,3,8],"easiest":[5,13],"behalf":[7],"dqliczh3bhunhflaovw6wxbnklenq":[5],"fly":[9,0,7],"judg":[8],"uniqu":[9,5,7],"cat":[5],"descriptor":[7],"can":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"purpos":[5,2,7,3],"iuuid":[12,7,8],"problemat":[3],"claim":[1,5,7,8,9,10,2,3],"predict":[9,2,3],"agent":[1,5,7,8,9,2,3],"topic":[5],"critic":[0,1,5,3,9,10,2],"abort":[5,2,3,8,4],"500000":[],"simul":[5,3,8],"occur":[1,3],"alwai":[5,2,7,8],"multipl":[0,5,7,8,9,10,13],"ping":[2,3],"uptim":[12],"write":[0,1,2,3,5,6,7,8,10,11,12,13],"pure":[7,13],"map":[9,5,7,8],"product":[0,1,5,6,7,8,9,11,12,13],"disagre":[10],"spot":[1],"usabl":[7],"lock2":[3],"aou":[],"lock1":[3],"mai":[1,5,3,8,9,10,13,7],"such":[1,2,5,3,8,9,10,13,12,7],"data":[1,5,6,7,8,9,2,3],"man":[13],"sy":[5,8],"usag":[0,1,2,6,7,8,9,12,11],"practic":[2,8],"divid":[9,10,5,8],"explicit":[],"q":[5],"
 inform":[1,5,3,8,9,2],"switch":[5,2,7],"combin":[9,5,6],"sm":[4],"callabl":[9,5,7,8,3],"talk":[],"cute":[13],"petkov":[2],"approv":[2],"imaginarynetworkcal":[8],"sleep_request":[3],"localsitemanag":[4],"still":[1,3,5,7,2,4],"dynam":[0,8],"conjunct":[9],"group":[8],"monitor":[1,12,5,6,7,2],"polici":[0,1,5,7,8,10,11,2,3],"job_zero":[8],"platform":[13],"window":[12],"main":[1,2,4,5,6,7,8,9,11,12,3],"non":[2,5,6,3,8,9,10,13,12,7],"killer":[4],"getreactor":[8],"initi":[9,2,7,3],"poll_interv":[5,3,8],"conquer":[9],"not":[0,1,2,3,5,6,7,8,9,10,11,12,13],"now":[0,1,2,3,5,7,8,9,4,12,13],"discuss":[1,5,6,7,8,9,10,2,3],"introduct":[9,0],"name":[2,5,7,8,9,11,12,13],"config":[12,11],"didn":[10,3],"separ":[1,5,7,8,13],"zcml":[1,2,6,7,8,12,11],"updat":[9,8],"domain":[3],"agil":[13],"replac":[5,3,9,13,12,11],"individu":[3],"continu":[2,7,3],"intermitt":[1,2],"significantli":[2,13],"begun":[0,8],"happen":[0,2,7,8,3],"dispos":[3,13],"shown":[5,6,3],"accomplish":[5,7,8,4],"recuper":[3],"profil"
 :[13],"internet":[5,7,8],"factori":[1,12,11],"earlier":[9,2,8],"state":[9,5,2,8],"failure_log_level":[5],"getcwd":[7],"queue_instal":[7,3],"theori":[3],"org":[13,5,3,11,12,7],"card":[9,3],"care":[1,5,7,4,13],"couldn":[1],"badli":[2,3],"synchron":[9,7,8],"getactivejobid":[8],"recov":[10,3],"thing":[1,5,3,8,13,7],"place":[4,6,3,8,9,13,12,7],"newinteract":[4],"think":[5],"frequent":[9,1,10],"first":[1,5,6,3,8,9,10,13,2,7],"oper":[4,12,7,11],"setliveannot":[8],"directli":[9,5,7,8,3],"onc":[1,5,8],"walkthrough":[5],"oppos":[6],"open":[0,1,3,4,5,7,8,11,12,13],"size":[1,5,7,8,11,12,3],"given":[1,5,3,8,9,10,13,7],"convent":[7,8],"teardown":[1,5,2,4],"reli":[9,5,7,8],"2":[0,1,2,3,4,5,7,8,9,10,11,12,13],"installsignalhandl":[7],"tell":[5,2,7,8,3],"circl":[7],"annotat":[9],"conveni":[5,6,7,8,4,2,13],"strawderman":[2],"especi":[9,2],"copi":[7],"specifi":[5,6,7,8,13],"broadcast":[5,7],"mostli":[0,3],"than":[1,11,4,5,3,8,9,10,13,2,7],"11":[8],"10":[5,7,8,13],"13":[5,8],"12":[8],"15":[8],"
 14":[0,2,8],"16":[8],"r":[3],"optimist":[5,8],"posit":[8],"conn2":[7],"pre":[0,5,3,4],"dead_pool":[],"mounted_queu":[7],"ani":[0,5,6,7,8,9,2,3],"delin":[9],"sethook":[4],"everywher":[2],"recover":[9],"saw":[3],"techniqu":[0,8],"moreov":[5,8,13],"_pi":[5],"1000000":[5],"note":[0,1,5,7,8,9,10,12,13],"ideal":[3,8],"take":[1,4,5,3,8,9,10,13,2,7],"iprincip":[4],"200":[8],"begin":[1,2,5,7,8,12,3],"sure":[1,5,6,7,10,3],"z3monitor":[6,11,12,7,13],"trace":[2,5,12,7,11],"normal":[1,3,8,4],"track":[9,5,7],"knew":[3],"wait_for_deactiv":[3,8],"renam":[2],"later":[1,5,7,8,9,12,3],"zc_async":[1,5,8],"meanwhil":[7],"getjob":[2,8],"integer_oid":[2],"quickstart_zc_buildout":[],"gracefulli":[7,3],"serializ":[8],"show":[1,5,7,8,4,2,3],"subprocess":[5],"concurr":[9,5,7],"second_job":[8],"line":[9,5,7,13],"help":[0,1,5,7,8,10,2,13],"onli":[0,11,5,6,3,8,9,13,2,7],"slow":[9],"transact":[0,1,2,4,5,7,8,10,11,12,3],"activ":[1,2,5,7,8,10,11,12,3],"3":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"gocept":[0],"ical
 lbackproxi":[2],"ftest":[1,2],"dict":[5,7,8],"analyz":[5,3,8],"variou":[1,10,8],"get":[0,1,2,3,4,5,6,7,8,10,11,12,13],"repr":[8],"mission":[9,0],"cannot":[7,3],"requir":[3,8,9,13,2,7],"prime":[7],"reveal":[2],"family32":[1],"dramat":[3],"where":[3,5,2,7,13],"summari":[1,2,3,8,10],"wiki":[5],"job3":[7],"job2":[7,8],"job1":[7,8],"j2":[5],"concern":[9,5,2],"infinit":[8],"detect":[3],"review":[3],"getattr":[12],"between":[9,5,6,7,3],"import":[1,2,4,5,6,7,8,11,12,3],"across":[0,5,7,8,9,13],"parent":[5,2],"rare":[7,8,4],"alt_dispatch":[3],"uncondition":[5],"come":[5,2,7,11,3],"21":[0,2,12,8,11],"monitor_port":[5,12,11],"getactivejob":[2],"contract":[10,2,7],"s":[1,2,3,4,5,6,7,8,9,10,11,12,13],"get_pol":[1,3,7,10,12,11],"improv":[9,2,3],"among":[0,7,8],"overview":[9,0,8],"period":[2,8],"dispatch":[1,2,5,6,7,8,9,10,11,12,3],"60":[2,3],"cancel":[9,8],"constant":[9,7,8],"choose_anoth":[5],"poll":[1,2,5,7,8,9,10,11,12,3],"coupl":[1,13],"iagent":[5],"zpl":[5,2],"valueerror":[7,8],"resch
 edul":[2],"relstorag":[0,13],"400":[8],"catastroph":[10,2,3],"schedule_third":[8],"former":[2],"those":[10,5,2,7,8],"case":[1,5,7,8,9,2,3],"these":[0,1,2,3,5,7,8,9,10,12,13],"trick":[0,1,5,8,10,2,4],"advantag":[5,2,13],"stdout":[1,12,8,11],"worri":[5,3],"shutil":[12,11],"__init__":[4],"extra_info":[8],"getjobinfo":[2,8],"develop":[9,0,5,7,13],"author":[3,8],"job_on":[8],"getinteract":[4],"same":[1,11,5,3,8,9,13,2,7],"html":[5],"pai":[5],"document":[0,5,7,8,10,2,3],"gintauta":[2],"finish":[9,1,3,8],"oid":[1,5,2,3,8],"someon":[10,3],"driven":[2],"quota_nam":[5,7,8],"mani":[0,5,6,3,9,13,2,7],"extern":[2,3,13],"postpon":[0,10],"appropri":[6,3],"choos":[9,5],"markup":[],"fail_flag":[3],"without":[0,2,5,6,7,8,4,12,3],"execut":[8],"211":[8],"tip":[0,1,2,10,4],"rest":[3],"kill":[12],"aspect":[3],"touch":[7],"speed":[3,13],"blow":[8],"gather":[7],"death":[3],"except":[0,5,3,8,10,12,11],"littl":[13],"pile":[3],"4":[0,1,5,3,8,10,13,2,7],"exercis":[12],"heavier":[13],"real":[1,5,6,7,8,2
 ],"itransactionmanag":[7,8,4],"around":[1,5,7,9,4,2,3],"read":[5,3,8,9,10,2,13],"dart":[5],"conflicterror":[9,1,3,10],"world":[5,7,8],"cd":[5],"mod":[5],"first_job":[8],"patrick":[2],"integ":[2,8],"server":[0,3,5,6,7,9,10,12,13],"benefit":[7],"either":[2,6,8],"cascad":[8],"output":[5,2],"manag":[9,8,4],"fulfil":[8],"adequ":[9],"drake":[2],"constitut":[8],"assertionerror":[3],"easili":[9,11],"definit":[1,11,12,13],"exit":[5,3],"complic":[8],"refer":[5,2,7,8],"power":[9,5,7,13],"broken":[2,7],"retrypolici":[2,3],"immut":[5,8],"throw":[5],"_":[1,5,3],"oo":[1],"comparison":[5,6],"central":[0,5,7],"of":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"stand":[0,8],"mean":[5,7,8,9,2,4],"os":[5,12,7,11,13],"or":[0,1,2,3,5,6,7,8,9,10,4,12,13],"effici":[5],"gari":[13],"agent_instal":[3],"strip":[7],"your":[0,1,3,4,5,6,7,8,9,10,11,13],"log":[0,1,2,5,6,7,8,9,10,11,12,3],"area":[9],"aren":[8],"there":[4,5,3,8,13,2,7],"ngi":[5,2,7,8],"start":[0,1,2,3,5,6,7,8,9,10,12,13],"interfac":[1,2,4,5,7,8,9,11,12,
 3],"low":[10],"lot":[5,7,8,9,2,4],"orchestr":[8],"9":[7,8],"enough":[5],"tupl":[8,4],"main_job":[8],"fighter":[4],"with":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"conclus":[0,8],"faster":[9,0,1,5],"1024":[5],"dirti":[5,2],"possibl":[4,3,8,9,10,13,2,7],"default":[1,5,3,8,9,10,13,2,7],"cecz6zbo8zm4aegi":[5],"zope_conf":[12,11],"unusu":[2],"deadlock":[8],"extras_requir":[6],"transactionmanag":[7,3],"gone":[5],"instanceuuid":[5,7,3],"ad":[9,1,5,2,8],"creat":[1,11,4,5,3,8,9,10,13,12,7],"certain":[9,0,5,2,7],"threadloc":[2],"an":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"strongli":[10,5,13],"as":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"ar":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"decreas":[5,7],"at":[0,1,2,3,5,6,7,8,9,10,4,12,13],"file":[1,2,5,6,7,9,11,12,13],"fill":[7],"again":[1,5,7,8,9,2,3],"cleanup":[1,3],"5":[0,1,5,3,8,10,13,2,7],"you":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"architectur":[5],"resolut":[10,2,7,3],"product_config":[12,11],"registri":[7,4],"i_scribble_on_str":[8],"perceptu":[5],"briefli":[7],"
 pool":[9,1,7,8],"reduc":[5,2,7,8,13],"1b5":[5],"directori":[5,6,13],"descript":[4],"mimic":[7,3],"u":[5],"potenti":[9,7,8],"escap":[5],"newli":[13],"all":[1,5,6,3,8,9,10,13,2,7],"skeleton":[13],"pth":[5],"illustr":[3,4],"error_rais":[3],"abil":[9],"follow":[0,5,8,9,10,13],"children":[5],"edg":[2,8],"tt":[0,5],"tp":[5],"tn":[12],"to":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"ti":[3],"wait_for_pend":[3],"introduc":[2,6,8,13],"ta":[8],"liter":[0,5],"fals":[1,3,7,8,12,11],"util":[2,7,8,4],"mechan":[9,5,8],"sloni":[0,13],"fall":[2,3],"veri":[4,5,3,8,9,13,12,7],"condition":[5],"returnsometh":[3],"zdaemon":[2,3,5,6,7,12,11],"list":[5,7,8],"adjust":[5,7,11],"small":[6,3,8,9,13,7],"addbeforecommithook":[4],"enterpris":[5],"ten":[3],"handi":[5],"sync":[5,12,8],"past":[8],"zero":[9,8],"ipersist":[7,8],"design":[9,0,8],"uuid1":[3],"further":[12,8],"casual":[7,13],"deleg":[8],"sub":[13],"11dd":[5],"sum":[8],"856108":[5],"data_cach":[3],"delet":[5],"stubprincip":[4],"version":[1,5,6,7,9,2,13],"m
 ethod":[1,5,7,8,9,10,2,3],"contrast":[8],"hasn":[3,8],"full":[2,6,7,3],"themselv":[5,3],"20000":[12,11],"variat":[9,7],"sophist":[5,7],"shouldn":[1,2,7,3],"trunk":[8],"strong":[],"modifi":[9,2,8],"valu":[1,5,7,8,9,4,2,3],"search":[0,13],"setupdatetim":[4],"iauthent":[4],"doctest":[1,2,5,3,8,10,12],"pick":[9,7],"action":[9],"replace_this_with_path_to_storag":[11],"believ":[2],"via":[5],"ask":[5,7,8,9,12,11],"heurist":[9,3],"suddenli":[0,3],"establish":[4],"select":[2,7],"ctrl":[5,12],"regist":[1,6,7,8,9,11,2,3],"two":[1,2,3,5,6,7,8,9,11,12,13],"6":[5,7,8,4],"taken":[9,2],"iconnect":[7,8],"more":[0,1,3,5,6,7,8,9,10,11,2,13],"chestnut":[5],"desir":[5,6,3,8,9,13,2,7],"abspath":[12,11],"flag":[8],"particular":[2,7,8,3],"compani":[9],"cach":[2,13],"none":[1,2,4,5,7,8,11,12,3],"configroot":[12,11],"hour":[2,8],"dev":[13],"histori":[9,0,8],"remain":[8],"paragraph":[8],"del":[1],"learn":[2],"def":[1,3,5,7,8,4],"annotatestatu":[8],"valuabl":[7,8],"necesari":[2],"879b":[5],"registr":[7
 ,8],"share":[0,3,5,6,7,9,10,11,12,13],"accept":[9,5,2,8],"explor":[7,8],"_p_jar":[8],"cours":[1,12,7,3],"goal":[9,0,2,13],"secur":[2,4],"rather":[1,11,5,3,8,13,2,7],"anoth":[1,5,7,8,9,10,2,3],"snippet":[1],"simpl":[1,5,7,8,9,10,3],"tear_down_dispatch":[3],"100000":[5],"harida":[2],"fs":[1,5,7,8,11,12,3],"resourc":[9,7,8],"whirl":[8],"variant":[3,8],"reflect":[2,8],"catalog":[9,7],"fg":[5],"mutat":[5,2,8,4],"associ":[9,10,2,13],"a":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"circumst":[0,5,3,8],"short":[5],"stash":[2],"caus":[9,10,2,3],"callback":[1,5,3,8,10,2],"egg":[2,5,6,7,13],"readme_3b":[2],"rotat":[7,8,3],"soon":[9,5,8],"zconfig":[9,5,7],"held":[6],"i386":[5],"through":[5,8,9,13,2,4],"reconnect":[3],"minmax":[5,2],"24":[0,2],"style":[],"20":[0,2,5,12,11],"job_id":[8],"22":[8],"pend":[5,2,7,8,3],"getpid":[12],"bypass":[1],"might":[1,5,6,3,8,9,10,13,7],"good":[0,1,5,7,8,10,11,13],"return":[1,5,7,8,9,10,4,2,3],"framework":[0,1,5,7,13],"montecarlopimod":[],"adventur":[8],"complain":
 [1,2],"eventu":[5,7,8],"unlik":[5,8],"authent":[4],"did":[1,2,5,7,8,9,12,3],"reactiv":[2],"compris":[9],"found":[1,5,8,13],"unicod":[2],"hard":[9,3],"idea":[8],"realli":[2,10,5,6,8],"heavi":[7],"principal_id":[4],"connect":[1,2,3,5,6,7,8,9,10,11,12,13],"beyond":[8],"event":[1,2,3,5,7,12,11],"setsit":[4],"p":[4],"addmainagentactivationhandl":[7],"setsiz":[],"publish":[7],"footnot":[1,2,4,6,7,8,9,11,12,3],"i_handle_nameerror":[8],"7":[5,7,8,4],"print":[4,12,7,8,3],"difficulti":[9],"proxi":[5,2,7],"advanc":[0,5,2,8],"gut":[1],"differ":[2,4,5,6,7,8,9,11,12,3],"pleasant":[5],"reason":[1,5,10,7,3],"base":[0,1,5,7,8,9,4,2,3],"miliauska":[2],"pending_result":[9],"put":[0,1,2,3,5,6,7,8,9,10,11,12,13],"_choos":[7],"basi":[0,8],"eventlog":[12,11],"thread":[0,1,5,7,8,9,11,2,3],"omit":[3],"success":[9,5,2,8],"benji":[2],"perhap":[9,10,3,8],"old_commit":[3],"perman":[3],"w":[12,11],"heartbeat":[9],"assign":[5,7],"major":[0],"notifi":[12,3,11],"obviou":[7,13],"feel":[0,8],"exchang":[13],"n
 umber":[1,5,3,8,9,10,13,2,7],"placehold":[0],"done":[0,1,5,7,8,9,12,3],"least":[5,3,8,13],"blank":[],"miss":[1],"bellweath":[3],"guess":[9,3],"connectionofpersist":[6,12,11],"retry_policy_factori":[5],"interact":[5,2,8,4],"construct":[8],"stori":[2,3,8,13],"store":[9,0,5,7],"schema":[12,11],"luckili":[5],"xmln":[12,11],"option":[5,12,7,11,3],"behind":[2,5,12],"part":[0,1,5,7,8,9,3],"grace":[9,3],"fred":[2],"provideadapt":[1,7],"kind":[5,2,7,8,13],"whenev":[2,8],"remov":[0,2,7,8,3],"reus":[5,2,7],"str":[3],"schemadir":[12,11],"arrang":[9,3,8],"toward":[13],"danc":[1,7,13],"cleaner":[8],"a_persistent_object_with_db_connect":[8],"mickei":[4],"packag":[0,1,2,5,6,7,8,9,10,11,12,13],"expir":[9],"dedic":[9],"wait_for":[8],"job_two":[8],"imagin":[5,3,8,4],"built":[5,8,13],"equival":[13],"randint":[12,11],"odd":[2],"self":[1,5,3,8,9,10,2,4],"also":[1,2,5,3,8,9,10,13,12,7],"build":[10,13],"distribut":[5,7,8,13],"exec":[13],"previou":[1,5,3,8,2,13],"quota":[5,2,7,8],"react":[3,8],"most
 ":[1,3,5,7,8,11],"plai":[1],"charg":[9,3],"clear":[3,4],"kw":[1],"clean":[1,12,4,3,13,2,7],"readconflicterror":[1,2],"grokproject":[13],"carefulli":[9],"session":[8,13],"posexcept":[3],"clientdisconnect":[2,3],"fine":[10,5,7],"find":[1,2,11,3,9,13,12,7],"giant":[4],"firewal":[5,12],"pretti":[5,6,7],"solut":[9,8],"queu":[9],"8":[10,5,7,8],"rotterdam":[6],"callback_job":[3],"__file__":[12,11],"send_messag":[8],"barrier":[9],"longest":[8],"restart":[0,5,7,3],"site_zcml":[12,11],"zodb3":[5,7],"statist":[1,7,8],"x":[9,13],"wrote":[9],"set":[0,1,2,3,4,5,6,7,8,11,12,13],"dump":[5],"startup":[7],"decompos":[9,0,2],"mutabl":[5,8],"see":[0,1,11,5,3,8,9,10,13,2,7],"lastcomplet":[5],"arg":[5,12,8,11],"close":[1,5,7,13,12,11],"timeouterror":[8],"inconveni":[1],"someth":[1,3,5,6,7,8,9,10,12,13],"particip":[0,2,4],"won":[1,5,8],"hold":[10,2,13],"experi":[9,10,5],"altern":[1,3,8],"numer":[5],"event_log":[12,3,11],"selectreactor":[7],"read_al":[12],"iquota":[5],"ireactor":[7],"c":[5,12,11,13
 ],"last":[1,2,5,6,7,8,12,3],"queue_nam":[8],"idispatcheractiv":[7],"alon":[2,0,1,6,8],"foreign":[5],"context":[8,4],"pdf":[9,0],"whole":[10],"load":[9],"simpli":[5,7,8,3],"point":[1,10],"instanti":[5,2,7,8,4],"schedul":[9,0,2,8],"shutdown":[7,3],"suppli":[0,8],"py":[1,5,6,7,9,11,2,13],"empti":[5,2,7,8,4],"runner":[2],"pi":[5],"blob":[2],"versa":[9],"fire":[2,7],"unnecessarili":[2],"coordin":[0,8],"understand":[5,7,8,13],"agent2":[7],"func":[5],"demand":[9],"talent":[5],"look":[1,2,5,6,7,8,9,12,3],"straight":[5,7],"histor":[2],"durat":[5],"formatt":[12,11],"while":[0,5,7,8,9,10,3],"abov":[1,4,5,7,8,11,12,3],"error":[1,2,5,7,8,9,10,11,12,3],"bad_job":[1],"loop":[9,7,8],"pack":[2],"multi_databas":[12,11],"10000":[],"readi":[5,7,13],"readm":[2],"itself":[9,5,2,7],"churn":[7],"minim":[1,7,8],"belong":[7],"hadoop":[5],"lengthi":[9],"getretrypolici":[5,3],"zope":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"conflict":[5,7,8,9,10,2,3],"higher":[1],"optim":[9,2,7,8],"painless":[0],"leopard":[13
 ],"alert":[10],"moment":[5,12,7],"temporari":[6],"user":[9,0,2,13],"robust":[2,3],"unixen":[12],"recent":[9,1,7,8,3],"lower":[3],"task":[0,5,7,8,9,10,2,3],"lib":[5,13],"older":[3],"entri":[2],"pickl":[9,0,5,2],"person":[5,8],"expens":[9,0,2],"site_zcml_fil":[12,11],"wait_for_start":[1,3],"baroqu":[7],"sidebar":[],"snappi":[5],"theoret":[2],"realloc":[9],"input":[8],"subsequ":[10,8],"app":[0,1,2,4,6,7,11,12,13],"bin":[5,13],"complaint":[1],"transpar":[4],"big":[7],"bit":[1,3,5,7,8,2,11],"characterist":[7],"async_trac":[12,11],"d":[5,3,8,13],"worker1":[5],"docutil":[0,5],"signal":[12,7],"iretrypolici":[5,2,3],"zasync":[9],"collect":[9,2,7,8,13],"princip":[4],"api":[9,7,8],"encount":[9,0,5],"often":[1,5,7,8,3],"creation":[0,2,7,8],"some":[0,1,3,5,6,7,8,9,10,4,2,13],"back":[5,7,8,9,2,3],"global":[1,5,7,8,9,3],"sampl":[5],"surpris":[8],"choosemul":[7],"virtualenv":[0,5,7,13],"joberror":[2,3],"scale":[5,12,11],"async_trace_log":[12,11],"per":[0,5,2,3],"pem":[5],"retri":[0,1,5,3,9,
 10,2],"larg":[0,3,7,9,12,11],"t":[1,2,4,5,3,8,9,10,13,12,7],"machin":[0,5,8,9,10,13],"be":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"run":[0,1,2,3,5,7,8,9,4,12,13],"step":[7,8,13],"prerequisit":[5,13],"by":[0,1,2,5,6,7,8,9,10,11,12,3],"constraint":[9],"prove":[5],"queueinstal":[2],"pertin":[1,2,3],"block":[5,3],"emphasi":[13],"primarili":[0,5,2],"into":[0,1,5,7,8,9,10,2,3],"within":[9,5,2,3,8],"ellipsi":[1,5,12,3,8],"52e1":[5],"chang":[0,1,11,5,3,8,9,13,2,7],"assigneruuid":[7],"durabl":[0],"zvezdan":[2],"span":[0,5],"errno":[12,11],"question":[3],"long":[0,1,5,3,8,9,10,2],"custom":[5,3],"time_fli":[8],"includ":[0,1,2,3,5,6,7,8,9,11,12,13],"suit":[5],"forward":[7],"imaginarynetworkcall2":[8],"properli":[11],"putback":[2],"translat":[7],"newer":[1,2],"atom":[2],"async_storage_path":[12,11],"iqueu":[12,5,7,8,2,11],"info":[2,3,8],"utc":[8],"_map":[4],"begin_aft":[5,2,8],"up":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"us":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"mystifi":[1],"zope_conf_fil":[12,11],"r
 eadlin":[7,13],"similar":[1,5,3,8,9,13,12,7],"supervisor":[2,5,12,7,3],"doesn":[1,5,2,8,13],"repres":[9,8],"incomplet":[0,3],"guarante":[8],"titl":[4],"nice":[5],"alt_poll_1":[3],"alt_poll_2":[3],"alt_poll_3":[3],"drag":[7],"begin_bi":[5,2],"e":[2,11,12,13],"vice":[9],"depth":[1,7],"2006":[8],"far":[2,5,6,8],"2008":[0,2],"code":[0,1,4,5,7,8,9,10,11,2,3],"partial":[5,2,8],"manpath":[13],"queri":[9,5],"process_sampl":[5],"edu":[],"go":[5,3,10,13,2,7],"privat":[5],"elsewher":[5,3],"appsetup":[6,12,7,11],"friendli":[0,5],"send":[1,12,7,8],"macport":[13],"opposit":[2],"noop":[1],"sent":[1,7,8],"deactiv":[2,12,7,3],"threadeddispatcherinstal":[2,3],"mous":[4],"volum":[9],"tri":[9,1,2,7,3],"magic":[8,13],"i18nmessageid":[5,2,7],"that\u00e6":[5],"scalabl":[9],"idispatcherregist":[7],"try":[2,4,5,7,8,10,11,12,3],"race":[2],"particularli":[1,10],"pleas":[0,8],"impli":[8],"smaller":[5,2,7],"alt_uuid":[3],"cfg":[13],"time_sleep":[1,3],"zc":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"blanklin":[12
 ,8],"video":[7],"download":[13],"carlo":[5],"append":[3,8],"compat":[9,2],"index":[9,0,7,8,13],"compar":[5],"access":[5,6,7,8,3],"experiment":[9,5,7],"deduc":[3],"whatev":[1],"async_db":[7],"leg":[8],"got":[2,5,12,3,11],"len":[1,3,7,8,12,11],"closur":[5,2,8],"let":[0,1,2,3,5,7,8,9,4,12,13],"becom":[8],"sinc":[2,12,7,8,3],"z3":[6,2,4,12,11],"convert":[9,2],"survei":[5],"try_transaction_five_tim":[2],"33":[8],"conceiv":[13],"30":[0,12,5,3,8,2],"technolog":[5,13],"typic":[1,5,6,3,8,9],"explain":[7],"chanc":[1,5,3,8,9,10,2],"heart":[3],"appli":[5],"approxim":[5,8],"imonitorplugin":[5],"expect":[1,4,3,8,10,13,7],"gettraceback":[10,8],"boolean":[7,3],"__name__":[5,4],"from":[0,1,2,3,4,5,7,8,9,10,11,12,13],"zip":[13],"commun":[9,0,2,3,8],"doubl":[2],"next":[1,5,7,8,10,2,3],"websit":[13],"few":[1,5,3,9,10,13,7],"postprocess":[5,2,8],"getutil":[12,8],"sort":[9,8],"isal":[3],"on":[0,1,3,4,5,6,7,8,9,10,11,2,13],"__stdout__":[1,2],"levelnam":[3],"ok":[8],"starter":[7],"high":[9,0,10,13]
 ,"account":[7],"alik":[6,12],"f":[5,12,7,11],"annot":[9,1,10,8],"drill":[3],"scribbl":[8],"aliv":[3],"control":[9,5,7,8,3],"quickstart":[0,5,2,7,13],"process":[0,2,5,7,8,9,11,12,3],"lock":[10,3],"sudo":[13],"third_job":[8],"main_storage_path":[12,11],"async_event_log":[12,11],"serial":[9,0,5,2,8],"basic_dispatcher_polici":[2,6,1,12,11],"delai":[9],"job_c":[8],"job_b":[8],"sit":[3],"six":[9],"conn":[1,4,5,7,8,11,12,3],"instead":[1,5,3,13,2,7],"circular":[7],"job_a":[8],"db":[1,4,5,7,8,11,12,3],"hazard":[3],"rmtree":[12,11],"attent":[5],"redund":[13],"endinteract":[4],"essenti":[7,8],"retrycommonfourtim":[5,3],"bind":[12,11],"counter":[8],"element":[5],"allow":[5,7,8,9,2,3],"old_dispatch":[3],"original_stop":[1],"move":[9,5,2,7,8],"own":[0,1,5,3,13,7],"utcnow":[5],"s5":[2],"1":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"ll":[1,2,3,5,6,7,8,11,12,13],"ls":[5],"6c8":[5],"therefor":[1,5,3,8,13,7],"crash":[9,3],"greater":[7,8,3],"handl":[0,2,4,5,7,8,9,11,12,3],"auto":[7],"spell":[9,2,12],"d
 ai":[5],"auth":[4],"mention":[1,4,2,3,13],"set_now":[8],"asyncdb":[5,2],"front":[3],"poskei":[3],"anyth":[5,3],"nameerror":[8],"get_connect":[7],"trap":[8],"subset":[9,7],"grok":[0,1,13],"chunk":[7],"clientstorag":[5,7],"consum":[9],"meta":[12,11],"catalogqueu":[8],"our":[1,3,5,6,7,8,11,12,13],"tenth":[1],"special":[9,5,7,8],"out":[5,3,8,9,13,2,7],"variabl":[5,6,7,11,3],"influenc":[5],"stub":[1],"suitabl":[9],"rel":[9],"dispatcherreactiv":[2],"math":[5],"common":[1,2,3],"miicxgibaakbgqcyazw":[5],"clarifi":[2],"shut":[2,1,5,12,7],"insid":[9],"manipul":[1],"standalon":[8,13],"zodb":[0,1,2,4,5,7,8,9,10,11,12,3],"york":[2],"wait_for_result":[1,2,5,3,10,12,11],"releas":[2,7,3],"shortest":[6,8],"etern":[9],"g":[13],"could":[11,2,5,3,8,9,13,12,7],"keep":[5,3,8,9,13,2,7],"enforc":[8],"outsid":[9,2],"retain":[2],"timezon":[5,8],"unexpectedli":[3],"getprincip":[4],"indiscrimin":[7],"qualiti":[7],"date":[5,8],"prioriti":[7],"strict":[2],"unknown":[7,8],"licens":[5],"macosx_deployment_t
 arget":[13],"mkdir":[5],"system":[0,2,5,3,9,12,13],"wrapper":[1,4],"attach":[5],"monkei":[],"termin":[5,13],"final":[4,5,7,8,3],"1434359999999999":[5],"shell":[5,13],"zc_async_uuid":[6,5,12,7,11],"rsa":[5],"providehandl":[7,3],"_handlesign":[7],"exactli":[8],"haven":[5],"multidb_queue_instal":[7],"bother":[13],"multidb_dispatcher_polici":[11],"structur":[9,1,5,7,8],"charact":[5],"sens":[5,8],"wait_for_m":[3],"teardowndispatchererror":[1],"py2":[5],"tranasctionmanag":[3],"clearli":[9],"have":[0,1,2,3,5,6,7,8,9,10,4,12,13],"tabl":[0],"need":[0,1,3,4,5,6,7,8,9,11,2,13],"simplejson":[2,5,6,7],"min":[8],"expos":[0,5],"accuraci":[5],"discret":[0],"which":[0,1,3,5,6,7,8,9,10,11,2,13],"conclud":[8],"so":[0,1,2,3,5,6,7,8,9,10,12,13],"singl":[5,6,7,8,9,10,12,11],"regard":[0],"unless":[1,8],"deploy":[9,0,5,13],"who":[5],"discov":[9,2,13],"fullerton":[],"queryinteract":[4],"eight":[5],"pyc":[13],"why":[1,3,8],"hopefulli":[5,2,7,11],"request":[0,1,5,7,8,9,4,2,3],"face":[3],"determin":[9,
 5,7,3],"try_five_tim":[2],"occasion":[1],"constrain":[8],"fact":[1,7,8],"text":[5,12,7,8],"syntaxerror":[10],"verbos":[9,0,2],"watch":[3],"rubric":[],"zdoption":[12,11],"trivial":[2,12,7,8,3],"inlin":[7],"locat":[5,7,4,13],"launchpad":[2],"rhssmcms0jlnwi2cwmz":[5],"jar":[8],"forev":[1,3,10],"should":[0,1,2,3,5,6,7,8,9,10,12,13],"suppos":[2,3],"local":[1,5,3,8,4,2,13],"worker2":[5],"pull":[2,6,3,8],"simplest":[0,7,8],"pypi":[3,5,2,7,13],"zr":[0,13],"autom":[0,5,6],"regularli":[5,2],"oner":[8],"fallout":[3],"127":[5,12,11],"increas":[2,8],"enabl":[0,1,5],"acquirelockandchoosefirst":[3],"ping_death_interv":[3],"integr":[1,5],"contain":[2,7],"view":[9,1,2,7,13],"conform":[7],"legaci":[2],"unimport":[2],"frame":[3],"stack":[3],"becaus":[1,5,6,7,8,9,4,2,3],"i_am_a_bad_bad_funct":[8],"statu":[1,2,5,3,8,9,10,12],"wa":[1,5,7,8,9,4,2,3],"correctli":[4,13],"pattern":[9,5,7,8,13],"picklabl":[5,8],"pythreadstate_setasyncexc":[5],"written":[10,5,3,13],"quickest":[12],"post_process":[8],"p
 rogress":[9,8],"kei":[1,5,7,8,9,2,3],"tempfil":[12,11],"job":[0,1,2,4,5,7,8,9,10,11,12,3],"entir":[13],"disconnect":[12,3],"wait_to_deactiv":[],"addit":[5,6,7,8,9,11,2,13],"getdispatch":[8],"instant":[2],"plugin":[9],"equal":[8],"etc":[3],"instanc":[0,1,4,5,3,8,9,10,13,2,7],"uncaught":[5],"generate_sampl":[5],"method_wrapper_typ":[7],"comment":[6],"dispatcherag":[2,3],"active_start":[5],"walk":[8],"respect":[2],"600":[3],"quit":[5],"slowli":[2],"evalu":[2,7],"compon":[1,2,4,5,7,8,9,11,12,3],"json":[1],"treat":[8],"immedi":[2,12],"_getrootobject":[1],"both":[1,4,5,6,3,9,10,13,2,7],"assert":[11,12,7,8,3],"togeth":[9,7,3],"subtitl":[],"present":[2,8],"replic":[0],"multi":[9,7,8],"harder":[2],"will":[1,2,3,4,5,6,7,8,9,10,11,12,13],"defin":[2,5,6,7,8,9,12],"ill":[7],"layer":[1,2],"ve":[6,5,12,11],"helper":[0,5,7,8,2,13],"almost":[2],"demo":[2,7,8],"site":[1,2,5,6,9,4,12,11],"unstop":[1],"doctest_test":[1,8],"revis":[5],"_reactor":[7],"choose_generate_sampl":[5],"prefermul":[],"pi
 ng_interv":[3],"dispatcher_ag":[7],"cross":[7],"member":[8],"python":[0,1,5,3,8,10,13,2,7],"difficult":[3],"http":[2,13,5,3,11,12,7],"mont":[5],"upon":[9],"effect":[1,5,8,13],"pleasantli":[13],"logfil":[12,11],"setsitemanag":[4],"distutil":[2],"expand":[2,13],"exceedingli":[7],"off":[5,7,8,3],"i":[2,5,3,8,13,12,11],"keyboardinterrupt":[12],"well":[0,1,5,7,8,9,3],"thought":[2],"exampl":[0,1,3,5,6,7,8,9,11,2,13],"command":[5,2,13],"filesystem":[9],"audio":[7],"44":[8],"sibl":[0,2,3],"usual":[1,4,5,7,8,9,11,12,13],"newest":[13],"paus":[8],"less":[5,2,8],"obtain":[9,8],"43":[8],"setproductconfigur":[12,11],"simultan":[9],"web":[9,0,13],"rwproperti":[5,7],"settransactionhook":[4],"add":[0,5,7,8,9,2,13],"other":[0,1,2,3,5,7,8,9,10,12,13],"ado":[12],"logger":[1,5,12,11],"site_definit":[12,11],"eaddrinus":[12,11],"realiz":[10,12,3,8,11],"five":[3,8],"know":[0,1,5,3,9,13,7],"recurr":[9],"python2":[5],"sample_job":[5],"like":[1,2,3,5,6,7,8,9,12,13],"lost":[3],"ldflag":[13],"necessari"
 :[1,5,7,8,9,3],"lose":[9],"async":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"page":[0,2,13],"revers":[3],"export":[13],"proper":[12,7],"home":[7,13],"librari":[5,7],"buildout":[5,13],"lead":[1],"leak":[2],"avoid":[1,5,10,8],"thank":[9,2],"leav":[1],"mode":[5],"investig":[1],"imaginari":[8],"deprec":[5,2,7],"host":[5],"isn":[1,5,3],"although":[9,1],"callfromthread":[12,3,5,7,2,11],"simpler":[9,7,3],"about":[1,5,7,8,9,4,2,3],"compet":[3],"actual":[1,5,7,8,10,2,3],"socket":[12,11],"constructor":[7],"discard":[3],"interrupt_job":[3],"10000000":[5],"easy_instal":[5,13],"automat":[5],"getqueu":[2,8],"getmessag":[3],"leverag":[4,6,13],"processor":[5,13],"pictur":[7],"inner":[8],"registerutil":[4],"function":[0,1,5,7,8,9,10,2],"unexpect":[],"subscrib":[1,3,7,13,2,11],"handleinterrupt":[2,3],"keyerror":[5],"but":[1,4,5,3,8,9,10,13,2,7],"spuriou":[1,2,13],"hm":[5],"ha":[0,1,2,3,5,6,7,8,9,10,4,12,13],"bug":[1,2,3],"count":[5],"succe":[3],"made":[0,5,2,8],"wise":[7],"whether":[9,2,7,8,3],"j":[5
 ,8,4],"getstatist":[8],"troubl":[11],"asynchron":[9,8],"record":[5,3],"below":[1,5,6,3,8,9,13,7],"limit":[5,7,8,9,2,3],"otherwis":[1,7,8],"problem":[0,1,5,3,8,9,10,13,2,7],"strategi":[10],"pin":[4],"int":[5,8],"dure":[3,7,8,9,10,4],"pid":[12],"twist":[0,2,5,6,7,8,9,10,11,12,3],"implement":[0,5,7,8,9,2,4],"workabl":[13],"probabl":[1,5,3,8,11,12,13],"47":[8],"guidanc":[13],"42":[4,12,3,8,11],"detail":[9,5,3,8],"virtual":[5,8],"book":[5],"bool":[1,12,3,11],"futur":[9,7],"rememb":[2],"varieti":[0,5],"zeoclient":[12],"handle_result":[3],"stat":[8],"repeat":[3,5,7,13],"fulli":[3],"stai":[5,7],"experienc":[13],"sphinx":[2],"reliabl":[9,0,10,2],"indirectli":[6,7],"rule":[5,8],"emerg":[13],"getrootobject":[1],"getlogg":[1]}]
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/tips.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/tips.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/tips.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,203 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Tips and Tricks &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="next" title="Recovering from Catastrophes" href="catastrophes.html" />
+    <link rel="prev" title="Two Database Set Up" href="README_3b.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="catastrophes.html" title="Recovering from Catastrophes"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README_3b.html" title="Two Database Set Up"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="tips-and-tricks">
+<h1 id="tips-and-tricks">Tips and Tricks<a class="headerlink" href="#tips-and-tricks" title="Permalink to this headline">¶</a></h1>
+<div class="section" id="general-tips-and-tricks">
+<h2 id="general-tips-and-tricks">General Tips and Tricks<a class="headerlink" href="#general-tips-and-tricks" title="Permalink to this headline">¶</a></h2>
+<ul class="simple">
+<li>If you have multiple machines working as zc.async dispatchers, it is
+strongly suggested that you get the associated servers connected to a shared
+time server.  You generally don&#8217;t want your machines to disagree by more than
+a few seconds.</li>
+<li>Avoid long transactions if possible.  Really try to avoid long transactions
+involving frequently written objects.  One possible strategy is to divide up
+your code into a job for low-conflict tasks and one or more jobs for
+high-conflict tasks, perhaps created in a callback.</li>
+<li>Sometimes you can&#8217;t avoid long transactions. But <em>really</em> try to avoid long
+commits. Commits hold a lock on the ZODB, and if you end up writing so much
+in a single transaction that you take noticeable time to write, realize that
+you are affecting&#8211;postponing&#8211;every single subsequent commit to the
+database.</li>
+<li>Callbacks should be quick and reliable. If you want to do something that
+might take a while, put another job in the queue.</li>
+<li>Some tasks are non-transactional.  If you want to do them in a <tt class="docutils literal"><span class="pre">Job</span></tt>, you
+don&#8217;t want them to be retried!  Use the NeverRetry retry policy for these,
+as described in the <a class="reference external" href="catastrophes.html#recovering-from-catastrophes"><em>Recovering from Catastrophes</em></a> section.</li>
+<li>zc.async works fine with both Python 2.4 and Python 2.5.  Note that building
+Twisted with Python 2.4 generates a SyntaxError in a test, but as of this
+writing Twisted 8.1.0 is supported for Python 2.4.</li>
+<li>Using the <tt class="docutils literal"><span class="pre">transaction</span></tt> package&#8217;s before-commit hooks can wreak havoc if
+your hook causes an exception during commit, and the job uses the zc.async
+<tt class="docutils literal"><span class="pre">RetryCommonForever</span></tt> retry policy (which all callbacks use by default).
+This policy has a contract that it <em>will</em> commit, or die trying, so it
+retries all transactions that have an error on commit, and emits a critical
+log message every few retries (configurable on the policy).  If the error
+never goes away, this will retry <em>forever</em>.  Make sure critical log messages
+actually alert someone!</li>
+</ul>
+</div>
+<div class="section" id="testing-tips-and-tricks">
+<h2 id="testing-tips-and-tricks">Testing Tips and Tricks<a class="headerlink" href="#testing-tips-and-tricks" title="Permalink to this headline">¶</a></h2>
+<ul class="simple">
+<li>In tests, don&#8217;t check to see if poll is activated until after the first
+poll. Try <tt class="docutils literal"><span class="pre">zc.async.testing.get_poll(zc.async.dispatcher.get(),</span> <span class="pre">0)</span></tt>, for
+instance.</li>
+<li>In tests, be aware that DemoStorage does not support mvcc and does not
+support conflict resolution, so you may experience ConflictError (write and
+particularly read) problems with it that you will not experience as much,
+or at all, with a storage that supports those features such as FileStorage.
+Notice that all of the tests in this package use FileStorage.</li>
+<li>If you get a failure as a result and you didn&#8217;t expect it, don&#8217;t forget
+the <tt class="docutils literal"><span class="pre">getTraceback</span></tt> and <tt class="docutils literal"><span class="pre">printTraceback</span></tt> methods on the failure.  The
+whole point of the failure is to help you diagnose problems.</li>
+<li><tt class="docutils literal"><span class="pre">zc.async.dispatcher.get()</span></tt> will get you the dispatcher.  You can then check
+if it is <tt class="docutils literal"><span class="pre">activated</span></tt> and also use the other introspection and status
+methods.</li>
+<li>The <tt class="docutils literal"><span class="pre">zc.async.testing</span></tt> module has a number of helpful functions for
+testing. <tt class="docutils literal"><span class="pre">get_poll</span></tt>, given a dispatcher, will give you the next poll. This
+is a good way to make sure that a job you just put in has had a chance to be
+claimed by a dispatcher. It&#8217;s also a reasonable way to verify that the
+dispatcher has started. Other useful testing functions are
+<tt class="docutils literal"><span class="pre">zc.async.testing.wait_for_result</span></tt>, which waits for the result on a give
+job and returns it; and <tt class="docutils literal"><span class="pre">zc.async.testing.wait_for_annotation</span></tt>, which waits
+for a given annotation on a given job. These are demonstrated in various
+doctests in this package, but should also be reasonably simple and
+self-explanatory.</li>
+</ul>
+</div>
+<div class="section" id="more-tips-and-tricks">
+<h2 id="more-tips-and-tricks">More Tips and Tricks<a class="headerlink" href="#more-tips-and-tricks" title="Permalink to this headline">¶</a></h2>
+<p>The following documents describe specific tips and tricks for specific
+situations.</p>
+<ul>
+<li><a class="reference external" href="catastrophes.html">Recovering from Catastrophes</a><ul>
+<li><a class="reference external" href="catastrophes.html#what-might-go-wrong">What Might Go Wrong?</a></li>
+<li><a class="reference external" href="catastrophes.html#scenarios-polling-errors">Scenarios: Polling Errors</a></li>
+<li><a class="reference external" href="catastrophes.html#scenarios-job-related-errors">Scenarios: Job-Related Errors</a></li>
+</ul>
+</li>
+</ul>
+<ul>
+<li><a class="reference external" href="z3.html">Zope 3 General Tips and Tricks</a></li>
+</ul>
+<ul>
+<li><a class="reference external" href="ftesting.html">Zope 3 Testing Tips and Tricks</a><ul>
+<li><a class="reference external" href="ftesting.html#summary">Summary</a></li>
+<li><a class="reference external" href="ftesting.html#discussion">Discussion</a></li>
+</ul>
+</li>
+</ul>
+</div>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h3>Table Of Contents</h3>
+            <ul>
+<li><a class="reference external" href="">Tips and Tricks</a><ul>
+<li><a class="reference external" href="#general-tips-and-tricks">General Tips and Tricks</a></li>
+<li><a class="reference external" href="#testing-tips-and-tricks">Testing Tips and Tricks</a></li>
+<li><a class="reference external" href="#more-tips-and-tricks">More Tips and Tricks</a><ul>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="README_3b.html" title="previous chapter">Two Database Set Up</a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="catastrophes.html" title="next chapter">Recovering from Catastrophes</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/tips.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="catastrophes.html" title="Recovering from Catastrophes"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="README_3b.html" title="Two Database Set Up"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/1.5.0/z3.html
===================================================================
--- zc.async/trunk/htmldocs/1.5.0/z3.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/1.5.0/z3.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,279 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>Zope 3 General Tips and Tricks &mdash; zc.async v1.5.0 documentation</title>
+    <link rel="stylesheet" href="_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+          URL_ROOT:    '',
+          VERSION:     '1.5.0',
+          COLLAPSE_MODINDEX: false,
+          FILE_SUFFIX: ''
+      };
+    </script>
+    <script type="text/javascript" src="_static/jquery.js"></script>
+    <script type="text/javascript" src="_static/interface.js"></script>
+    <script type="text/javascript" src="_static/doctools.js"></script>
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+    <link rel="contents" title="Global table of contents" href="contents.html" />
+    <link rel="index" title="Global index" href="genindex.html" />
+    <link rel="search" title="Search" href="search.html" />
+    <link rel="top" title="zc.async v1.5.0 documentation" href="index.html" />
+    <link rel="up" title="Tips and Tricks" href="tips.html" />
+    <link rel="next" title="Zope 3 Testing Tips and Tricks" href="ftesting.html" />
+    <link rel="prev" title="Recovering from Catastrophes" href="catastrophes.html" />
+  </head>
+  <body>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="ftesting.html" title="Zope 3 Testing Tips and Tricks"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="catastrophes.html" title="Recovering from Catastrophes"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+          <li><a href="tips.html" accesskey="U">Tips and Tricks</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="zope-3-general-tips-and-tricks">
+<h1 id="zope-3-general-tips-and-tricks">Zope 3 General Tips and Tricks<a class="headerlink" href="#zope-3-general-tips-and-tricks" title="Permalink to this headline">¶</a></h1>
+<p>If you use Zope 3, sometimes you want async jobs that have local sites and
+security set up. <tt class="docutils literal"><span class="pre">zc.async.z3.Job</span></tt> is a subclass of the main
+<tt class="docutils literal"><span class="pre">zc.async.job.Job</span></tt> implementation that leverages the <tt class="docutils literal"><span class="pre">setUp</span></tt> and
+<tt class="docutils literal"><span class="pre">tearDown</span></tt> hooks to accomplish this.</p>
+<p>It takes the site and the ids of the principals in the security context at its
+instantiation. The values can be mutated. Then when the job runs it sets the
+context up for the job&#8217;s code, and then tears it down after the work has been
+committed (or aborted, if there was a failure). This can be very convenient for
+jobs that care about site-based component registries, or that care about the
+participants in zope.security interactions.</p>
+<p>This is different than a <tt class="docutils literal"><span class="pre">try:</span> <span class="pre">finally:</span></tt> wrapper around your main code that
+does the work, both because it is handled for you transparently, and because
+the context is cleaned up <em>after</em> the job&#8217;s main transaction is committed. This
+means that code that expects a security or site context during a
+pre-transaction hook will be satisfied.</p>
+<p>For instance, let&#8217;s imagine we have a database, and we establish a local site
+and an interaction with a request. <a class="footnote-reference" href="#zope3job-database-setup" id="id1">[1]</a> <a class="footnote-reference" href="#empty-setup" id="id2">[2]</a>
+Unfortunately, this is a lot of set up. <a class="footnote-reference" href="#zope3job-setup" id="id3">[3]</a></p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.app.component.hooks</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">hooks</span><span class="o">.</span><span class="n">setSite</span><span class="p">(</span><span class="n">site</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.security.management</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.z3</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">security</span><span class="o">.</span><span class="n">management</span><span class="o">.</span><span class="n">newInteraction</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">z3</span><span class="o">.</span><span class="n">Participation</span><span class="p">(</span><span class="n">mickey</span><span class="p">))</span> <span class="c"># usually would be a request</span>
+</pre></div>
+<p>Now we create a new job.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">reportOnContext</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="k">print</span> <span class="p">(</span><span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">hooks</span><span class="o">.</span><span class="n">getSite</span><span class="p">()</span><span class="o">.</span><span class="n">__class__</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span>
+<span class="gp">... </span>            <span class="nb">tuple</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">principal</span><span class="o">.</span><span class="n">id</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span>
+<span class="gp">... </span>            <span class="n">zope</span><span class="o">.</span><span class="n">security</span><span class="o">.</span><span class="n">management</span><span class="o">.</span><span class="n">getInteraction</span><span class="p">()</span><span class="o">.</span><span class="n">participations</span><span class="p">))</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="s">&#39;j&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">z3</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">reportOnContext</span><span class="p">)</span>
+</pre></div>
+<p>The ids of the principals in the participations in the current interaction
+are in a <tt class="docutils literal"><span class="pre">participants</span></tt> tuple.  The site is on the job&#8217;s <tt class="docutils literal"><span class="pre">site</span></tt> attribute.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="o">.</span><span class="n">participants</span>
+<span class="go">(&#39;mickey&#39;,)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="o">.</span><span class="n">site</span> <span class="ow">is</span> <span class="n">site</span>
+<span class="go">True</span>
+</pre></div>
+<p>If we end the interaction, clear the local site, and run the job, the job we
+used (<tt class="docutils literal"><span class="pre">reportOnContext</span></tt> above) shows that the context was correctly in place.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">security</span><span class="o">.</span><span class="n">management</span><span class="o">.</span><span class="n">endInteraction</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">hooks</span><span class="o">.</span><span class="n">setSite</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="p">()</span>
+<span class="go">(&#39;StubSite&#39;, (&#39;mickey&#39;,))</span>
+</pre></div>
+<p>However, now the site and interaction are empty.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">zope</span><span class="o">.</span><span class="n">security</span><span class="o">.</span><span class="n">management</span><span class="o">.</span><span class="n">queryInteraction</span><span class="p">()</span>
+<span class="go">None</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">hooks</span><span class="o">.</span><span class="n">getSite</span><span class="p">()</span>
+<span class="go">None</span>
+</pre></div>
+<p>As mentioned, the context will be maintained through the transaction&#8217;s commit.
+Let&#8217;s illustrate.</p>
+<blockquote>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">def</span> <span class="nf">setTransactionHook</span><span class="p">():</span>
+<span class="gp">... </span>    <span class="n">t</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">ITransactionManager</span><span class="p">(</span><span class="n">j</span><span class="p">)</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
+<span class="gp">... </span>    <span class="n">t</span><span class="o">.</span><span class="n">addBeforeCommitHook</span><span class="p">(</span><span class="n">reportOnContext</span><span class="p">)</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">hooks</span><span class="o">.</span><span class="n">setSite</span><span class="p">(</span><span class="n">site</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">security</span><span class="o">.</span><span class="n">management</span><span class="o">.</span><span class="n">newInteraction</span><span class="p">(</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">z3</span><span class="o">.</span><span class="n">Participation</span><span class="p">(</span><span class="n">mickey</span><span class="p">),</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">z3</span><span class="o">.</span><span class="n">Participation</span><span class="p">(</span><span class="n">jack</span><span class="p">),</span>
+<span class="gp">... </span>    <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">z3</span><span class="o">.</span><span class="n">Participation</span><span class="p">(</span><span class="n">foo</span><span class="p">))</span> <span class="c"># &gt;1 == rare but possible scenario</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="s">&#39;j&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">z3</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">setTransactionHook</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="o">.</span><span class="n">participants</span>
+<span class="go">(&#39;mickey&#39;, &#39;jack&#39;, &#39;foo&#39;)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="o">.</span><span class="n">site</span> <span class="ow">is</span> <span class="n">site</span>
+<span class="go">True</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">security</span><span class="o">.</span><span class="n">management</span><span class="o">.</span><span class="n">endInteraction</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">hooks</span><span class="o">.</span><span class="n">setSite</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="p">()</span>
+<span class="go">(&#39;StubSite&#39;, (&#39;mickey&#39;, &#39;jack&#39;, &#39;foo&#39;))</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">zope</span><span class="o">.</span><span class="n">security</span><span class="o">.</span><span class="n">management</span><span class="o">.</span><span class="n">queryInteraction</span><span class="p">()</span>
+<span class="go">None</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">hooks</span><span class="o">.</span><span class="n">getSite</span><span class="p">()</span>
+<span class="go">None</span>
+</pre></div>
+</blockquote>
+<p class="rubric">Footnotes</p>
+<table class="docutils footnote" frame="void" id="zope3job-database-setup" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id1">[1]</a></td><td><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">ZODB.tests.util</span> <span class="kn">import</span> <span class="n">DB</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">db</span> <span class="o">=</span> <span class="n">DB</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">conn</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">open</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">root</span> <span class="o">=</span> <span class="n">conn</span><span class="o">.</span><span class="n">root</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.configure</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">configure</span><span class="o">.</span><span class="n">base</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.testing</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">testing</span><span class="o">.</span><span class="n">setUpDatetime</span><span class="p">()</span> <span class="c"># pins datetimes</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="empty-setup" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id2">[2]</a></td><td><p class="first">Without a site or an interaction, you can still instantiate
+and run the job normally.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zc.async.z3</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">operator</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="s">&#39;j&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">zc</span><span class="o">.</span><span class="n">async</span><span class="o">.</span><span class="n">z3</span><span class="o">.</span><span class="n">Job</span><span class="p">(</span><span class="n">operator</span><span class="o">.</span><span class="n">mul</span><span class="p">,</span> <span class="mf">6</span><span class="p">,</span> <span class="mf">7</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="o">.</span><span class="n">participants</span>
+<span class="go">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">print</span> <span class="n">j</span><span class="o">.</span><span class="n">site</span>
+<span class="go">None</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">transaction</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">j</span><span class="p">()</span>
+<span class="go">42</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+<table class="docutils footnote" frame="void" id="zope3job-setup" rules="none">
+<colgroup><col class="label" /><col /></colgroup>
+<tbody valign="top">
+<tr><td class="label"><a class="fn-backref" href="#id3">[3]</a></td><td><p class="first">To do this, we need to set up the zope.app.component
+hooks, create a site, set up an authentication utility, and create some
+principals that the authentication utility can return.</p>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.app.component.hooks</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">hooks</span><span class="o">.</span><span class="n">setHooks</span><span class="p">()</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.app.component.site</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">persistent</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">class</span> <span class="nc">StubSite</span><span class="p">(</span><span class="n">persistent</span><span class="o">.</span><span class="n">Persistent</span><span class="p">,</span>
+<span class="gp">... </span>               <span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">SiteManagerContainer</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="k">pass</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">site</span> <span class="o">=</span> <span class="n">root</span><span class="p">[</span><span class="s">&#39;site&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">StubSite</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">sm</span> <span class="o">=</span> <span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">component</span><span class="o">.</span><span class="n">site</span><span class="o">.</span><span class="n">LocalSiteManager</span><span class="p">(</span><span class="n">site</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">site</span><span class="o">.</span><span class="n">setSiteManager</span><span class="p">(</span><span class="n">sm</span><span class="p">)</span>
+</pre></div>
+<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.security.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.app.security.interfaces</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.interface</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">zope.location</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">class</span> <span class="nc">StubPrincipal</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">zope</span><span class="o">.</span><span class="n">interface</span><span class="o">.</span><span class="n">implements</span><span class="p">(</span><span class="n">zope</span><span class="o">.</span><span class="n">security</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IPrincipal</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">identifier</span><span class="p">,</span> <span class="n">title</span><span class="p">,</span> <span class="n">description</span><span class="o">=</span><span class="s">&#39;&#39;</span><span class="p">):</span>
+<span class="gp">... </span>        <span class="bp">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">identifier</span>
+<span class="gp">... </span>        <span class="bp">self</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="n">title</span>
+<span class="gp">... </span>        <span class="bp">self</span><span class="o">.</span><span class="n">description</span> <span class="o">=</span> <span class="n">description</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="k">class</span> <span class="nc">StubPersistentAuth</span><span class="p">(</span><span class="n">persistent</span><span class="o">.</span><span class="n">Persistent</span><span class="p">,</span>
+<span class="gp">... </span>                         <span class="n">zope</span><span class="o">.</span><span class="n">location</span><span class="o">.</span><span class="n">Location</span><span class="p">):</span>
+<span class="gp">... </span>    <span class="n">zope</span><span class="o">.</span><span class="n">interface</span><span class="o">.</span><span class="n">implements</span><span class="p">(</span>
+<span class="gp">... </span>        <span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">security</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IAuthentication</span><span class="p">)</span>
+<span class="gp">... </span>    <span class="n">_mapping</span> <span class="o">=</span> <span class="p">{</span><span class="s">&#39;foo&#39;</span><span class="p">:</span> <span class="s">&#39;Foo Fighter&#39;</span><span class="p">,</span>
+<span class="gp">... </span>                <span class="s">&#39;jack&#39;</span><span class="p">:</span> <span class="s">&#39;Jack, Giant Killer&#39;</span><span class="p">,</span>
+<span class="gp">... </span>                <span class="s">&#39;mickey&#39;</span><span class="p">:</span> <span class="s">&#39;Mickey Mouse&#39;</span><span class="p">}</span>
+<span class="gp">... </span>    <span class="k">def</span> <span class="nf">getPrincipal</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">principal_id</span><span class="p">):</span>
+<span class="gp">... </span>        <span class="k">return</span> <span class="n">StubPrincipal</span><span class="p">(</span><span class="n">principal_id</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_mapping</span><span class="p">[</span><span class="n">principal_id</span><span class="p">])</span>
+<span class="gp">...</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">auth</span> <span class="o">=</span> <span class="n">StubPersistentAuth</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">sm</span><span class="o">.</span><span class="n">registerUtility</span><span class="p">(</span><span class="n">auth</span><span class="p">,</span> <span class="n">zope</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">security</span><span class="o">.</span><span class="n">interfaces</span><span class="o">.</span><span class="n">IAuthentication</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">transaction</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">mickey</span> <span class="o">=</span> <span class="n">auth</span><span class="o">.</span><span class="n">getPrincipal</span><span class="p">(</span><span class="s">&#39;mickey&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">jack</span> <span class="o">=</span> <span class="n">auth</span><span class="o">.</span><span class="n">getPrincipal</span><span class="p">(</span><span class="s">&#39;jack&#39;</span><span class="p">)</span>
+<span class="gp">&gt;&gt;&gt; </span><span class="n">foo</span> <span class="o">=</span> <span class="n">auth</span><span class="o">.</span><span class="n">getPrincipal</span><span class="p">(</span><span class="s">&#39;foo&#39;</span><span class="p">)</span>
+</pre></div>
+</td></tr>
+</tbody>
+</table>
+</div>
+
+
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="_static/zc_async.png" alt="Logo"/></p>
+            <h4>Previous topic</h4>
+            <p class="topless"><a href="catastrophes.html" title="previous chapter">Recovering from Catastrophes</a></p>
+            <h4>Next topic</h4>
+            <p class="topless"><a href="ftesting.html" title="next chapter">Zope 3 Testing Tips and Tricks</a></p>
+            <h3>This Page</h3>
+            <ul class="this-page-menu">
+              <li><a href="_sources/z3.txt">Show Source</a></li>
+            </ul>
+            <h3>Quick search</h3>
+            <form class="search" action="search.html" method="get">
+              <input type="text" name="q" size="18" /> <input type="submit" value="Go" />
+              <input type="hidden" name="check_keywords" value="yes" />
+              <input type="hidden" name="area" value="default" />
+            </form>
+        </div>
+      </div>
+      <div class="clearer"></div>
+    </div>
+    <div class="related">
+      <h3>Navigation</h3>
+      <ul>
+        <li class="right" style="margin-right: 10px">
+          <a href="genindex.html" title="General Index"
+             accesskey="I">index</a></li>
+        <li class="right" >
+          <a href="modindex.html" title="Global Module Index"
+             accesskey="M">modules</a> |</li>
+        <li class="right" >
+          <a href="ftesting.html" title="Zope 3 Testing Tips and Tricks"
+             accesskey="N">next</a> |</li>
+        <li class="right" >
+          <a href="catastrophes.html" title="Recovering from Catastrophes"
+             accesskey="P">previous</a> |</li>
+        <li><a href="index.html">zc.async v1.5.0 documentation</a> &raquo;</li>
+          <li><a href="tips.html" accesskey="U">Tips and Tricks</a> &raquo;</li>
+      </ul>
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008, Gary Poster.
+      Last updated on Sep 21, 2008.
+      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
+    </div>
+  </body>
+</html>
\ No newline at end of file

Added: zc.async/trunk/htmldocs/index.html
===================================================================
--- zc.async/trunk/htmldocs/index.html	                        (rev 0)
+++ zc.async/trunk/htmldocs/index.html	2008-09-22 02:38:23 UTC (rev 91302)
@@ -0,0 +1,32 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>zc.async &mdash; zc.async documentation</title>
+    <link rel="stylesheet" href="1.5.0/_static/default.css" type="text/css" />
+    <link rel="stylesheet" href="1.5.0/_static/pygments.css" type="text/css" />
+    <link rel="shortcut icon" href="_static/favicon.ico"/>
+  </head>
+  <body>
+    <div class="document">
+      <div class="documentwrapper">
+        <div class="bodywrapper">
+          <div class="body">
+            
+  
+  <div class="section" id="async">
+<h1 id="async"><tt class="docutils literal"><span class="pre">zc.async</span>: Documentation by Version</tt></h1>
+<div class="section" id="1.5.0">
+<h2 id="1.5.0"><a href="1.5.0/index.html" title="1.5.0">1.5.0</a></h2>
+          </div>
+        </div>
+          </div>
+        </div>
+      </div>
+      <div class="sphinxsidebar">
+        <div class="sphinxsidebarwrapper">
+            <p class="logo"><img class="logo" src="1.5.0/_static/zc_async.png" alt="Logo"/></p>
+        </div>
+      </div>
+  </body>
+</html>



More information about the Checkins mailing list