Revision: 12
Author:
mike.a...@gmail.com
Date: Mon Nov 15 04:53:40 2010
Log: updated documentation
http://code.google.com/p/appengine-afterburner/source/detail?r=12
Added:
/doc/python/html/_modules
/doc/python/html/_modules/afterburner
/doc/python/html/_modules/afterburner/experimental
/doc/python/html/_modules/afterburner/experimental/db
/doc/python/html/_modules/afterburner/experimental/db/triggers.html
/doc/python/html/_modules/afterburner/experimental/memoize.html
/doc/python/html/_modules/afterburner/experimental/quota.html
/doc/python/html/_modules/index.html
/doc/python/html/py-modindex.html
/trunk/python/doc
/trunk/python/doc/Makefile
/trunk/python/doc/conf.py
Deleted:
/trunk/python/Makefile
/trunk/python/src/conf.py
Modified:
/doc/python/doctrees/afterburner/experimental/db/triggers.doctree
/doc/python/doctrees/afterburner/experimental/memoize.doctree
/doc/python/doctrees/afterburner/experimental/quota.doctree
/doc/python/doctrees/environment.pickle
/doc/python/doctrees/index.doctree
/doc/python/html/afterburner/experimental/db/triggers.html
/doc/python/html/afterburner/experimental/memoize.html
/doc/python/html/afterburner/experimental/quota.html
/doc/python/html/genindex.html
/doc/python/html/index.html
/doc/python/html/objects.inv
/doc/python/html/search.html
/doc/python/html/searchindex.js
/trunk/python/build.sh
=======================================
--- /dev/null
+++ /doc/python/html/_modules/afterburner/experimental/db/triggers.html Mon
Nov 15 04:53:40 2010
@@ -0,0 +1,408 @@
+
+
+<!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>afterburner.experimental.db.triggers —
Appengine-Afterburner v0.0.1 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: '0.0.1',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript"
src="../../../../_static/jquery.js"></script>
+ <script type="text/javascript"
src="../../../../_static/underscore.js"></script>
+ <script type="text/javascript"
src="../../../../_static/doctools.js"></script>
+ <link rel="top" title="Appengine-Afterburner v0.0.1 documentation"
href="../../../../index.html" />
+ <link rel="up" title="Module code" 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="../../../../py-modindex.html" title="Python Module
Index"
+ >modules</a> |</li>
+ <li><a href="../../../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
+ <li><a href="../../../index.html" accesskey="U">Module code</a>
»</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <h1>Source code for afterburner.experimental.db.triggers</h1><div
class="highlight"><pre>
+<span class="c">#!/usr/bin/env python</span>
+<span class="c">#</span>
+<span class="c"># Copyright 2010 Google Inc.</span>
+<span class="c">#</span>
+<span class="c"># Licensed under the Apache License, Version 2.0 (the
"License");</span>
+<span class="c"># you may not use this file except in compliance with the
License.</span>
+<span class="c"># You may obtain a copy of the License at</span>
+<span class="c">#</span>
+<span class="c">#
http://www.apache.org/licenses/LICENSE-2.0</span>
+<span class="c">#</span>
+<span class="c"># Unless required by applicable law or agreed to in
writing, software</span>
+<span class="c"># distributed under the License is distributed on an
"AS IS" BASIS,</span>
+<span class="c"># WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied.</span>
+<span class="c"># See the License for the specific language governing
permissions and</span>
+<span class="c"># limitations under the License.</span>
+
+
+<span class="sd">"""This module contains datastore trigger
system implementation.</span>
+
+<span class="sd">Overview</span>
+<span class="sd">========</span>
+
+<span class="sd">The system is designed to execute user's code every
time an entity of specific</span>
+<span class="sd">kind is changed by application. Right now the only
execution model is delayed</span>
+<span class="sd">execution (via taskqueue), which makes it useful only in
eventually-consistent</span>
+<span class="sd">scenarios.</span>
+
+<span class="sd">It is important to remember that:</span>
+
+<span class="sd">* Triggers won't be fired if you change your data not
from the app itself (e.g.</span>
+<span class="sd"> admin console).</span>
+<span class="sd">* There might be a delay (sometimes even minute) between
actual data change and</span>
+<span class="sd"> trigger execution.</span>
+<span class="sd">* Trigger executions for a single entity are batched.
I.e. if you change entity</span>
+<span class="sd"> quickly multiple times you'll most likely get your
trigger function called</span>
+<span class="sd"> only once.</span>
+
+<span class="sd">Current Limitations:</span>
+
+<span class="sd">* Right now only cron-based updates processing is
implemented. This means that</span>
+<span class="sd"> the delay between event and trigger processing might be
about 1 minute in</span>
+<span class="sd"> worst case.</span>
+<span class="sd">* Due to limitations of current datastore api batch
deletes are executed</span>
+<span class="sd"> by triggers module as separate transactions. This may
decrease performance</span>
+<span class="sd"> of deleting code quite a lot.</span>
+<span class="sd">* It is estimated that with this design you won't be
able to process more than</span>
+<span class="sd"> 60 changes per second. See the implementation details
and planned improvements</span>
+<span class="sd"> sections for more details.</span>
+
+
+<span class="sd">Usage</span>
+<span class="sd">=====</span>
+
+<span class="sd">To use triggers:</span>
+
+<span class="sd">* Add the following code to your app.yaml::</span>
+
+<span class="sd"> includes:</span>
+<span class="sd"> - afterburner/experimental/db/triggers.yaml</span>
+
+<span class="sd">* Add following lines to your cron.yaml::</span>
+
+<span class="sd"> cron:</span>
+<span class="sd"> - description: triggers processing</span>
+<span class="sd"> url: /_ab/triggers/process</span>
+<span class="sd"> schedule: every 1 minutes</span>
+
+<span class="sd">* Create appengine_config.py in application root
directory and import your</span>
+<span class="sd"> modules with all trigger definitions from it.</span>
+<span class="sd">* Define triggers as following::</span>
+
+<span class="sd"> from afterburner.experimental.db import
triggers</span>
+<span class="sd"> @triggers.trigger(kind=Order.kind())</span>
+<span class="sd"> def order_trigger(key):</span>
+<span class="sd"> logging.debug("Order was changed: %s",
key)</span>
+
+<span class="sd"> Trigger function only argument is changed entity key.
Because trigger execution</span>
+<span class="sd"> is decoupled in time from actual db operation, the
information about operation</span>
+<span class="sd"> is not provided.</span>
+<span class="sd">* Make sure that you're using run_wsgi_app() function
to run your app and not</span>
+<span class="sd"> older approach. For details check appengine
documentation:</span>
+<span class="sd">
http://code.google.com/appengine/docs/python/tools/webapp/running.html</span>
+
+
+<span class="sd">Implementation Details</span>
+<span class="sd">======================</span>
+
+<span class="sd">To implement transactionally-safe triggers for every
entity change we detect,</span>
+<span class="sd">we create a child "Update" entity, which is
later processed by updater</span>
+<span class="sd">cron job. Updater cron job deletes Update entities and
queues tasks for executing</span>
+<span class="sd">triggers for particular entities.</span>
+
+<span class="sd">Adding child entities to puts doesn't change runtime
behavior of the system</span>
+<span class="sd">at all, because datastore internally groups all entities
in batch puts into</span>
+<span class="sd">entity groups and performs their write together in one
transaction automatically.</span>
+
+<span class="sd">Unfortunately it's currently not possible to use the
same approach to batch</span>
+<span class="sd">deletes. We disassemble batch delete and delete each
entity individually in</span>
+<span class="sd">its own transaction together with creating an update
entity.</span>
+<span class="sd">Ideally datastore should provide an api where you can
batch all kinds of</span>
+<span class="sd">operations together.</span>
+
+
+<span class="sd">Alternative Approaches</span>
+<span class="sd">======================</span>
+
+<span class="sd">This section outlines alternative approaches to triggers
system designs. Some</span>
+<span class="sd">of these approaches can augment already existing ones to
improve performance</span>
+<span class="sd">and other characteristics.</span>
+
+<span class="sd">Transactional Tasks</span>
+<span class="sd">-------------------</span>
+
+<span class="sd">Instead of creating update tasks it's possible to use
transactional tasks</span>
+<span class="sd">to queue triggers processing for each entity. This works
perfectly for</span>
+<span class="sd">writes in transaction, but doesn't work good in case
of batch operations</span>
+<span class="sd">across multiple entity groups: you have to disassemble
both puts and gets</span>
+<span class="sd">the same way we disassemble deletes right now.</span>
+
+<span class="sd">The benefit of this approach is that by eliminating
Update entity and</span>
+<span class="sd">update processor, trigger latency can be made quite
low.</span>
+
+<span class="sd">Another caveat with this approach is task's 10K
payload size limit and limit</span>
+<span class="sd">of 5 transactional tasks per transaction. If there are
lots of entities in a</span>
+<span class="sd">single transaction, it might be difficult to store keys
for all of them</span>
+<span class="sd">in 5 tasks. However, it looks like this limitation is not
a problem for most</span>
+<span class="sd">apps.</span>
+
+<span class="sd">Immediate Triggers Execution</span>
+<span class="sd">----------------------------</span>
+
+<span class="sd">It was a specific goal for this library to implement
delayed triggers execution.</span>
+<span class="sd">Immediate trigger execution can be significant utility
for certain use cases.</span>
+
+
+<span class="sd">Future Improvements</span>
+<span class="sd">===================</span>
+
+<span class="sd">Following improvements to the library are
considered:</span>
+
+<span class="sd">* Use transactional tasks for triggers enqueing in
situtations where it's</span>
+<span class="sd"> possible (non-batched puts/deletes, small
transactions)</span>
+<span class="sd">* Optional unbatched trigger execution</span>
+<span class="sd">* Augment current cron execution with chained tasks. This
will allow bringing</span>
+<span class="sd"> trigger latency down.</span>
+
+<span class="sd">One of these can be an excellent project for beginning
contributors.</span>
+<span class="sd">"""</span>
+
+
+<span class="kn">import</span> <span class="nn">logging</span>
+<span class="kn">import</span> <span class="nn">urllib</span>
+
+<span class="kn">from</span> <span class="nn">afterburner</span> <span
class="kn">import</span> <span class="n">util</span>
+<span class="kn">from</span> <span class="nn">google.appengine.api</span>
<span class="kn">import</span> <span class="n">apiproxy_stub_map</span>
+<span class="kn">from</span> <span class="nn">google.appengine.api</span>
<span class="kn">import</span> <span class="n">datastore</span>
+<span class="kn">from</span> <span class="nn">google.appengine.api</span>
<span class="kn">import</span> <span class="n">urlfetch</span>
+<span class="kn">from</span> <span
class="nn">google.appengine.api.labs</span> <span class="kn">import</span>
<span class="n">taskqueue</span>
+<span class="kn">from</span> <span class="nn">google.appengine.ext</span>
<span class="kn">import</span> <span class="n">db</span>
+<span class="kn">from</span> <span class="nn">google.appengine.ext</span>
<span class="kn">import</span> <span class="n">webapp</span>
+<span class="kn">from</span> <span
class="nn">google.appengine.ext.webapp</span> <span
class="kn">import</span> <span class="n">template</span>
+
+
+<span class="n">PROCESS_URL</span> <span class="o">=</span> <span
class="s">"/_ab/triggers/process"</span>
+<span class="n">UPDATE_URL</span> <span class="o">=</span> <span
class="s">"/_ab/triggers/update"</span>
+
+
+<span class="k">class</span> <span class="nc">Update</span><span
class="p">(</span><span class="n">db</span><span class="o">.</span><span
class="n">Model</span><span class="p">):</span>
+ <span class="nd">@classmethod</span>
+ <span class="k">def</span> <span class="nf">kind</span><span
class="p">(</span><span class="n">cls</span><span class="p">):</span>
+ <span class="k">return</span> <span
class="s">"_ab_triggers_Update"</span>
+
+
+<span class="k">class</span> <span class="nc">TriggerManager</span><span
class="p">(</span><span class="nb">object</span><span class="p">):</span>
+ <span class="c"># entity kind => [trigger functions]</span>
+ <span class="n">triggers</span> <span class="o">=</span> <span
class="p">{}</span>
+ <span class="n">ignore_deletes</span> <span class="o">=</span> <span
class="bp">False</span>
+ <span class="n">initialized</span> <span class="o">=</span> <span
class="bp">False</span>
+
+ <span class="nd">@classmethod</span>
+ <span class="k">def</span> <span class="nf">pre_hook</span><span
class="p">(</span><span class="n">cls</span><span class="p">,</span> <span
class="n">service</span><span class="p">,</span> <span
class="n">call</span><span class="p">,</span> <span
class="n">request</span><span class="p">,</span> <span
class="n">response</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">call</span> <span
class="o">==</span> <span class="s">"Put"</span><span
class="p">:</span>
+ <span class="n">extra_entities</span> <span class="o">=</span> <span
class="p">[]</span>
+ <span class="k">for</span> <span class="n">entity</span> <span
class="ow">in</span> <span class="n">request</span><span
class="o">.</span><span class="n">entity_list</span><span
class="p">():</span>
+ <span class="n">key</span> <span class="o">=</span> <span
class="n">entity</span><span class="o">.</span><span
class="n">mutable_key</span><span class="p">()</span>
+ <span class="n">path</span> <span class="o">=</span> <span
class="n">key</span><span class="o">.</span><span
class="n">mutable_path</span><span class="p">()</span>
+ <span class="n">last_path_element</span> <span class="o">=</span>
<span class="n">path</span><span class="o">.</span><span
class="n">element_list</span><span class="p">()[</span><span
class="o">-</span><span class="mi">1</span><span class="p">]</span>
+ <span class="k">if</span> <span class="p">(</span><span
class="n">last_path_element</span><span class="o">.</span><span
class="n">type</span><span class="p">()</span> <span class="o">==</span>
<span class="n">Update</span><span class="o">.</span><span
class="n">kind</span><span class="p">()</span>
+ <span class="ow">or</span> <span
class="n">last_path_element</span><span class="o">.</span><span
class="n">type</span><span class="p">()</span> <span class="ow">not</span>
<span class="ow">in</span> <span class="n">cls</span><span
class="o">.</span><span class="n">triggers</span><span class="p">):</span>
+ <span class="c"># no triggers for this entity kind.</span>
+ <span class="k">continue</span>
+ <span class="k">if</span> <span class="ow">not</span> <span
class="n">last_path_element</span><span class="o">.</span><span
class="n">id</span><span class="p">()</span> <span class="ow">and</span>
<span class="ow">not</span> <span class="n">last_path_element</span><span
class="o">.</span><span class="n">has_name</span><span class="p">():</span>
+ <span class="c"># assign id to the entity</span>
+ <span class="n">last_path_element</span><span
class="o">.</span><span class="n">set_id</span><span
class="p">(</span><span class="o">-</span><span class="mi">1</span><span
class="p">)</span>
+ <span class="n">db_key</span> <span class="o">=</span> <span
class="n">db</span><span class="o">.</span><span class="n">Key</span><span
class="o">.</span><span class="n">_FromPb</span><span
class="p">(</span><span class="n">key</span><span class="p">)</span>
+ <span class="n">allocated_id</span> <span class="o">=</span>
<span class="n">db</span><span class="o">.</span><span
class="n">allocate_ids</span><span class="p">(</span><span
class="n">db_key</span><span class="p">,</span> <span
class="mi">1</span><span class="p">)[</span><span class="mi">0</span><span
class="p">]</span>
+ <span class="n">last_path_element</span><span
class="o">.</span><span class="n">set_id</span><span
class="p">(</span><span class="n">allocated_id</span><span
class="p">)</span>
+
+ <span class="c"># Populate entity group, because datastore stub
is stupid.</span>
+ <span class="c"># Actual datastore doesn't need it, but it
won't hurt anyway.</span>
+ <span class="n">group</span> <span class="o">=</span> <span
class="n">entity</span><span class="o">.</span><span
class="n">mutable_entity_group</span><span class="p">()</span>
+ <span class="n">root</span> <span class="o">=</span> <span
class="n">entity</span><span class="o">.</span><span
class="n">key</span><span class="p">()</span><span class="o">.</span><span
class="n">path</span><span class="p">()</span><span class="o">.</span><span
class="n">element</span><span class="p">(</span><span
class="mi">0</span><span class="p">)</span>
+ <span class="n">group</span><span class="o">.</span><span
class="n">add_element</span><span class="p">()</span><span
class="o">.</span><span class="n">CopyFrom</span><span
class="p">(</span><span class="n">root</span><span class="p">)</span>
+ <span class="n">update_entity</span> <span class="o">=</span>
<span class="n">Update</span><span class="p">(</span>
+ <span class="n">key_name</span><span class="o">=</span><span
class="nb">str</span><span class="p">(</span><span class="n">db</span><span
class="o">.</span><span class="n">Key</span><span class="o">.</span><span
class="n">_FromPb</span><span class="p">(</span><span
class="n">key</span><span class="p">)),</span>
+ <span class="n">parent</span><span class="o">=</span><span
class="n">db</span><span class="o">.</span><span class="n">Key</span><span
class="o">.</span><span class="n">_FromPb</span><span
class="p">(</span><span class="n">key</span><span class="p">))</span><span
class="o">.</span><span class="n">_populate_entity</span><span
class="p">()</span>
+ <span class="n">extra_entities</span><span class="o">.</span><span
class="n">append</span><span class="p">(</span><span
class="n">update_entity</span><span class="o">.</span><span
class="n">_ToPb</span><span class="p">())</span>
+ <span class="n">request</span><span class="o">.</span><span
class="n">entity_list</span><span class="p">()</span><span
class="o">.</span><span class="n">extend</span><span
class="p">(</span><span class="n">extra_entities</span><span
class="p">)</span>
+ <span class="k">elif</span> <span class="n">call</span> <span
class="o">==</span> <span class="s">"Delete"</span> <span
class="ow">and</span> <span class="ow">not</span> <span
class="n">cls</span><span class="o">.</span><span
class="n">ignore_deletes</span><span class="p">:</span>
+ <span class="n">cls</span><span class="o">.</span><span
class="n">ignore_deletes</span> <span class="o">=</span> <span
class="bp">True</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="k">if</span> <span class="n">request</span><span
class="o">.</span><span class="n">has_transaction</span><span
class="p">():</span>
+ <span class="k">for</span> <span class="n">key</span> <span
class="ow">in</span> <span class="n">request</span><span
class="o">.</span><span class="n">key_list</span><span class="p">():</span>
+ <span class="n">update</span> <span class="o">=</span> <span
class="n">Update</span><span class="p">(</span>
+ <span class="n">key_name</span><span
class="o">=</span><span class="nb">str</span><span class="p">(</span><span
class="n">db</span><span class="o">.</span><span class="n">Key</span><span
class="o">.</span><span class="n">_FromPb</span><span
class="p">(</span><span class="n">key</span><span class="p">)),</span>
+ <span class="n">parent</span><span class="o">=</span><span
class="n">db</span><span class="o">.</span><span class="n">Key</span><span
class="o">.</span><span class="n">_FromPb</span><span
class="p">(</span><span class="n">key</span><span class="p">))</span>
+ <span class="n">db</span><span class="o">.</span><span
class="n">put</span><span class="p">(</span><span
class="n">update</span><span class="p">)</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="n">keys</span> <span class="o">=</span> <span
class="n">request</span><span class="o">.</span><span
class="n">key_list</span><span class="p">()</span>
+ <span class="n">request</span><span class="o">.</span><span
class="n">clear_key</span><span class="p">()</span>
+ <span class="k">for</span> <span class="n">key</span> <span
class="ow">in</span> <span class="n">keys</span><span class="p">:</span>
+ <span class="c"># TODO: group keys by entity groups</span>
+ <span class="k">def</span> <span class="nf">tx</span><span
class="p">():</span>
+ <span class="n">db_key</span> <span class="o">=</span> <span
class="n">db</span><span class="o">.</span><span class="n">Key</span><span
class="o">.</span><span class="n">_FromPb</span><span
class="p">(</span><span class="n">key</span><span class="p">)</span>
+ <span class="n">update</span> <span class="o">=</span> <span
class="n">Update</span><span class="p">(</span><span
class="n">key_name</span><span class="o">=</span><span
class="nb">str</span><span class="p">(</span><span
class="n">db_key</span><span class="p">),</span> <span
class="n">parent</span><span class="o">=</span><span
class="n">db_key</span><span class="p">)</span>
+ <span class="n">db</span><span class="o">.</span><span
class="n">put</span><span class="p">(</span><span
class="n">update</span><span class="p">)</span>
+ <span class="n">db</span><span class="o">.</span><span
class="n">delete</span><span class="p">(</span><span
class="n">db_key</span><span class="p">)</span>
+ <span class="n">db</span><span class="o">.</span><span
class="n">run_in_transaction</span><span class="p">(</span><span
class="n">tx</span><span class="p">)</span>
+ <span class="k">finally</span><span class="p">:</span>
+ <span class="n">cls</span><span class="o">.</span><span
class="n">ignore_deletes</span> <span class="o">=</span> <span
class="bp">False</span>
+
+ <span class="nd">@classmethod</span>
+ <span class="k">def</span> <span class="nf">post_hook</span><span
class="p">(</span><span class="n">cls</span><span class="p">,</span> <span
class="n">service</span><span class="p">,</span> <span
class="n">call</span><span class="p">,</span> <span
class="n">request</span><span class="p">,</span> <span
class="n">response</span><span class="p">):</span>
+ <span class="k">if</span> <span class="n">call</span> <span
class="o">==</span> <span class="s">"Put"</span> <span
class="ow">and</span> <span class="ow">not</span> <span
class="n">cls</span><span class="o">.</span><span
class="n">ignore_deletes</span><span class="p">:</span>
+ <span class="n">keys</span> <span class="o">=</span> <span
class="n">response</span><span class="o">.</span><span
class="n">key_list</span><span class="p">()</span>
+ <span class="n">response</span><span class="o">.</span><span
class="n">clear_key</span><span class="p">()</span>
+ <span class="n">keys</span> <span class="o">=</span> <span
class="p">([</span><span class="n">key</span> <span class="k">for</span>
<span class="n">key</span> <span class="ow">in</span> <span
class="n">keys</span>
+ <span class="k">if</span> <span class="n">key</span><span
class="o">.</span><span class="n">path</span><span class="p">()</span><span
class="o">.</span><span class="n">element_list</span><span
class="p">()[</span><span class="o">-</span><span class="mi">1</span><span
class="p">]</span><span class="o">.</span><span class="n">type</span><span
class="p">()</span> <span class="o">!=</span> <span
class="n">Update</span><span class="o">.</span><span
class="n">kind</span><span class="p">()])</span>
+ <span class="n">response</span><span class="o">.</span><span
class="n">key_list</span><span class="p">()</span><span
class="o">.</span><span class="n">extend</span><span
class="p">(</span><span class="n">keys</span><span class="p">)</span>
+
+ <span class="nd">@classmethod</span>
+ <span class="k">def</span> <span class="nf">init</span><span
class="p">(</span><span class="n">cls</span><span class="p">):</span>
+ <span class="n">apiproxy_stub_map</span><span class="o">.</span><span
class="n">apiproxy</span><span class="o">.</span><span
class="n">GetPreCallHooks</span><span class="p">()</span><span
class="o">.</span><span class="n">Append</span><span class="p">(</span>
+ <span class="s">'trigger_manager'</span><span
class="p">,</span> <span class="n">cls</span><span class="o">.</span><span
class="n">pre_hook</span><span class="p">,</span> <span
class="s">'datastore_v3'</span><span class="p">)</span>
+ <span class="n">apiproxy_stub_map</span><span class="o">.</span><span
class="n">apiproxy</span><span class="o">.</span><span
class="n">GetPostCallHooks</span><span class="p">()</span><span
class="o">.</span><span class="n">Append</span><span class="p">(</span>
+ <span class="s">'trigger_manager'</span><span
class="p">,</span> <span class="n">cls</span><span class="o">.</span><span
class="n">post_hook</span><span class="p">,</span> <span
class="s">'datastore_v3'</span><span class="p">)</span>
+
+ <span class="nd">@classmethod</span>
+ <span class="k">def</span> <span class="nf">init_if_needed</span><span
class="p">(</span><span class="n">cls</span><span class="p">):</span>
+ <span class="k">if</span> <span class="ow">not</span> <span
class="n">cls</span><span class="o">.</span><span
class="n">initialized</span><span class="p">:</span>
+ <span class="n">cls</span><span class="o">.</span><span
class="n">initialized</span> <span class="o">=</span> <span
class="bp">True</span>
+ <span class="n">cls</span><span class="o">.</span><span
class="n">init</span><span class="p">()</span>
+
+ <span class="nd">@classmethod</span>
+ <span class="k">def</span> <span class="nf">register_trigger</span><span
class="p">(</span><span class="n">cls</span><span class="p">,</span> <span
class="n">entity_kind</span><span class="p">,</span> <span
class="n">function_name</span><span class="p">):</span>
+ <span class="n">cls</span><span class="o">.</span><span
class="n">init_if_needed</span><span class="p">()</span>
+ <span class="c"># TODO: double registration</span>
+ <span class="n">logging</span><span class="o">.</span><span
class="n">error</span><span class="p">(</span><span
class="s">"register_trigger: </span><span class="si">%s</span><span
class="s">, </span><span class="si">%s</span><span
class="s">"</span><span class="p">,</span> <span
class="n">entity_kind</span><span class="p">,</span> <span
class="n">function_name</span><span class="p">)</span>
+ <span class="n">cls</span><span class="o">.</span><span
class="n">triggers</span><span class="p">[</span><span
class="n">entity_kind</span><span class="p">]</span> <span
class="o">=</span> <span class="p">(</span>
+ <span class="n">cls</span><span class="o">.</span><span
class="n">triggers</span><span class="o">.</span><span
class="n">get</span><span class="p">(</span><span
class="n">entity_kind</span><span class="p">,</span> <span
class="p">[])</span> <span class="o">+</span> <span class="p">[</span><span
class="n">function_name</span><span class="p">])</span>
+
+ <span class="nd">@classmethod</span>
+ <span class="k">def</span> <span class="nf">get_triggers</span><span
class="p">(</span><span class="n">cls</span><span class="p">,</span> <span
class="n">entity_kind</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">cls</span><span
class="o">.</span><span class="n">triggers</span><span
class="o">.</span><span class="n">get</span><span class="p">(</span><span
class="n">entity_kind</span><span class="p">)</span>
+
+ <span class="nd">@classmethod</span>
+ <span class="k">def</span> <span class="nf">process_updates</span><span
class="p">(</span><span class="n">cls</span><span class="p">):</span>
+ <span class="n">cls</span><span class="o">.</span><span
class="n">ignore_deletes</span> <span class="o">=</span> <span
class="bp">True</span>
+ <span class="k">try</span><span class="p">:</span>
+ <span class="k">while</span> <span class="bp">True</span><span
class="p">:</span>
+ <span class="c"># TODO time limits are needed here</span>
+ <span class="n">updates</span> <span class="o">=</span> <span
class="n">Update</span><span class="o">.</span><span
class="n">all</span><span class="p">()</span><span class="o">.</span><span
class="n">fetch</span><span class="p">(</span><span
class="n">limit</span><span class="o">=</span><span
class="mi">50</span><span class="p">)</span>
+ <span class="k">if</span> <span class="ow">not</span> <span
class="n">updates</span><span class="p">:</span>
+ <span class="k">return</span>
+ <span class="k">for</span> <span class="n">update</span> <span
class="ow">in</span> <span class="n">updates</span><span class="p">:</span>
+ <span class="k">def</span> <span class="nf">tx</span><span
class="p">():</span>
+ <span class="n">db</span><span class="o">.</span><span
class="n">delete</span><span class="p">(</span><span
class="n">update</span><span class="o">.</span><span
class="n">key</span><span class="p">())</span>
+ <span class="n">taskqueue</span><span class="o">.</span><span
class="n">add</span><span class="p">(</span><span class="n">url</span><span
class="o">=</span><span class="n">UPDATE_URL</span><span class="p">,</span>
<span class="n">params</span><span class="o">=</span><span
class="p">{</span><span class="s">'key'</span><span
class="p">:</span> <span class="n">update</span><span
class="o">.</span><span class="n">key</span><span class="p">()</span><span
class="o">.</span><span class="n">name</span><span class="p">()},</span>
+ <span class="n">transactional</span><span
class="o">=</span><span class="bp">True</span><span class="p">)</span>
+ <span class="n">db</span><span class="o">.</span><span
class="n">run_in_transaction</span><span class="p">(</span><span
class="n">tx</span><span class="p">)</span>
+ <span class="k">finally</span><span class="p">:</span>
+ <span class="n">cls</span><span class="o">.</span><span
class="n">ignore_deletes</span> <span class="o">=</span> <span
class="bp">False</span>
+
+
+<span class="k">class</span> <span
class="nc">ProcessUpdatesHandler</span><span class="p">(</span><span
class="n">webapp</span><span class="o">.</span><span
class="n">RequestHandler</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">get</span><span
class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">TriggerManager</span><span class="o">.</span><span
class="n">process_updates</span><span class="p">()</span>
+
+
+<span class="k">class</span> <span
class="nc">UpdateEntityHandler</span><span class="p">(</span><span
class="n">webapp</span><span class="o">.</span><span
class="n">RequestHandler</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">post</span><span
class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="n">key</span> <span class="o">=</span> <span
class="n">db</span><span class="o">.</span><span class="n">Key</span><span
class="p">(</span><span class="bp">self</span><span class="o">.</span><span
class="n">request</span><span class="o">.</span><span
class="n">get</span><span class="p">(</span><span
class="s">"key"</span><span class="p">))</span>
+ <span class="n">triggers</span> <span class="o">=</span> <span
class="n">TriggerManager</span><span class="o">.</span><span
class="n">get_triggers</span><span class="p">(</span><span
class="n">key</span><span class="o">.</span><span
class="n">kind</span><span class="p">())</span>
+ <span class="k">if</span> <span class="ow">not</span> <span
class="n">triggers</span><span class="p">:</span>
+ <span class="k">return</span>
+ <span class="k">for</span> <span class="n">trigger</span> <span
class="ow">in</span> <span class="n">triggers</span><span class="p">:</span>
+ <span class="n">util</span><span class="o">.</span><span
class="n">for_name</span><span class="p">(</span><span
class="n">trigger</span><span class="p">)(</span><span
class="n">key</span><span class="p">)</span>
+
+
+<div class="viewcode-block" id="trigger"><a class="viewcode-back"
href="../../../../afterburner/experimental/db/triggers.html#afterburner.experimental.db.triggers.trigger">[docs]</a><span
class="k">def</span> <span class="nf">trigger</span><span
class="p">(</span><span class="n">kind</span><span class="p">):</span>
+ <span class="sd">"""Trigger registering decorator.</span>
+
+<span class="sd"> Args:</span>
+<span class="sd"> kind: entity kind to attach defined trigger to.</span>
+<span class="sd"> """</span>
+ <span class="n">TriggerManager</span><span class="o">.</span><span
class="n">init_if_needed</span><span class="p">()</span>
+ <span class="k">def</span> <span class="nf">decorator</span><span
class="p">(</span><span class="n">f</span><span class="p">):</span>
+ <span class="n">trigger_name</span> <span class="o">=</span> <span
class="n">util</span><span class="o">.</span><span
class="n">fq_name</span><span class="p">(</span><span
class="n">f</span><span class="p">)</span>
+ <span class="n">TriggerManager</span><span class="o">.</span><span
class="n">register_trigger</span><span class="p">(</span><span
class="n">kind</span><span class="p">,</span> <span
class="n">trigger_name</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">f</span>
+ <span class="k">return</span> <span class="n">decorator</span>
+
+</div>
+<span class="n">application</span> <span class="o">=</span> <span
class="n">webapp</span><span class="o">.</span><span
class="n">WSGIApplication</span><span class="p">([</span>
+ <span class="p">(</span><span class="n">PROCESS_URL</span><span
class="p">,</span> <span class="n">ProcessUpdatesHandler</span><span
class="p">),</span>
+ <span class="p">(</span><span class="n">UPDATE_URL</span><span
class="p">,</span> <span class="n">UpdateEntityHandler</span><span
class="p">),</span>
+ <span class="p">],</span> <span class="n">debug</span><span
class="o">=</span><span class="bp">True</span><span class="p">)</span>
+</pre></div>
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper">
+<div id="searchbox" style="display: none">
+ <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>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </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"
+ >index</a></li>
+ <li class="right" >
+ <a href="../../../../py-modindex.html" title="Python Module
Index"
+ >modules</a> |</li>
+ <li><a href="../../../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
+ <li><a href="../../../index.html" >Module code</a> »</li>
+ </ul>
+ </div>
+ <div class="footer">
+ © Copyright 2010, Mike Aizatsky.
+ Created using <a href="
http://sphinx.pocoo.org/">Sphinx</a> 1.0.4.
+ </div>
+ </body>
+</html>
=======================================
--- /dev/null
+++ /doc/python/html/_modules/afterburner/experimental/memoize.html Mon Nov
15 04:53:40 2010
@@ -0,0 +1,143 @@
+
+
+<!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>afterburner.experimental.memoize — Appengine-Afterburner
v0.0.1 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: '0.0.1',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript"
src="../../../_static/jquery.js"></script>
+ <script type="text/javascript"
src="../../../_static/underscore.js"></script>
+ <script type="text/javascript"
src="../../../_static/doctools.js"></script>
+ <link rel="top" title="Appengine-Afterburner v0.0.1 documentation"
href="../../../index.html" />
+ <link rel="up" title="Module code" 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="../../../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
+ <li><a href="../../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
+ <li><a href="../../index.html" accesskey="U">Module code</a>
»</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <h1>Source code for afterburner.experimental.memoize</h1><div
class="highlight"><pre>
+<span class="c">#!/usr/bin/env python</span>
+<span class="c">#</span>
+<span class="c"># Copyright 2010 Google Inc.</span>
+<span class="c">#</span>
+<span class="c"># Licensed under the Apache License, Version 2.0 (the
"License");</span>
+<span class="c"># you may not use this file except in compliance with the
License.</span>
+<span class="c"># You may obtain a copy of the License at</span>
+<span class="c">#</span>
+<span class="c">#
http://www.apache.org/licenses/LICENSE-2.0</span>
+<span class="c">#</span>
+<span class="c"># Unless required by applicable law or agreed to in
writing, software</span>
+<span class="c"># distributed under the License is distributed on an
"AS IS" BASIS,</span>
+<span class="c"># WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied.</span>
+<span class="c"># See the License for the specific language governing
permissions and</span>
+<span class="c"># limitations under the License.</span>
+
+<span class="sd">"""Containes memoization decorator to
cache function result in memcache.</span>
+
+<span class="sd">Usage::</span>
+
+<span class="sd"> @memoize('account:name=%s')</span>
+<span class="sd"> def get_account_by_name(name):</span>
+<span class="sd"> return Account.all().filter('name =',
name).get()</span>
+<span class="sd">"""</span>
+
+<span class="kn">from</span> <span class="nn">google.appengine.api</span>
<span class="kn">import</span> <span class="n">memcache</span>
+
+
+<span class="n">DEBUG</span> <span class="o">=</span> <span
class="bp">False</span>
+
+
+<span class="k">try</span><span class="p">:</span>
+ <span class="c"># try to obtain debug settings from Django</span>
+ <span class="c"># TODO: introduce debug module which will do this</span>
+ <span class="kn">import</span> <span class="nn">settings</span>
+ <span class="n">DEBUG</span> <span class="o">=</span> <span
class="n">settings</span><span class="o">.</span><span
class="n">DEBUG</span>
+<span class="k">except</span><span class="p">:</span>
+ <span class="k">pass</span>
+
+<div class="viewcode-block" id="memoize"><a class="viewcode-back"
href="../../../afterburner/experimental/memoize.html#afterburner.experimental.memoize.memoize">[docs]</a><span
class="k">def</span> <span class="nf">memoize</span><span
class="p">(</span><span class="n">keyformat</span><span class="p">,</span>
<span class="n">time</span><span class="o">=</span><span
class="mi">60</span><span class="p">):</span>
+ <span class="sd">"""Decorator to memoize functions using
memcache."""</span>
+ <span class="k">def</span> <span class="nf">decorator</span><span
class="p">(</span><span class="n">f</span><span class="p">):</span>
+ <span class="k">def</span> <span class="nf">wrapper</span><span
class="p">(</span><span class="o">*</span><span class="n">args</span><span
class="p">,</span> <span class="o">**</span><span
class="n">kwargs</span><span class="p">):</span>
+ <span class="n">key</span> <span class="o">=</span> <span
class="n">keyformat</span> <span class="o">%</span> <span
class="n">args</span><span class="p">[</span><span class="mi">0</span><span
class="p">:</span><span class="n">keyformat</span><span
class="o">.</span><span class="n">count</span><span class="p">(</span><span
class="s">'%'</span><span class="p">)]</span>
+ <span class="n">data</span> <span class="o">=</span> <span
class="n">memcache</span><span class="o">.</span><span
class="n">get</span><span class="p">(</span><span class="n">key</span><span
class="p">)</span>
+ <span class="k">if</span> <span class="n">data</span> <span
class="ow">is</span> <span class="ow">not</span> <span
class="bp">None</span><span class="p">:</span>
+ <span class="k">return</span> <span class="n">data</span>
+ <span class="n">data</span> <span class="o">=</span> <span
class="n">f</span><span class="p">(</span><span class="o">*</span><span
class="n">args</span><span class="p">,</span> <span
class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
+ <span class="n">memcache</span><span class="o">.</span><span
class="n">set</span><span class="p">(</span><span class="n">key</span><span
class="p">,</span> <span class="n">data</span><span class="p">,</span>
<span class="n">time</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">data</span>
+ <span class="k">return</span> <span class="n">wrapper</span>
+ <span class="k">return</span> <span class="n">decorator</span> <span
class="k">if</span> <span class="ow">not</span> <span
class="n">DEBUG</span> <span class="k">else</span> <span
class="n">f</span></div>
+</pre></div>
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper">
+<div id="searchbox" style="display: none">
+ <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>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </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"
+ >index</a></li>
+ <li class="right" >
+ <a href="../../../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
+ <li><a href="../../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
+ <li><a href="../../index.html" >Module code</a> »</li>
+ </ul>
+ </div>
+ <div class="footer">
+ © Copyright 2010, Mike Aizatsky.
+ Created using <a href="
http://sphinx.pocoo.org/">Sphinx</a> 1.0.4.
+ </div>
+ </body>
+</html>
=======================================
--- /dev/null
+++ /doc/python/html/_modules/afterburner/experimental/quota.html Mon Nov
15 04:53:40 2010
@@ -0,0 +1,276 @@
+
+
+<!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>afterburner.experimental.quota — Appengine-Afterburner
v0.0.1 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: '0.0.1',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript"
src="../../../_static/jquery.js"></script>
+ <script type="text/javascript"
src="../../../_static/underscore.js"></script>
+ <script type="text/javascript"
src="../../../_static/doctools.js"></script>
+ <link rel="top" title="Appengine-Afterburner v0.0.1 documentation"
href="../../../index.html" />
+ <link rel="up" title="Module code" 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="../../../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
+ <li><a href="../../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
+ <li><a href="../../index.html" accesskey="U">Module code</a>
»</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <h1>Source code for afterburner.experimental.quota</h1><div
class="highlight"><pre>
+<span class="c">#!/usr/bin/env python</span>
+<span class="c">#</span>
+<span class="c"># Copyright 2010 Google Inc.</span>
+<span class="c">#</span>
+<span class="c"># Licensed under the Apache License, Version 2.0 (the
"License");</span>
+<span class="c"># you may not use this file except in compliance with the
License.</span>
+<span class="c"># You may obtain a copy of the License at</span>
+<span class="c">#</span>
+<span class="c">#
http://www.apache.org/licenses/LICENSE-2.0</span>
+<span class="c">#</span>
+<span class="c"># Unless required by applicable law or agreed to in
writing, software</span>
+<span class="c"># distributed under the License is distributed on an
"AS IS" BASIS,</span>
+<span class="c"># WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied.</span>
+<span class="c"># See the License for the specific language governing
permissions and</span>
+<span class="c"># limitations under the License.</span>
+
+<span class="sd">"""Contains simple quota system for
rate-limiting arbitrary operations."""</span>
+
+
+<span class="kn">from</span> <span class="nn">google.appengine.api</span>
<span class="kn">import</span> <span class="n">memcache</span>
+
+
+<span class="c"># Memcache namespace to use.</span>
+<span class="n">_QUOTA_NAMESPACE</span> <span class="o">=</span> <span
class="s">"ab__quota__"</span>
+
+<span class="c"># Offset all quota values by this amount since memcache
incr/decr</span>
+<span class="c"># operate only with unsigned values.</span>
+<span class="n">_OFFSET</span> <span class="o">=</span> <span
class="mi">2</span><span class="o">**</span><span class="mi">32</span>
+
+<div class="viewcode-block" id="QuotaManager"><a class="viewcode-back"
href="../../../afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaManager">[docs]</a><span
class="k">class</span> <span class="nc">QuotaManager</span><span
class="p">(</span><span class="nb">object</span><span class="p">):</span>
+ <span class="sd">"""Simple quota system manager, backed
by memcache storage.</span>
+
+<span class="sd"> Since memcache storage is not reliable, this quota
system is not reliable and</span>
+<span class="sd"> best effort only.</span>
+
+<span class="sd"> Quota is managed by buckets. Each bucket contains a
32-bit int value of</span>
+<span class="sd"> available quota. Buckets should be refilled manually
with 'put' method.</span>
+
+<span class="sd"> It is safe to use a single bucket from multiple clients
simultaneously.</span>
+<span class="sd"> """</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">memcache_client</span><span class="o">=</span><span
class="n">memcache</span><span class="o">.</span><span
class="n">Client</span><span class="p">()):</span>
+ <span class="sd">"""Initialize new instance.</span>
+
+<span class="sd"> Args:</span>
+<span class="sd"> memcache_client: an instance of memcache client to
use.</span>
+<span class="sd"> """</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">memcache_client</span> <span class="o">=</span> <span
class="n">memcache_client</span>
+
+<div class="viewcode-block" id="QuotaManager.put"><a class="viewcode-back"
href="../../../afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaManager.put">[docs]</a>
<span class="k">def</span> <span class="nf">put</span><span
class="p">(</span><span class="bp">self</span><span class="p">,</span>
<span class="n">bucket</span><span class="p">,</span> <span
class="n">amount</span><span class="p">):</span>
+ <span class="sd">"""Put amount into quota bucket.</span>
+
+<span class="sd"> Args:</span>
+<span class="sd"> bucket: quota bucket as string.</span>
+<span class="sd"> amount: amount to bit put into quota as int.</span>
+<span class="sd"> """</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">memcache_client</span><span class="o">.</span><span
class="n">incr</span><span class="p">(</span><span
class="n">bucket</span><span class="p">,</span> <span
class="n">delta</span><span class="o">=</span><span
class="n">amount</span><span class="p">,</span>
+ <span class="n">initial_value</span><span
class="o">=</span><span class="n">_OFFSET</span><span class="p">,</span>
<span class="n">namespace</span><span class="o">=</span><span
class="n">_QUOTA_NAMESPACE</span><span class="p">)</span>
+</div>
+<div class="viewcode-block" id="QuotaManager.consume"><a
class="viewcode-back"
href="../../../afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaManager.consume">[docs]</a>
<span class="k">def</span> <span class="nf">consume</span><span
class="p">(</span><span class="bp">self</span><span class="p">,</span>
<span class="n">bucket</span><span class="p">,</span> <span
class="n">amount</span><span class="p">,</span> <span
class="n">consume_some</span><span class="o">=</span><span
class="bp">False</span><span class="p">):</span>
+ <span class="sd">"""Consume amount from quota
bucket.</span>
+
+<span class="sd"> Args:</span>
+<span class="sd"> bucket: quota bucket as string.</span>
+<span class="sd"> amount: amount to consume.</span>
+<span class="sd"> consume_some: specifies behavior in case of not
enough quota. If False,</span>
+<span class="sd"> the method will leave quota intact and return 0.
If True, will try to</span>
+<span class="sd"> consume as much as possible.</span>
+
+<span class="sd"> Returns:</span>
+<span class="sd"> Amount of quota consumed.</span>
+<span class="sd"> """</span>
+ <span class="n">new_quota</span> <span class="o">=</span> <span
class="bp">self</span><span class="o">.</span><span
class="n">memcache_client</span><span class="o">.</span><span
class="n">decr</span><span class="p">(</span>
+ <span class="n">bucket</span><span class="p">,</span> <span
class="n">delta</span><span class="o">=</span><span
class="n">amount</span><span class="p">,</span> <span
class="n">initial_value</span><span class="o">=</span><span
class="n">_OFFSET</span><span class="p">,</span> <span
class="n">namespace</span><span class="o">=</span><span
class="n">_QUOTA_NAMESPACE</span><span class="p">)</span>
+
+ <span class="k">if</span> <span class="n">new_quota</span> <span
class="o">>=</span> <span class="n">_OFFSET</span><span
class="p">:</span>
+ <span class="k">return</span> <span class="n">amount</span>
+
+ <span class="k">if</span> <span class="n">consume_some</span> <span
class="ow">and</span> <span class="n">_OFFSET</span> <span
class="o">-</span> <span class="n">new_quota</span> <span
class="o"><</span> <span class="n">amount</span><span class="p">:</span>
+ <span class="c"># we still can consume some</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">put</span><span class="p">(</span><span
class="n">bucket</span><span class="p">,</span> <span
class="n">_OFFSET</span> <span class="o">-</span> <span
class="n">new_quota</span><span class="p">)</span>
+ <span class="k">return</span> <span class="n">amount</span> <span
class="o">-</span> <span class="p">(</span><span class="n">_OFFSET</span>
<span class="o">-</span> <span class="n">new_quota</span><span
class="p">)</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">put</span><span class="p">(</span><span
class="n">bucket</span><span class="p">,</span> <span
class="n">amount</span><span class="p">)</span>
+ <span class="k">return</span> <span class="mi">0</span>
+</div>
+<div class="viewcode-block" id="QuotaManager.get"><a class="viewcode-back"
href="../../../afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaManager.get">[docs]</a>
<span class="k">def</span> <span class="nf">get</span><span
class="p">(</span><span class="bp">self</span><span class="p">,</span>
<span class="n">bucket</span><span class="p">):</span>
+ <span class="sd">"""Get current bucket amount.</span>
+
+<span class="sd"> Args:</span>
+<span class="sd"> bucket: quota bucket as string.</span>
+
+<span class="sd"> Returns:</span>
+<span class="sd"> current bucket amount as int.</span>
+<span class="sd"> """</span>
+ <span class="n">amount</span> <span class="o">=</span> <span
class="bp">self</span><span class="o">.</span><span
class="n">memcache_client</span><span class="o">.</span><span
class="n">get</span><span class="p">(</span><span
class="n">bucket</span><span class="p">,</span> <span
class="n">namespace</span><span class="o">=</span><span
class="n">_QUOTA_NAMESPACE</span><span class="p">)</span>
+ <span class="k">if</span> <span class="n">amount</span><span
class="p">:</span>
+ <span class="k">return</span> <span class="nb">int</span><span
class="p">(</span><span class="n">amount</span><span class="p">)</span>
<span class="o">-</span> <span class="n">_OFFSET</span>
+ <span class="k">else</span><span class="p">:</span>
+ <span class="k">return</span> <span class="mi">0</span>
+</div>
+<div class="viewcode-block" id="QuotaManager.set"><a class="viewcode-back"
href="../../../afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaManager.set">[docs]</a>
<span class="k">def</span> <span class="nf">set</span><span
class="p">(</span><span class="bp">self</span><span class="p">,</span>
<span class="n">bucket</span><span class="p">,</span> <span
class="n">amount</span><span class="p">):</span>
+ <span class="sd">"""Set bucket amount.</span>
+
+<span class="sd"> Args:</span>
+<span class="sd"> bucket: quota bucket as string.</span>
+<span class="sd"> amount: new bucket amount as int.</span>
+<span class="sd"> """</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">memcache_client</span><span class="o">.</span><span
class="n">set</span><span class="p">(</span><span
class="n">bucket</span><span class="p">,</span> <span
class="n">amount</span> <span class="o">+</span> <span
class="n">_OFFSET</span><span class="p">,</span>
+ <span class="n">namespace</span><span
class="o">=</span><span class="n">_QUOTA_NAMESPACE</span><span
class="p">)</span>
+
+</div></div>
+<div class="viewcode-block" id="QuotaConsumer"><a class="viewcode-back"
href="../../../afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaConsumer">[docs]</a><span
class="k">class</span> <span class="nc">QuotaConsumer</span><span
class="p">(</span><span class="nb">object</span><span class="p">):</span>
+ <span class="sd">"""Quota consumer wrapper for efficient
quota consuming/reclaiming.</span>
+
+<span class="sd"> Quota is consumed in batches and put back in dispose()
method.</span>
+
+<span class="sd"> WARNING: Always call the dispose() method if you need
to keep quota</span>
+<span class="sd"> consistent.</span>
+<span class="sd"> """</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">quota_manager</span><span class="p">,</span> <span
class="n">bucket</span><span class="p">,</span> <span
class="n">batch_size</span><span class="p">):</span>
+ <span class="sd">"""Initialize new instance.</span>
+
+<span class="sd"> Args:</span>
+<span class="sd"> quota_manager: quota manager to use for quota
operations as QuotaManager.</span>
+<span class="sd"> bucket: quota bucket name as string.</span>
+<span class="sd"> batch_size: batch size for quota consuming as
int.</span>
+<span class="sd"> """</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">quota_manager</span> <span class="o">=</span> <span
class="n">quota_manager</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">batch_size</span> <span class="o">=</span> <span
class="n">batch_size</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">bucket</span> <span class="o">=</span> <span
class="n">bucket</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">quota</span> <span class="o">=</span> <span class="mi">0</span>
+
+<div class="viewcode-block" id="QuotaConsumer.consume"><a
class="viewcode-back"
href="../../../afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaConsumer.consume">[docs]</a>
<span class="k">def</span> <span class="nf">consume</span><span
class="p">(</span><span class="bp">self</span><span class="p">,</span>
<span class="n">amount</span><span class="o">=</span><span
class="mi">1</span><span class="p">):</span>
+ <span class="sd">"""Consume quota.</span>
+
+<span class="sd"> Args:</span>
+<span class="sd"> amount: amount of quota to be consumed as
int.</span>
+
+<span class="sd"> Returns:</span>
+<span class="sd"> True if quota was successfully consumed, False if
there's not enough</span>
+<span class="sd"> quota.</span>
+<span class="sd"> """</span>
+ <span class="k">while</span> <span class="bp">self</span><span
class="o">.</span><span class="n">quota</span> <span class="o"><</span>
<span class="n">amount</span><span class="p">:</span>
+ <span class="n">delta</span> <span class="o">=</span> <span
class="bp">self</span><span class="o">.</span><span
class="n">quota_manager</span><span class="o">.</span><span
class="n">consume</span><span class="p">(</span><span
class="bp">self</span><span class="o">.</span><span
class="n">bucket</span><span class="p">,</span> <span
class="bp">self</span><span class="o">.</span><span
class="n">batch_size</span><span class="p">,</span>
+ <span
class="n">consume_some</span><span class="o">=</span><span
class="bp">True</span><span class="p">)</span>
+ <span class="k">if</span> <span class="ow">not</span> <span
class="n">delta</span><span class="p">:</span>
+ <span class="k">return</span> <span class="bp">False</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">quota</span> <span class="o">+=</span> <span
class="n">delta</span>
+
+ <span class="bp">self</span><span class="o">.</span><span
class="n">quota</span> <span class="o">-=</span> <span
class="n">amount</span>
+ <span class="k">return</span> <span class="bp">True</span>
+</div>
+<div class="viewcode-block" id="QuotaConsumer.put"><a
class="viewcode-back"
href="../../../afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaConsumer.put">[docs]</a>
<span class="k">def</span> <span class="nf">put</span><span
class="p">(</span><span class="bp">self</span><span class="p">,</span>
<span class="n">amount</span><span class="o">=</span><span
class="mi">1</span><span class="p">):</span>
+ <span class="sd">"""Put quota back.</span>
+
+<span class="sd"> Args:</span>
+<span class="sd"> amount: amount of quota as int.</span>
+<span class="sd"> """</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">quota</span> <span class="o">+=</span> <span
class="n">amount</span>
+</div>
+<div class="viewcode-block" id="QuotaConsumer.check"><a
class="viewcode-back"
href="../../../afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaConsumer.check">[docs]</a>
<span class="k">def</span> <span class="nf">check</span><span
class="p">(</span><span class="bp">self</span><span class="p">,</span>
<span class="n">amount</span><span class="o">=</span><span
class="mi">1</span><span class="p">):</span>
+ <span class="sd">"""Check that we have enough quota
right now.</span>
+
+<span class="sd"> This doesn't lock or consume the quota. Following
consume might in fact</span>
+<span class="sd"> fail/succeeded.</span>
+
+<span class="sd"> Args:</span>
+<span class="sd"> amount: amount of quota to check.</span>
+
+<span class="sd"> Returns:</span>
+<span class="sd"> True if we have enough quota to consume specified
amount right now. False</span>
+<span class="sd"> otherwise.</span>
+<span class="sd"> """</span>
+ <span class="k">if</span> <span class="bp">self</span><span
class="o">.</span><span class="n">quota</span> <span class="o">>=</span>
<span class="n">amount</span><span class="p">:</span>
+ <span class="k">return</span> <span class="bp">True</span>
+ <span class="k">return</span> <span class="bp">self</span><span
class="o">.</span><span class="n">quota</span> <span class="o">+</span>
<span class="bp">self</span><span class="o">.</span><span
class="n">quota_manager</span><span class="o">.</span><span
class="n">get</span><span class="p">(</span><span
class="bp">self</span><span class="o">.</span><span
class="n">bucket</span><span class="p">)</span> <span
class="o">>=</span> <span class="n">amount</span>
+</div>
+<div class="viewcode-block" id="QuotaConsumer.dispose"><a
class="viewcode-back"
href="../../../afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaConsumer.dispose">[docs]</a>
<span class="k">def</span> <span class="nf">dispose</span><span
class="p">(</span><span class="bp">self</span><span class="p">):</span>
+ <span class="sd">"""Dispose QuotaConsumer and put all
actually unconsumed quota back.</span>
+
+<span class="sd"> This method has to be called for quota
consistency!</span>
+<span class="sd"> """</span>
+ <span class="bp">self</span><span class="o">.</span><span
class="n">quota_manager</span><span class="o">.</span><span
class="n">put</span><span class="p">(</span><span
class="bp">self</span><span class="o">.</span><span
class="n">bucket</span><span class="p">,</span> <span
class="bp">self</span><span class="o">.</span><span
class="n">quota</span><span class="p">)</span></div></div>
+</pre></div>
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper">
+<div id="searchbox" style="display: none">
+ <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>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </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"
+ >index</a></li>
+ <li class="right" >
+ <a href="../../../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
+ <li><a href="../../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
+ <li><a href="../../index.html" >Module code</a> »</li>
+ </ul>
+ </div>
+ <div class="footer">
+ © Copyright 2010, Mike Aizatsky.
+ Created using <a href="
http://sphinx.pocoo.org/">Sphinx</a> 1.0.4.
+ </div>
+ </body>
+</html>
=======================================
--- /dev/null
+++ /doc/python/html/_modules/index.html Mon Nov 15 04:53:40 2010
@@ -0,0 +1,91 @@
+
+
+<!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>Overview: module code — Appengine-Afterburner v0.0.1
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: '0.0.1',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="../_static/jquery.js"></script>
+ <script type="text/javascript" src="../_static/underscore.js"></script>
+ <script type="text/javascript" src="../_static/doctools.js"></script>
+ <link rel="top" title="Appengine-Afterburner v0.0.1 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="../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
+ <li><a href="../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+ <h1>All modules for which code is available</h1>
+<ul><li><a
href="afterburner/experimental/db/triggers.html">afterburner.experimental.db.triggers</a></li>
+<li><a
href="afterburner/experimental/memoize.html">afterburner.experimental.memoize</a></li>
+<li><a
href="afterburner/experimental/quota.html">afterburner.experimental.quota</a></li>
+</ul>
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper">
+<div id="searchbox" style="display: none">
+ <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>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </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"
+ >index</a></li>
+ <li class="right" >
+ <a href="../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
+ <li><a href="../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
+ </ul>
+ </div>
+ <div class="footer">
+ © Copyright 2010, Mike Aizatsky.
+ Created using <a href="
http://sphinx.pocoo.org/">Sphinx</a> 1.0.4.
+ </div>
+ </body>
+</html>
=======================================
--- /dev/null
+++ /doc/python/html/py-modindex.html Mon Nov 15 04:53:40 2010
@@ -0,0 +1,123 @@
+
+
+<!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>Python Module Index — Appengine-Afterburner v0.0.1
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: '0.0.1',
+ COLLAPSE_INDEX: false,
+ FILE_SUFFIX: '.html',
+ HAS_SOURCE: true
+ };
+ </script>
+ <script type="text/javascript" src="_static/jquery.js"></script>
+ <script type="text/javascript" src="_static/underscore.js"></script>
+ <script type="text/javascript" src="_static/doctools.js"></script>
+ <link rel="top" title="Appengine-Afterburner v0.0.1 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="Python Module Index"
+ >modules</a> |</li>
+ <li><a href="index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
+ </ul>
+ </div>
+
+ <div class="document">
+ <div class="documentwrapper">
+ <div class="bodywrapper">
+ <div class="body">
+
+
+ <h1>Python Module Index</h1>
+
+ <div class="modindex-jumpbox">
+ <a href="#cap-a"><strong>a</strong></a>
+ </div>
+
+ <table class="indextable modindextable" cellspacing="0" cellpadding="2">
+ <tr class="pcap"><td></td><td> </td><td></td></tr>
+ <tr class="cap"><td></td><td><a name="cap-a">
+ <strong>a</strong></a></td><td></td></tr>
+ <tr>
+ <td><img src="_static/minus.png" id="toggle-1"
+ class="toggler" style="display: none" alt="-" /></td>
+ <td>
+ <tt class="xref">afterburner</tt></td><td>
+ <em></em></td></tr>
+ <tr class="cg-1">
+ <td></td>
+ <td>
+ <a
href="afterburner/experimental/db/triggers.html#module-afterburner.experimental.db.triggers"><tt
class="xref">afterburner.experimental.db.triggers</tt></a></td><td>
+ <em></em></td></tr>
+ <tr class="cg-1">
+ <td></td>
+ <td>
+ <a
href="afterburner/experimental/memoize.html#module-afterburner.experimental.memoize"><tt
class="xref">afterburner.experimental.memoize</tt></a></td><td>
+ <em></em></td></tr>
+ <tr class="cg-1">
+ <td></td>
+ <td>
+ <a
href="afterburner/experimental/quota.html#module-afterburner.experimental.quota"><tt
class="xref">afterburner.experimental.quota</tt></a></td><td>
+ <em></em></td></tr>
+ </table>
+
+
+ </div>
+ </div>
+ </div>
+ <div class="sphinxsidebar">
+ <div class="sphinxsidebarwrapper">
+<div id="searchbox" style="display: none">
+ <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>
+ <p class="searchtip" style="font-size: 90%">
+ Enter search terms or a module, class or function name.
+ </p>
+</div>
+<script type="text/javascript">$('#searchbox').show(0);</script>
+ </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"
+ >index</a></li>
+ <li class="right" >
+ <a href="#" title="Python Module Index"
+ >modules</a> |</li>
+ <li><a href="index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
+ </ul>
+ </div>
+ <div class="footer">
+ © Copyright 2010, Mike Aizatsky.
+ Created using <a href="
http://sphinx.pocoo.org/">Sphinx</a> 1.0.4.
+ </div>
+ </body>
+</html>
=======================================
--- /dev/null
+++ /trunk/python/doc/Makefile Mon Nov 15 04:53:40 2010
@@ -0,0 +1,130 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS = -c .
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = ../../../doc/python
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER))
$(SPHINXOPTS) ../src
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp
devhelp epub latex latexpdf text man changes linkcheck doctest
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or
PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " changes to make an overview of all changed/added/deprecated
items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if
enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator
$(BUILDDIR)/qthelp/Appengine-Afterburner.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile
$(BUILDDIR)/qthelp/Appengine-Afterburner.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Appengine-Afterburner"
+ @echo "# ln -s $(BUILDDIR)/devhelp
$$HOME/.local/share/devhelp/Appengine-Afterburner"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ make -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
=======================================
--- /dev/null
+++ /trunk/python/doc/conf.py Mon Nov 15 04:53:40 2010
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+#
+# Appengine-Afterburner documentation build configuration file, created by
+# sphinx-quickstart on Thu Oct 21 14:10:12 2010.
+#
+# This file is execfile()d with the current directory set to its
containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another
directory,
+# add these directories to sys.path here. If the directory is relative to
the
+# documentation root, use os.path.abspath to make it absolute, like shown
here.
+sys.path.insert(0, os.path.abspath('./../src'))
+
+# -- General configuration
-----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions =
['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Appengine-Afterburner'
+copyright = u'2010, Mike Aizatsky'
+
+# The version info for the project you're documenting, acts as replacement
for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.0.1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to
some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all
documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output
---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a
theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this
directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as
html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the
top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of
the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or
32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets)
here,
+# relative to this directory. They are copied after the builtin static
files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page
bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is
True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is
True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages
will
+# contain a <link> tag referring to it. The value of this option must be
the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Appengine-Afterburnerdoc'
+
+
+# -- Options for LaTeX output
--------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass
[howto/manual]).
+latex_documents = [
+ ('index', 'Appengine-Afterburner.tex', u'Appengine-Afterburner
Documentation',
+ u'Mike Aizatsky', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the
top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are
parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output
--------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'appengine-afterburner', u'Appengine-Afterburner
Documentation',
+ [u'Mike Aizatsky'], 1)
+]
=======================================
--- /trunk/python/Makefile Fri Nov 5 21:29:00 2010
+++ /dev/null
@@ -1,130 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = ../../doc/python
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER))
$(SPHINXOPTS) src
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp
devhelp epub latex latexpdf text man changes linkcheck doctest
-
-help:
- @echo "Please use \`make <target>' where <target> is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or
PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " changes to make an overview of all changed/added/deprecated
items"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if
enabled)"
-
-clean:
- -rm -rf $(BUILDDIR)/*
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator
$(BUILDDIR)/qthelp/Appengine-Afterburner.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile
$(BUILDDIR)/qthelp/Appengine-Afterburner.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/Appengine-Afterburner"
- @echo "# ln -s $(BUILDDIR)/devhelp
$$HOME/.local/share/devhelp/Appengine-Afterburner"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- make -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
=======================================
--- /trunk/python/src/conf.py Fri Nov 5 21:29:00 2010
+++ /dev/null
@@ -1,216 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Appengine-Afterburner documentation build configuration file, created by
-# sphinx-quickstart on Thu Oct 21 14:10:12 2010.
-#
-# This file is execfile()d with the current directory set to its
containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys, os
-
-# If extensions (or modules to document with autodoc) are in another
directory,
-# add these directories to sys.path here. If the directory is relative to
the
-# documentation root, use os.path.abspath to make it absolute, like shown
here.
-sys.path.insert(0, os.path.abspath('./../src'))
-
-# -- General configuration
-----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be
extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions =
['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'Appengine-Afterburner'
-copyright = u'2010, Mike Aizatsky'
-
-# The version info for the project you're documenting, acts as replacement
for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '0.0.1'
-# The full version, including alpha/beta/rc tags.
-release = '0.0.1'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to
some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = []
-
-# The reST default role (used for this markup: `text`) to use for all
documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output
---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-html_theme = 'default'
-
-# Theme options are theme-specific and customize the look and feel of a
theme
-# further. For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this
directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar. Default is the same as
html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the
top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of
the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or
32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets)
here,
-# relative to this directory. They are copied after the builtin static
files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page
bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is
True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is
True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages
will
-# contain a <link> tag referring to it. The value of this option must be
the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'Appengine-Afterburnerdoc'
-
-
-# -- Options for LaTeX output
--------------------------------------------------
-
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass
[howto/manual]).
-latex_documents = [
- ('index', 'Appengine-Afterburner.tex', u'Appengine-Afterburner
Documentation',
- u'Mike Aizatsky', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the
top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are
parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output
--------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'appengine-afterburner', u'Appengine-Afterburner
Documentation',
- [u'Mike Aizatsky'], 1)
-]
=======================================
--- /doc/python/doctrees/afterburner/experimental/db/triggers.doctree Fri
Nov 5 21:31:46 2010
+++ /doc/python/doctrees/afterburner/experimental/db/triggers.doctree Mon
Nov 15 04:53:40 2010
Binary file, no diff available.
=======================================
--- /doc/python/doctrees/afterburner/experimental/memoize.doctree Fri Nov
5 21:31:46 2010
+++ /doc/python/doctrees/afterburner/experimental/memoize.doctree Mon Nov
15 04:53:40 2010
Binary file, no diff available.
=======================================
--- /doc/python/doctrees/afterburner/experimental/quota.doctree Fri Nov 5
21:31:46 2010
+++ /doc/python/doctrees/afterburner/experimental/quota.doctree Mon Nov 15
04:53:40 2010
Binary file, no diff available.
=======================================
--- /doc/python/doctrees/environment.pickle Fri Nov 5 21:31:46 2010
+++ /doc/python/doctrees/environment.pickle Mon Nov 15 04:53:40 2010
Binary file, no diff available.
=======================================
--- /doc/python/doctrees/index.doctree Fri Nov 5 21:31:46 2010
+++ /doc/python/doctrees/index.doctree Mon Nov 15 04:53:40 2010
Binary file, no diff available.
=======================================
--- /doc/python/html/afterburner/experimental/db/triggers.html Fri Nov 5
21:31:46 2010
+++ /doc/python/html/afterburner/experimental/db/triggers.html Mon Nov 15
04:53:40 2010
@@ -31,6 +31,9 @@
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="../../../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="../../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
</ul>
</div>
@@ -40,9 +43,138 @@
<div class="bodywrapper">
<div class="body">
- <div class="section" id="triggers-module">
-<h1>Triggers Module<a class="headerlink" href="#triggers-module"
title="Permalink to this headline">¶</a></h1>
+ <div class="section" id="module-afterburner.experimental.db.triggers">
+<span id="triggers-module"></span><h1>Triggers Module<a class="headerlink"
href="#module-afterburner.experimental.db.triggers" title="Permalink to
this headline">¶</a></h1>
+<p>This module contains datastore trigger system implementation.</p>
+<div class="section" id="overview">
+<h2>Overview<a class="headerlink" href="#overview" title="Permalink to
this headline">¶</a></h2>
+<p>The system is designed to execute user’s code every time an
entity of specific
+kind is changed by application. Right now the only execution model is
delayed
+execution (via taskqueue), which makes it useful only in
eventually-consistent
+scenarios.</p>
+<p>It is important to remember that:</p>
+<ul class="simple">
+<li>Triggers won’t be fired if you change your data not from the app
itself (e.g.
+admin console).</li>
+<li>There might be a delay (sometimes even minute) between actual data
change and
+trigger execution.</li>
+<li>Trigger executions for a single entity are batched. I.e. if you change
entity
+quickly multiple times you’ll most likely get your trigger function
called
+only once.</li>
+</ul>
+<p>Current Limitations:</p>
+<ul class="simple">
+<li>Right now only cron-based updates processing is implemented. This
means that
+the delay between event and trigger processing might be about 1 minute in
+worst case.</li>
+<li>Due to limitations of current datastore api batch deletes are executed
+by triggers module as separate transactions. This may decrease performance
+of deleting code quite a lot.</li>
+<li>It is estimated that with this design you won’t be able to
process more than
+60 changes per second. See the implementation details and planned
improvements
+sections for more details.</li>
+</ul>
</div>
+<div class="section" id="usage">
+<h2>Usage<a class="headerlink" href="#usage" title="Permalink to this
headline">¶</a></h2>
+<p>To use triggers:</p>
+<ul>
+<li><p class="first">Add the following code to your app.yaml:</p>
+<div class="highlight-python"><pre>includes:
+- afterburner/experimental/db/triggers.yaml</pre>
+</div>
+</li>
+<li><p class="first">Add following lines to your cron.yaml:</p>
+<div class="highlight-python"><pre>cron:
+- description: triggers processing
+ url: /_ab/triggers/process
+ schedule: every 1 minutes</pre>
+</div>
+</li>
+<li><p class="first">Create appengine_config.py in application root
directory and import your
+modules with all trigger definitions from it.</p>
+</li>
+<li><p class="first">Define triggers as following:</p>
+<div class="highlight-python"><div class="highlight"><pre><span
class="kn">from</span> <span class="nn">afterburner.experimental.db</span>
<span class="kn">import</span> <span class="n">triggers</span>
+<span class="nd">@triggers.trigger</span><span class="p">(</span><span
class="n">kind</span><span class="o">=</span><span
class="n">Order</span><span class="o">.</span><span
class="n">kind</span><span class="p">())</span>
+<span class="k">def</span> <span class="nf">order_trigger</span><span
class="p">(</span><span class="n">key</span><span class="p">):</span>
+ <span class="n">logging</span><span class="o">.</span><span
class="n">debug</span><span class="p">(</span><span class="s">"Order
was changed: </span><span class="si">%s</span><span
class="s">"</span><span class="p">,</span> <span
class="n">key</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Trigger function only argument is changed entity key. Because trigger
execution
+is decoupled in time from actual db operation, the information about
operation
+is not provided.</p>
+</li>
+<li><p class="first">Make sure that you’re using run_wsgi_app()
function to run your app and not
+older approach. For details check appengine documentation:
+<a class="reference external"
href="
http://code.google.com/appengine/docs/python/tools/webapp/running.html">
http://code.google.com/appengine/docs/python/tools/webapp/running.html</a></p>
+</li>
+</ul>
+</div>
+<div class="section" id="implementation-details">
+<h2>Implementation Details<a class="headerlink"
href="#implementation-details" title="Permalink to this headline">¶</a></h2>
+<p>To implement transactionally-safe triggers for every entity change we
detect,
+we create a child “Update” entity, which is later processed by
updater
+cron job. Updater cron job deletes Update entities and queues tasks for
executing
+triggers for particular entities.</p>
+<p>Adding child entities to puts doesn’t change runtime behavior of
the system
+at all, because datastore internally groups all entities in batch puts into
+entity groups and performs their write together in one transaction
automatically.</p>
+<p>Unfortunately it’s currently not possible to use the same
approach to batch
+deletes. We disassemble batch delete and delete each entity individually in
+its own transaction together with creating an update entity.
+Ideally datastore should provide an api where you can batch all kinds of
+operations together.</p>
+</div>
+<div class="section" id="alternative-approaches">
+<h2>Alternative Approaches<a class="headerlink"
href="#alternative-approaches" title="Permalink to this headline">¶</a></h2>
+<p>This section outlines alternative approaches to triggers system
designs. Some
+of these approaches can augment already existing ones to improve
performance
+and other characteristics.</p>
+<div class="section" id="transactional-tasks">
+<h3>Transactional Tasks<a class="headerlink" href="#transactional-tasks"
title="Permalink to this headline">¶</a></h3>
+<p>Instead of creating update tasks it’s possible to use
transactional tasks
+to queue triggers processing for each entity. This works perfectly for
+writes in transaction, but doesn’t work good in case of batch
operations
+across multiple entity groups: you have to disassemble both puts and gets
+the same way we disassemble deletes right now.</p>
+<p>The benefit of this approach is that by eliminating Update entity and
+update processor, trigger latency can be made quite low.</p>
+<p>Another caveat with this approach is task’s 10K payload size
limit and limit
+of 5 transactional tasks per transaction. If there are lots of entities in
a
+single transaction, it might be difficult to store keys for all of them
+in 5 tasks. However, it looks like this limitation is not a problem for
most
+apps.</p>
+</div>
+<div class="section" id="immediate-triggers-execution">
+<h3>Immediate Triggers Execution<a class="headerlink"
href="#immediate-triggers-execution" title="Permalink to this
headline">¶</a></h3>
+<p>It was a specific goal for this library to implement delayed triggers
execution.
+Immediate trigger execution can be significant utility for certain use
cases.</p>
+</div>
+</div>
+<div class="section" id="future-improvements">
+<h2>Future Improvements<a class="headerlink" href="#future-improvements"
title="Permalink to this headline">¶</a></h2>
+<p>Following improvements to the library are considered:</p>
+<ul class="simple">
+<li>Use transactional tasks for triggers enqueing in situtations where
it’s
+possible (non-batched puts/deletes, small transactions)</li>
+<li>Optional unbatched trigger execution</li>
+<li>Augment current cron execution with chained tasks. This will allow
bringing
+trigger latency down.</li>
+</ul>
+<p>One of these can be an excellent project for beginning contributors.</p>
+<dl class="function">
+<dt id="afterburner.experimental.db.triggers.trigger">
+<tt class="descclassname">afterburner.experimental.db.triggers.</tt><tt
class="descname">trigger</tt><big>(</big><em>kind</em><big>)</big><a
class="reference internal"
href="../../../_modules/afterburner/experimental/db/triggers.html#trigger"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.db.triggers.trigger" title="Permalink to
this definition">¶</a></dt>
+<dd><p>Trigger registering decorator.</p>
+<dl class="docutils">
+<dt>Args:</dt>
+<dd>kind: entity kind to attach defined trigger to.</dd>
+</dl>
+</dd></dl>
+
+</div>
+</div>
</div>
@@ -50,6 +182,22 @@
</div>
<div class="sphinxsidebar">
<div class="sphinxsidebarwrapper">
+ <h3><a href="../../../index.html">Table Of Contents</a></h3>
+ <ul>
+<li><a class="reference internal" href="#">Triggers Module</a><ul>
+<li><a class="reference internal" href="#overview">Overview</a></li>
+<li><a class="reference internal" href="#usage">Usage</a></li>
+<li><a class="reference internal"
href="#implementation-details">Implementation Details</a></li>
+<li><a class="reference internal"
href="#alternative-approaches">Alternative Approaches</a><ul>
+<li><a class="reference internal"
href="#transactional-tasks">Transactional Tasks</a></li>
+<li><a class="reference internal"
href="#immediate-triggers-execution">Immediate Triggers Execution</a></li>
+</ul>
+</li>
+<li><a class="reference internal" href="#future-improvements">Future
Improvements</a></li>
+</ul>
+</li>
+</ul>
+
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a
href="../../../_sources/afterburner/experimental/db/triggers.txt"
@@ -78,6 +226,9 @@
<li class="right" style="margin-right: 10px">
<a href="../../../genindex.html" title="General Index"
>index</a></li>
+ <li class="right" >
+ <a href="../../../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="../../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
</ul>
</div>
=======================================
--- /doc/python/html/afterburner/experimental/memoize.html Fri Nov 5
21:31:46 2010
+++ /doc/python/html/afterburner/experimental/memoize.html Mon Nov 15
04:53:40 2010
@@ -31,6 +31,9 @@
<li class="right" style="margin-right: 10px">
<a href="../../genindex.html" title="General Index"
accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="../../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
</ul>
</div>
@@ -40,9 +43,22 @@
<div class="bodywrapper">
<div class="body">
- <div class="section" id="memoize-module">
-<h1>Memoize Module<a class="headerlink" href="#memoize-module"
title="Permalink to this headline">¶</a></h1>
+ <div class="section" id="module-afterburner.experimental.memoize">
+<span id="memoize-module"></span><h1>Memoize Module<a class="headerlink"
href="#module-afterburner.experimental.memoize" title="Permalink to this
headline">¶</a></h1>
+<p>Containes memoization decorator to cache function result in
memcache.</p>
+<p>Usage:</p>
+<div class="highlight-python"><div class="highlight"><pre><span
class="nd">@memoize</span><span class="p">(</span><span
class="s">'account:name=</span><span class="si">%s</span><span
class="s">'</span><span class="p">)</span>
+<span class="k">def</span> <span
class="nf">get_account_by_name</span><span class="p">(</span><span
class="n">name</span><span class="p">):</span>
+ <span class="k">return</span> <span class="n">Account</span><span
class="o">.</span><span class="n">all</span><span class="p">()</span><span
class="o">.</span><span class="n">filter</span><span
class="p">(</span><span class="s">'name ='</span><span
class="p">,</span> <span class="n">name</span><span class="p">)</span><span
class="o">.</span><span class="n">get</span><span class="p">()</span>
+</pre></div>
</div>
+<dl class="function">
+<dt id="afterburner.experimental.memoize.memoize">
+<tt class="descclassname">afterburner.experimental.memoize.</tt><tt
class="descname">memoize</tt><big>(</big><em>keyformat</em>,
<em>time=60</em><big>)</big><a class="reference internal"
href="../../_modules/afterburner/experimental/memoize.html#memoize"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.memoize.memoize" title="Permalink to this
definition">¶</a></dt>
+<dd><p>Decorator to memoize functions using memcache.</p>
+</dd></dl>
+
+</div>
</div>
@@ -78,6 +94,9 @@
<li class="right" style="margin-right: 10px">
<a href="../../genindex.html" title="General Index"
>index</a></li>
+ <li class="right" >
+ <a href="../../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
</ul>
</div>
=======================================
--- /doc/python/html/afterburner/experimental/quota.html Fri Nov 5
21:31:46 2010
+++ /doc/python/html/afterburner/experimental/quota.html Mon Nov 15
04:53:40 2010
@@ -31,6 +31,9 @@
<li class="right" style="margin-right: 10px">
<a href="../../genindex.html" title="General Index"
accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="../../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
</ul>
</div>
@@ -40,8 +43,126 @@
<div class="bodywrapper">
<div class="body">
- <div class="section" id="quota-module">
-<h1>Quota Module<a class="headerlink" href="#quota-module"
title="Permalink to this headline">¶</a></h1>
+ <div class="section" id="module-afterburner.experimental.quota">
+<span id="quota-module"></span><h1>Quota Module<a class="headerlink"
href="#module-afterburner.experimental.quota" title="Permalink to this
headline">¶</a></h1>
+<p>Contains simple quota system for rate-limiting arbitrary operations.</p>
+<dl class="class">
+<dt id="afterburner.experimental.quota.QuotaConsumer">
+<em class="property">class </em><tt
class="descclassname">afterburner.experimental.quota.</tt><tt
class="descname">QuotaConsumer</tt><big>(</big><em>quota_manager</em>,
<em>bucket</em>, <em>batch_size</em><big>)</big><a class="reference
internal"
href="../../_modules/afterburner/experimental/quota.html#QuotaConsumer"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.quota.QuotaConsumer" title="Permalink to
this definition">¶</a></dt>
+<dd><p>Quota consumer wrapper for efficient quota consuming/reclaiming.</p>
+<p>Quota is consumed in batches and put back in dispose() method.</p>
+<p>WARNING: Always call the dispose() method if you need to keep quota
+consistent.</p>
+<dl class="method">
+<dt id="afterburner.experimental.quota.QuotaConsumer.check">
+<tt class="descname">check</tt><big>(</big><em>amount=1</em><big>)</big><a
class="reference internal"
href="../../_modules/afterburner/experimental/quota.html#QuotaConsumer.check"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.quota.QuotaConsumer.check" title="Permalink
to this definition">¶</a></dt>
+<dd><p>Check that we have enough quota right now.</p>
+<p>This doesn’t lock or consume the quota. Following consume might
in fact
+fail/succeeded.</p>
+<dl class="docutils">
+<dt>Args:</dt>
+<dd>amount: amount of quota to check.</dd>
+<dt>Returns:</dt>
+<dd>True if we have enough quota to consume specified amount right now.
False
+otherwise.</dd>
+</dl>
+</dd></dl>
+
+<dl class="method">
+<dt id="afterburner.experimental.quota.QuotaConsumer.consume">
+<tt
class="descname">consume</tt><big>(</big><em>amount=1</em><big>)</big><a
class="reference internal"
href="../../_modules/afterburner/experimental/quota.html#QuotaConsumer.consume"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.quota.QuotaConsumer.consume"
title="Permalink to this definition">¶</a></dt>
+<dd><p>Consume quota.</p>
+<dl class="docutils">
+<dt>Args:</dt>
+<dd>amount: amount of quota to be consumed as int.</dd>
+<dt>Returns:</dt>
+<dd>True if quota was successfully consumed, False if there’s not
enough
+quota.</dd>
+</dl>
+</dd></dl>
+
+<dl class="method">
+<dt id="afterburner.experimental.quota.QuotaConsumer.dispose">
+<tt class="descname">dispose</tt><big>(</big><big>)</big><a
class="reference internal"
href="../../_modules/afterburner/experimental/quota.html#QuotaConsumer.dispose"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.quota.QuotaConsumer.dispose"
title="Permalink to this definition">¶</a></dt>
+<dd><p>Dispose QuotaConsumer and put all actually unconsumed quota
back.</p>
+<p>This method has to be called for quota consistency!</p>
+</dd></dl>
+
+<dl class="method">
+<dt id="afterburner.experimental.quota.QuotaConsumer.put">
+<tt class="descname">put</tt><big>(</big><em>amount=1</em><big>)</big><a
class="reference internal"
href="../../_modules/afterburner/experimental/quota.html#QuotaConsumer.put"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.quota.QuotaConsumer.put" title="Permalink
to this definition">¶</a></dt>
+<dd><p>Put quota back.</p>
+<dl class="docutils">
+<dt>Args:</dt>
+<dd>amount: amount of quota as int.</dd>
+</dl>
+</dd></dl>
+
+</dd></dl>
+
+<dl class="class">
+<dt id="afterburner.experimental.quota.QuotaManager">
+<em class="property">class </em><tt
class="descclassname">afterburner.experimental.quota.</tt><tt
class="descname">QuotaManager</tt><big>(</big><em>memcache_client=<google.appengine.api.memcache.Client
object at 0x18eab70></em><big>)</big><a class="reference internal"
href="../../_modules/afterburner/experimental/quota.html#QuotaManager"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.quota.QuotaManager" title="Permalink to
this definition">¶</a></dt>
+<dd><p>Simple quota system manager, backed by memcache storage.</p>
+<p>Since memcache storage is not reliable, this quota system is not
reliable and
+best effort only.</p>
+<p>Quota is managed by buckets. Each bucket contains a 32-bit int value of
+available quota. Buckets should be refilled manually with
‘put’ method.</p>
+<p>It is safe to use a single bucket from multiple clients
simultaneously.</p>
+<dl class="method">
+<dt id="afterburner.experimental.quota.QuotaManager.consume">
+<tt class="descname">consume</tt><big>(</big><em>bucket</em>,
<em>amount</em>, <em>consume_some=False</em><big>)</big><a class="reference
internal"
href="../../_modules/afterburner/experimental/quota.html#QuotaManager.consume"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.quota.QuotaManager.consume"
title="Permalink to this definition">¶</a></dt>
+<dd><p>Consume amount from quota bucket.</p>
+<dl class="docutils">
+<dt>Args:</dt>
+<dd><p class="first">bucket: quota bucket as string.
+amount: amount to consume.
+consume_some: specifies behavior in case of not enough quota. If False,</p>
+<blockquote class="last">
+the method will leave quota intact and return 0. If True, will try to
+consume as much as possible.</blockquote>
+</dd>
+<dt>Returns:</dt>
+<dd>Amount of quota consumed.</dd>
+</dl>
+</dd></dl>
+
+<dl class="method">
+<dt id="afterburner.experimental.quota.QuotaManager.get">
+<tt class="descname">get</tt><big>(</big><em>bucket</em><big>)</big><a
class="reference internal"
href="../../_modules/afterburner/experimental/quota.html#QuotaManager.get"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.quota.QuotaManager.get" title="Permalink to
this definition">¶</a></dt>
+<dd><p>Get current bucket amount.</p>
+<dl class="docutils">
+<dt>Args:</dt>
+<dd>bucket: quota bucket as string.</dd>
+<dt>Returns:</dt>
+<dd>current bucket amount as int.</dd>
+</dl>
+</dd></dl>
+
+<dl class="method">
+<dt id="afterburner.experimental.quota.QuotaManager.put">
+<tt class="descname">put</tt><big>(</big><em>bucket</em>,
<em>amount</em><big>)</big><a class="reference internal"
href="../../_modules/afterburner/experimental/quota.html#QuotaManager.put"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.quota.QuotaManager.put" title="Permalink to
this definition">¶</a></dt>
+<dd><p>Put amount into quota bucket.</p>
+<dl class="docutils">
+<dt>Args:</dt>
+<dd>bucket: quota bucket as string.
+amount: amount to bit put into quota as int.</dd>
+</dl>
+</dd></dl>
+
+<dl class="method">
+<dt id="afterburner.experimental.quota.QuotaManager.set">
+<tt class="descname">set</tt><big>(</big><em>bucket</em>,
<em>amount</em><big>)</big><a class="reference internal"
href="../../_modules/afterburner/experimental/quota.html#QuotaManager.set"><span
class="viewcode-link">[source]</span></a><a class="headerlink"
href="#afterburner.experimental.quota.QuotaManager.set" title="Permalink to
this definition">¶</a></dt>
+<dd><p>Set bucket amount.</p>
+<dl class="docutils">
+<dt>Args:</dt>
+<dd>bucket: quota bucket as string.
+amount: new bucket amount as int.</dd>
+</dl>
+</dd></dl>
+
+</dd></dl>
+
</div>
@@ -78,6 +199,9 @@
<li class="right" style="margin-right: 10px">
<a href="../../genindex.html" title="General Index"
>index</a></li>
+ <li class="right" >
+ <a href="../../py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="../../index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
</ul>
</div>
=======================================
--- /doc/python/html/genindex.html Fri Nov 5 21:31:46 2010
+++ /doc/python/html/genindex.html Mon Nov 15 04:53:40 2010
@@ -31,6 +31,9 @@
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
</ul>
</div>
@@ -44,8 +47,87 @@
<h1 id="index">Index</h1>
<div class="genindex-jumpbox">
-
+ <a href="#A"><strong>A</strong></a> | <a
href="#C"><strong>C</strong></a> | <a href="#D"><strong>D</strong></a> | <a
href="#G"><strong>G</strong></a> | <a href="#M"><strong>M</strong></a> | <a
href="#P"><strong>P</strong></a> | <a href="#Q"><strong>Q</strong></a> | <a
href="#S"><strong>S</strong></a> | <a href="#T"><strong>T</strong></a>
</div>
+<h2 id="A">A</h2>
+<table width="100%" class="indextable genindextable"><tr>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/db/triggers.html#module-afterburner.experimental.db.triggers">afterburner.experimental.db.triggers
(module)</a></dt>
+ <dt><a
href="afterburner/experimental/memoize.html#module-afterburner.experimental.memoize">afterburner.experimental.memoize
(module)</a></dt>
+</dl></td>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/quota.html#module-afterburner.experimental.quota">afterburner.experimental.quota
(module)</a></dt>
+</dl></td>
+</tr></table>
+
+<h2 id="C">C</h2>
+<table width="100%" class="indextable genindextable"><tr>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaConsumer.check">check()
(afterburner.experimental.quota.QuotaConsumer method)</a></dt>
+</dl></td>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaConsumer.consume">consume()
(afterburner.experimental.quota.QuotaConsumer method)</a></dt>
+ <dd><dl>
+ <dt><a
href="afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaManager.consume">(afterburner.experimental.quota.QuotaManager
method)</a></dt>
+ </dl></dd>
+</dl></td>
+</tr></table>
+
+<h2 id="D">D</h2>
+<table width="100%" class="indextable genindextable"><tr>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaConsumer.dispose">dispose()
(afterburner.experimental.quota.QuotaConsumer method)</a></dt>
+</dl></td>
+</tr></table>
+
+<h2 id="G">G</h2>
+<table width="100%" class="indextable genindextable"><tr>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaManager.get">get()
(afterburner.experimental.quota.QuotaManager method)</a></dt>
+</dl></td>
+</tr></table>
+
+<h2 id="M">M</h2>
+<table width="100%" class="indextable genindextable"><tr>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/memoize.html#afterburner.experimental.memoize.memoize">memoize()
(in module afterburner.experimental.memoize)</a></dt>
+</dl></td>
+</tr></table>
+
+<h2 id="P">P</h2>
+<table width="100%" class="indextable genindextable"><tr>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaConsumer.put">put()
(afterburner.experimental.quota.QuotaConsumer method)</a></dt>
+ <dd><dl>
+ <dt><a
href="afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaManager.put">(afterburner.experimental.quota.QuotaManager
method)</a></dt>
+ </dl></dd>
+</dl></td>
+</tr></table>
+
+<h2 id="Q">Q</h2>
+<table width="100%" class="indextable genindextable"><tr>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaConsumer">QuotaConsumer
(class in afterburner.experimental.quota)</a></dt>
+</dl></td>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaManager">QuotaManager
(class in afterburner.experimental.quota)</a></dt>
+</dl></td>
+</tr></table>
+
+<h2 id="S">S</h2>
+<table width="100%" class="indextable genindextable"><tr>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/quota.html#afterburner.experimental.quota.QuotaManager.set">set()
(afterburner.experimental.quota.QuotaManager method)</a></dt>
+</dl></td>
+</tr></table>
+
+<h2 id="T">T</h2>
+<table width="100%" class="indextable genindextable"><tr>
+ <td width="33%" valign="top"><dl>
+ <dt><a
href="afterburner/experimental/db/triggers.html#afterburner.experimental.db.triggers.trigger">trigger()
(in module afterburner.experimental.db.triggers)</a></dt>
+</dl></td>
+</tr></table>
+
</div>
@@ -79,6 +161,9 @@
<li class="right" style="margin-right: 10px">
<a href="#" title="General Index"
>index</a></li>
+ <li class="right" >
+ <a href="py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
</ul>
</div>
=======================================
--- /doc/python/html/index.html Fri Nov 5 21:31:46 2010
+++ /doc/python/html/index.html Mon Nov 15 04:53:40 2010
@@ -31,6 +31,9 @@
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="#">Appengine-Afterburner v0.0.1 documentation</a>
»</li>
</ul>
</div>
@@ -53,11 +56,11 @@
<div class="section" id="experimental-modules">
<h2>Experimental Modules<a class="headerlink" href="#experimental-modules"
title="Permalink to this headline">¶</a></h2>
<ul class="simple">
-<li><tt class="xref py py-mod docutils literal"><span
class="pre">afterburner.experimental.quota</span></tt> - simple
memcache-based quota
+<li><a class="reference internal"
href="afterburner/experimental/quota.html#module-afterburner.experimental.quota"
title="afterburner.experimental.quota"><tt class="xref py py-mod docutils
literal"><span class="pre">afterburner.experimental.quota</span></tt></a> -
simple memcache-based quota
system for rate-limiting operations.</li>
-<li><tt class="xref py py-mod docutils literal"><span
class="pre">afterburner.experimental.memoize</span></tt> - function
decorator to cache
+<li><a class="reference internal"
href="afterburner/experimental/memoize.html#module-afterburner.experimental.memoize"
title="afterburner.experimental.memoize"><tt class="xref py py-mod docutils
literal"><span class="pre">afterburner.experimental.memoize</span></tt></a>
- function decorator to cache
function value in memcache.</li>
-<li><tt class="xref py py-mod docutils literal"><span
class="pre">afterburner.experimental.db.triggers</span></tt> - datastore
triggers system.</li>
+<li><a class="reference internal"
href="afterburner/experimental/db/triggers.html#module-afterburner.experimental.db.triggers"
title="afterburner.experimental.db.triggers"><tt class="xref py py-mod
docutils literal"><span
class="pre">afterburner.experimental.db.triggers</span></tt></a> -
datastore triggers system.</li>
</ul>
</div>
<div class="section" id="indices-and-tables">
@@ -114,6 +117,9 @@
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
+ <li class="right" >
+ <a href="py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="#">Appengine-Afterburner v0.0.1 documentation</a>
»</li>
</ul>
</div>
=======================================
--- /doc/python/html/objects.inv Fri Nov 5 21:31:46 2010
+++ /doc/python/html/objects.inv Mon Nov 15 04:53:40 2010
@@ -2,5 +2,16 @@
# Project: Appengine-Afterburner
# Version: 0.0.1
# The remainder of this file is compressed using zlib.
-xÚËÍOÉÌKIP(.I±ÊILJÍQÐ5T(¨ÔÍ
-Jèe”äæ(+øæ§”æ¤*x‚„¸ÒSó°h‚‰Bu@” §& %g *„ˆA• C $¦§r ßæ1
+xÚ½TËNÃ0 ¼÷+,Á5 ¹öʉC% _àØ[ÇÂ Ô ©ðõ$u,¹PÀ
+˜Kdmfgf ¶2Lh Gä<ÛJÒƒDM‡Æ×F-?ðà•¼A; ô0‡6
+ô
+¤ ]2"Ô ±t8 ÆØ {Ž€GÂaCö
+l ¬ ‹á8‚
+´' ‚ñdrµUÑÆ
+Ê m mOÐÈ
+ÁÍ-j¾ff=öVp Ö•ñ³¾M å*
+” oP¦°€ËÙ?v§+êN‰áÜø>hê
+ÑßÑŸYÿÙ3~š¿;¢§á[< * ü`Ø Ed ÷F» &
+€¾¬+óy¸µ:ä ^‡’ _[ãò
+뮸aW ™ 7 ÿ°MñP{(3?•ĹJuÔâO;U¥MÙ^¥Cé3õ÷
+ûí[õ ¶’ÈÀ
=======================================
--- /doc/python/html/search.html Fri Nov 5 21:31:46 2010
+++ /doc/python/html/search.html Mon Nov 15 04:53:40 2010
@@ -37,6 +37,9 @@
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
+ <li class="right" >
+ <a href="py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
</ul>
</div>
@@ -85,6 +88,9 @@
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
+ <li class="right" >
+ <a href="py-modindex.html" title="Python Module Index"
+ >modules</a> |</li>
<li><a href="index.html">Appengine-Afterburner v0.0.1
documentation</a> »</li>
</ul>
</div>
=======================================
--- /doc/python/html/searchindex.js Fri Nov 5 21:31:46 2010
+++ /doc/python/html/searchindex.js Mon Nov 15 04:53:40 2010
@@ -1,1 +1,1 @@
-Search.setIndex({objects:{},terms:{oper:0,code:0,modul:[0,1,2,3],appengin:0,system:0,rate:0,tabl:0,stabl:0,speed:0,librari:0,index:0,decor:0,develop:0,trigger:[0,2],detail:0,current:0,experiment:0,memoiz:[0,1],lot:0,simpl:0,"function":0,applic:0,contribut:0,relat:0,quota:[0,3],base:0,indic:0,java:0,datastor:0,memcach:0,cach:0,valu:0,reusabl:0,search:0,see:0,structur:0,project:0,limit:0,contain:0,afterburn:0,about:0,page:0},objtypes:{},titles:["Appengine-Afterburner","Memoize
Module","Triggers
Module","Quota
Module"],objnames:{},filenames:["index","afterburner/experimental/memoize","afterburner/experimental/db/triggers","afterburner/experimental/quota"]})
+Search.setIndex({objects:{},terms:{oper:0,code:0,modul:0,appengin:0,detail:0,rate:0,tabl:0,stabl:0,speed:0,librari:0,index:0,decor:0,develop:0,trigger:0,system:0,current:0,experiment:0,memoiz:0,lot:0,simpl:0,"function":0,applic:0,contribut:0,see:0,relat:0,quota:0,base:0,indic:0,java:0,datastor:0,cach:0,valu:0,reusabl:0,search:0,memcach:0,structur:0,project:0,limit:0,contain:0,afterburn:0,about:0,page:0},objtypes:{},titles:["Appengine-Afterburner"],objnames:{},filenames:["index"]})
=======================================
--- /trunk/python/build.sh Fri Nov 5 21:22:06 2010
+++ /trunk/python/build.sh Mon Nov 15 04:53:40 2010
@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-dir=$(dirname $(readlink -f $0) )
+dir=$(dirname $0)
if [ -z $APPENGINE_SDK ]; then
echo "APPENGINE_SDK environment variable shoud be defined and should
point to appengine sdk folder"
@@ -56,6 +56,7 @@
doc () {
cd $dir/doc
+ echo `pwd`
make html
}