Recent Blog Postshttps://michael.merickel.org/feed.atom2011-08-23T00:00:00ZRecent blog postsWerkzeugOutgrowing Pyramid Handlershttps://michael.merickel.org/2011/8/23/outgrowing-pyramid-handlers2011-08-23T00:00:00ZMichael Merickel<p><a class="reference external" href="https://docs.pylonsproject.org/projects/pyramid_handlers/dev/">pyramid_handlers</a> is a
package introduced to the <a class="reference external" href="https://pylonsproject.org">Pyramid</a> suite of
supported addons in Pyramid 1.0 as a way to ease the transition for
developers coming from the Pylons framework during the public merger of the
two projects. It closely maps previous functionality from the Pylons concept
of <tt class="docutils literal">controllers</tt>. Handlers provide three main features:</p>
<ol class="arabic simple">
<li>Grouping of relevant code. A class provides a logical way to organize a
bunch of code related to a section of your site.</li>
<li>Syntactic sugar for exposing several URLs under a single route name. This
can help with generation and configuration verbosity.</li>
<li>A single location for routing. It's possible to look at the
<tt class="docutils literal">add_handler</tt> calls and determine not only what URLs are supported but
also where the code is that is handling those URLs.</li>
</ol>
<p>Some handler-style code:</p>
<div class="highlight"><pre><span></span><span class="c1"># main.py</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="s1">'home'</span><span class="p">,</span> <span class="s1">'/'</span><span class="p">,</span>
<span class="n">handler</span><span class="o">=</span><span class="s1">'handlers.main.MainHandler'</span><span class="p">,</span>
<span class="n">action</span><span class="o">=</span><span class="s1">'index'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="s1">'main'</span><span class="p">,</span> <span class="s1">'/{action}'</span><span class="p">,</span>
<span class="n">handler</span><span class="o">=</span><span class="s1">'handlers.main.MainHandler'</span><span class="p">,</span>
<span class="n">path_info</span><span class="o">=</span><span class="sa">r</span><span class="s1">'/(?!index)'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_handler</span><span class="p">(</span><span class="s1">'search'</span><span class="p">,</span> <span class="s1">'/search/{id}'</span><span class="p">,</span>
<span class="n">handler</span><span class="o">=</span><span class="s1">'handlers.main.MainHandler'</span><span class="p">,</span>
<span class="n">action</span><span class="o">=</span><span class="s1">'search'</span><span class="p">)</span>
<span class="c1"># handlers/main.py</span>
<span class="k">class</span> <span class="nc">MainHandler</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</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">request</span>
<span class="nd">@action</span><span class="p">(</span><span class="n">renderer</span><span class="o">=</span><span class="s1">'home.mako'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="c1"># vars exposed for home.mako</span>
<span class="p">}</span>
<span class="nd">@action</span><span class="p">(</span><span class="n">renderer</span><span class="o">=</span><span class="s1">'login.mako'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">login</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="c1"># vars exposed for login.mako</span>
<span class="p">}</span>
<span class="nd">@action</span><span class="p">(</span><span class="n">renderer</span><span class="o">=</span><span class="s1">'logout.mako'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">logout</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="c1"># vars exposed for logout.mako</span>
<span class="p">}</span>
<span class="nd">@action</span><span class="p">(</span><span class="n">renderer</span><span class="o">=</span><span class="s1">'search.mako'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{}</span>
</pre></div>
<div class="section" id="moving-to-pyramid-core">
<h2>Moving to Pyramid-Core</h2>
<p>Pyramid generalizes the relationship between URLs and views much further
than the original Pylons routing where there was one class method registered
per URL. Pyramid separates URLs from code by way of view lookup. It has no
constraints that the view must be a class (but it can!). Once the
matching route pattern has been computed for a URL, it then does a second
step to determine which view to call. Each of these steps may have predicates
that can determine per-request whether the route or the view should be
invoked.</p>
<p>Given that multiple views can be attached to a single route, Pyramid 1.2
introduces the <tt class="docutils literal">match_param</tt> view predicate which helps the user. This
will allow the user to register multiple views for the same route and then
control the dispatch based on a pattern in the URL, e.g. <tt class="docutils literal">{action}</tt>.</p>
<p>Below is the handler-style code translated into vanilla <tt class="docutils literal">add_route</tt> and
<tt class="docutils literal">view_config</tt> URL dispatch.</p>
<div class="highlight"><pre><span></span><span class="c1"># main.py</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span><span class="s1">'home'</span><span class="p">,</span> <span class="s1">'/'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span><span class="s1">'main'</span><span class="p">,</span> <span class="s1">'/{action}'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span><span class="s1">'search'</span><span class="p">,</span> <span class="s1">'/search/{id}'</span><span class="p">)</span>
<span class="n">config</span><span class="o">.</span><span class="n">scan</span><span class="p">()</span> <span class="c1"># required to find code decorated by view_config</span>
<span class="c1"># views/main.py</span>
<span class="k">class</span> <span class="nc">MainHandler</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</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">request</span>
<span class="nd">@view_config</span><span class="p">(</span><span class="n">route_name</span><span class="o">=</span><span class="s1">'home'</span><span class="p">,</span> <span class="n">renderer</span><span class="o">=</span><span class="s1">'home.mako'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="c1"># vars exposed for home.mako</span>
<span class="p">}</span>
<span class="nd">@view_config</span><span class="p">(</span><span class="n">route_name</span><span class="o">=</span><span class="s1">'main'</span><span class="p">,</span> <span class="n">match_param</span><span class="o">=</span><span class="s1">'action=login'</span><span class="p">,</span>
<span class="n">renderer</span><span class="o">=</span><span class="s1">'login.mako'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">login</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="c1"># vars exposed for login.mako</span>
<span class="p">}</span>
<span class="nd">@view_config</span><span class="p">(</span><span class="n">route_name</span><span class="o">=</span><span class="s1">'main'</span><span class="p">,</span> <span class="n">match_param</span><span class="o">=</span><span class="s1">'action=logout'</span><span class="p">,</span>
<span class="n">renderer</span><span class="o">=</span><span class="s1">'logout.mako'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">logout</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="c1"># vars exposed for logout.mako</span>
<span class="p">}</span>
<span class="nd">@view_config</span><span class="p">(</span><span class="n">route_name</span><span class="o">=</span><span class="s1">'main'</span><span class="p">,</span> <span class="n">match_param</span><span class="o">=</span><span class="s1">'action=search'</span><span class="p">,</span>
<span class="n">renderer</span><span class="o">=</span><span class="s1">'search.mako'</span><span class="p">)</span>
<span class="nd">@view_config</span><span class="p">(</span><span class="n">route_name</span><span class="o">=</span><span class="s1">'search'</span><span class="p">,</span> <span class="n">renderer</span><span class="o">=</span><span class="s1">'search.mako'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="p">{</span>
<span class="c1"># vars exposed for search.mako</span>
<span class="p">}</span>
</pre></div>
<div class="section" id="what-are-the-advantages">
<h3>What are the advantages?</h3>
<div class="section" id="explicit-is-better-than-implicit">
<h4>Explicit is better than implicit</h4>
<p>Very rarely do you actually want to expose <em>all</em> of the methods in a class
via the same URL patterns. By being explicit, the configuration avoids
unintended side-effects. For example, in the <tt class="docutils literal">pyramid_handlers</tt> code above,
we have to be careful to avoid <cite>/index</cite> being a valid URL by way of the
<tt class="docutils literal">path_info</tt> regular expression predicate and any other methods we add to
the class need to take into consideration all of the URL patterns it may
implicitly match. This is the definition of a maintenance nightmare.</p>
</div>
<div class="section" id="separation-of-concerns">
<h4>Separation of concerns</h4>
<p>In the handler code the actions are embedded in not only the methods
decorated by <tt class="docutils literal">@action</tt> but also in some of the <tt class="docutils literal">add_handler</tt> calls,
e.g. the <tt class="docutils literal">search</tt> route. Notice that in the Pyramid code the
<tt class="docutils literal">MainHandler.search</tt> method is very clearly handling two different routes,
at the point where the view is defined. This serves as a reminder while
implementing those functions that it needs to account for both possibilities.</p>
</div>
<div class="section" id="fewer-dependencies">
<h4>Fewer dependencies</h4>
<p>Removing the need for <tt class="docutils literal">pyramid_handlers</tt>, while small, encourages users to
learn the Pyramid API which is well-designed, extensible and capable of
handling a large number scenarios on its own merit.</p>
</div>
</div>
<div class="section" id="what-are-the-disadvantages">
<h3>What are the disadvantages?</h3>
<p>The major feature that <tt class="docutils literal">pyramid_handlers</tt> provides is a central location
where URLs are mapped to code. Using Pyramid's <tt class="docutils literal">add_route</tt> and <tt class="docutils literal">add_view</tt>
APIs provides an explicit separation between the URL and the view to which
this URL could map. Pyramid tries to help by providing <tt class="docutils literal">paster</tt> functions
like <tt class="docutils literal">pviews</tt> that will show, for a URL, what views exist. However, some
developers will prefer the ability to look at the <tt class="docutils literal">add_handler</tt> calls
directly and determine not only what URL is supported, but what code will be
executed for that URL.</p>
</div>
<div class="section" id="why-is-pyramid-s-routing-awesome">
<h3>Why is Pyramid's routing awesome?</h3>
<p>Whether you use <tt class="docutils literal">pyramid_handlers</tt> or the routing directly, hopefully you
can gain an appreciation for the configurability of Pyramid's URL Dispatch.
While Pyramid's configuration API is verbose, you are greatly rewarded by
way of fast runtimes and simpler view code. Since multiple views may be
attached to a route, you can leave the dispatch up to Pyramid, allowing your
views to focus on their single purpose, without requiring a bunch of
<tt class="docutils literal">if</tt>-statements to handle different functionality.</p>
</div>
</div>
TicTacToe and Long Polling with Pyramidhttps://michael.merickel.org/2011/6/21/tictactoe-and-long-polling-with-pyramid2011-06-21T00:00:00ZMichael Merickel<p>Long-polling in Python has always been complicated by the fact that it tends
to require an asynchronous web server. It's unrealistic to support thousands
(or event tens) of active connections given Python's threading issues
(the GIL, OS-level threads). There are several asynchronous solutions in
Python including
<a class="reference external" href="http://www.tornadoweb.org/">Tornado</a>,
<a class="reference external" href="http://twistedmatrix.com/trac/">Twisted</a>,
and <a class="reference external" href="http://www.gevent.org/">gevent</a>.</p>
<p>Both Tornado and Twisted force you to write code in callbacks to avoid
blocking during long-running operations like I/O. Unfortunately this requires
your whole application to be written differently from how we're used to
writing imperative, sequential code in Python.</p>
<div class="section" id="gevent">
<h2>gevent</h2>
<p>gevent is an asynchronous framework, but it has the unique ability to allow the
developer to almost completely ignore the fact that the code is executed
asynchronously. Your code will run on an event loop using greenlets which
feel like real threads, but are very light weight, trivial to spawn, and run
off of the gevent event loop. gevent is capable of monkey patching the
necessary Python standard libraries like socket, such that when I/O or other
blocking calls happen the current greenlet will suspend and the event loop will
resume another greenlet while it waits for a response from the blocking
operation. What this means for you is that you do not need to change your
Python code in order to run it in an asynchronous way.</p>
<p>While gevent can monkey patch the Python standard library, it can't do it all.
Fortunately, my favorite SQL database (PostgreSQL) already supports coroutines
and asynchronous execution. See Daniel Varrazzo's
<a class="reference external" href="https://bitbucket.org/dvarrazzo/psycogreen/src/77a9c05f5229/gevent/psyco_gevent.py">psycogreen</a>
repository for an example of configuring the psycopg2 driver to run under
gevent. This also means that developers can use their favorite ORM
(<a class="reference external" href="http://sqlalchemy.org">SQLAlchemy</a>) on top of psycopg2 to talk to a
PostgreSQL database.</p>
</div>
<div class="section" id="tictactoe">
<h2>TicTacToe</h2>
<p>As an experiment, I wanted a small application that could demonstrate long
polling in action. Chat servers are boring and overused, so a friend came up
with the idea of implementing TicTacToe, enabling various mobile devices to
connect and play against each other. The API is pretty straightforward,
allowing players to connect, be assigned to a game, and make moves. These are
all basic functions that can be easily implemented using Pyramid's URL
Dispatch:</p>
<div class="highlight"><pre><span></span><span class="n">config</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span><span class="s1">'api.play'</span><span class="p">,</span> <span class="s1">'/api/play'</span><span class="p">)</span>
<span class="nd">@view_config</span><span class="p">(</span><span class="n">route_name</span><span class="o">=</span><span class="s1">'api.play'</span><span class="p">,</span> <span class="n">request_method</span><span class="o">=</span><span class="s1">'POST'</span><span class="p">,</span> <span class="n">renderer</span><span class="o">=</span><span class="s1">'json'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">play_view</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="c1"># handle connecting a new player</span>
<span class="k">return</span> <span class="p">{</span>
<span class="s1">'game_id'</span><span class="p">:</span> <span class="n">game_id</span><span class="p">,</span>
<span class="s1">'client_id'</span><span class="p">:</span> <span class="n">client_id</span><span class="p">,</span>
<span class="s1">'name'</span><span class="p">:</span> <span class="n">name</span><span class="p">,</span>
<span class="p">}</span>
</pre></div>
<p>Long polling comes in when <tt class="docutils literal">playerX</tt> places an X in a location on the board and
we want to notify <tt class="docutils literal">playerO</tt> that it is their turn to move. To accomplish this,
each game has a queue of events that have occurred to get the board to the state
it is at currently. Each player can then watch this queue for changes. Each
player in each game is then expected to connect to the server and maintain a
connection until a new update happens which we can return in the response.</p>
<div class="section" id="handling-updates">
<h3>Handling Updates</h3>
<p>There is already a
<a class="reference external" href="http://blog.gevent.org/2009/10/10/simpler-long-polling-with-django-and-gevent/">well-documented</a> way to handle long polling in
a WSGI application by simply using <tt class="docutils literal">gevent.event.Event</tt> to block the active
request until the server is ready to notify each client. The caveat to this
solution is that the resources your web framework has allocated for each
request will remain in memory until an update occurs, ending the request.</p>
<p>gevent has a nice trick to get around this problem. The <tt class="docutils literal">gevent.queue.Queue</tt>
class can be used as a blocking iterator, and for anyone who knows about WSGI,
the actual response of a WSGI application is an iterator. The underlying server
will attempt to iterate across the <tt class="docutils literal">Queue</tt>, returning each message to the
client as part of a chunked response. A <tt class="docutils literal">Queue</tt> can be closed by pushing a <tt class="docutils literal">StopIteration</tt> exception into it. This tells the WSGI server that the
response is complete.</p>
<p>TicTacToe utilizes this to be able to release the resources Pyramid has
allocated for a request (including possible database connections that you
want the server to reclaim as quickly as possible). Each client polling for an
update is turned into a <tt class="docutils literal">Queue</tt> object which can be stored in memory to be
used when notifications occur.</p>
<p>The implementation of this basically boils down to a couple arrays. One holds
the timeline of updates, and the other stores the connected observers waiting
for notifications. The <tt class="docutils literal">Game</tt> then becomes a mechanism for grouping these
together. When a new update is added to the <tt class="docutils literal">Game</tt>, all observers are
notified. Using the <tt class="docutils literal">cursor</tt> pattern, the ability for clients to disconnect
and resume where they left off naturally falls out of the design.</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Game</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="nb">id</span>
<span class="bp">self</span><span class="o">.</span><span class="n">observers</span> <span class="o">=</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">updates</span> <span class="o">=</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">cursor</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">def</span> <span class="nf">add_update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">cursor</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">kw</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s1">'timestamp'</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()</span><span class="o">.</span><span class="n">isoformat</span><span class="p">())</span>
<span class="n">kw</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s1">'cursor'</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">updates</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">kw</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">notify_observers</span><span class="p">(</span><span class="n">kw</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">add_observer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">cursor</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
<span class="n">obs</span> <span class="o">=</span> <span class="n">Observer</span><span class="p">(</span><span class="n">game</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
<span class="k">if</span> <span class="n">cursor</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor</span> <span class="ow">or</span> <span class="n">cursor</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">obs</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">msg</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">updates</span><span class="p">[</span><span class="n">cursor</span><span class="o">+</span><span class="mi">1</span><span class="p">])</span>
<span class="n">obs</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="n">obs</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="ne">StopIteration</span><span class="p">)</span>
<span class="k">return</span> <span class="n">obs</span>
<span class="k">def</span> <span class="nf">remove_observer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obs</span><span class="p">):</span>
<span class="k">if</span> <span class="n">obs</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="p">:</span>
<span class="n">obs</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="ne">StopIteration</span><span class="p">)</span>
<span class="n">i</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">obs</span><span class="p">)</span>
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">notify_observers</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">):</span>
<span class="n">out</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="k">for</span> <span class="n">obs</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">observers</span><span class="p">:</span>
<span class="n">obs</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">out</span><span class="p">)</span>
<span class="n">obs</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="ne">StopIteration</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">observers</span> <span class="o">=</span> <span class="p">[]</span>
</pre></div>
<p>The <tt class="docutils literal">Observer</tt> is a simple subclass of a <tt class="docutils literal">Queue</tt> that provides a way to
monitor how long a client has been connected. gevent currently doesn't
provide a good way to tell when disconnections occur, so at some point it's
important to kill active connections that may have stagnated.</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Observer</span><span class="p">(</span><span class="n">Queue</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</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">kw</span><span class="p">):</span>
<span class="n">game</span> <span class="o">=</span> <span class="n">kw</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s1">'game'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">event</span> <span class="o">=</span> <span class="n">Event</span><span class="p">()</span>
<span class="n">Queue</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</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">kw</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">reaper</span><span class="p">():</span>
<span class="bp">self</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">clear</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">wait</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span>
<span class="n">game</span><span class="o">.</span><span class="n">remove_observer</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
<span class="n">gevent</span><span class="o">.</span><span class="n">spawn</span><span class="p">(</span><span class="n">reaper</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="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">set</span><span class="p">()</span>
<span class="k">return</span> <span class="n">Queue</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="bp">self</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">kw</span><span class="p">)</span>
</pre></div>
<p>The actual Pyramid code for handling the long polling connections becomes
trivial, as all we have to do is turn the connection into an <tt class="docutils literal">Observer</tt>
which we can return as the response.</p>
<div class="highlight"><pre><span></span><span class="n">config</span><span class="o">.</span><span class="n">add_route</span><span class="p">(</span><span class="s1">'api.updates'</span><span class="p">,</span> <span class="s1">'/api/updates/{game_id}'</span><span class="p">)</span>
<span class="nd">@view_config</span><span class="p">(</span><span class="n">route_name</span><span class="o">=</span><span class="s1">'api.updates'</span><span class="p">,</span> <span class="n">request_method</span><span class="o">=</span><span class="s1">'GET'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">updates_view</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
<span class="n">game_id</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'game_id'</span><span class="p">)</span>
<span class="n">cursor</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">GET</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'cursor'</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">game</span> <span class="o">=</span> <span class="n">find_game</span><span class="p">(</span><span class="n">game_id</span><span class="p">)</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">Response</span><span class="p">()</span>
<span class="n">r</span><span class="o">.</span><span class="n">content_type</span> <span class="o">=</span> <span class="s1">'application/json'</span>
<span class="n">r</span><span class="o">.</span><span class="n">app_iter</span> <span class="o">=</span> <span class="n">game</span><span class="o">.</span><span class="n">add_observer</span><span class="p">(</span><span class="n">cursor</span><span class="p">)</span>
<span class="k">return</span> <span class="n">r</span>
</pre></div>
<p>So the <tt class="docutils literal">Response</tt>'s <tt class="docutils literal">app_iter</tt> is simply a blocking <tt class="docutils literal">Queue</tt> to which we
can publish notifications!</p>
</div>
<div class="section" id="the-code">
<h3>The Code</h3>
<p>The full code is available on Github at <a class="reference external" href="https://github.com/mmerickel/tictactoe">https://github.com/mmerickel/tictactoe</a>.
The code also includes an iOS client which was developed with the help of
employees at <a class="reference external" href="http://www.componica.com">Componica</a>.
.</p>
</div>
</div>