<?xml version="1.0"?><!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" lang="en"><head><title>Twisted Documentation: Managing Clients of Perspectives</title><link href="../howto/stylesheet.css" type="text/css" rel="stylesheet" /><link href="http://twistedmatrix.com/users/acapnotic/" rel="author" title="Kevin Turner" /></head><body bgcolor="white"><h1 class="title">Managing Clients of Perspectives</h1><div class="toc"><ol><li><a href="#auto0">Overview</a></li><li><a href="#auto1">Managing Avatars</a></li><li><a href="#auto2">Managing Clients</a></li></ol></div><div class="content"><span></span><h2>Overview<a name="auto0"></a></h2><p>In all the <code base="twisted.spread.pb" class="API">IPerspective</code> we have shown so far, we ignored the <code>mind</code> argument and created a new <code>Avatar</code> for every connection. This is usually an easy design choice, and it works well for simple cases.</p><p>In more complicated cases, for example an <code>Avatar</code> that represents a player object which is persistent in the game universe, we will want connections from the same player to use the same <code>Avatar</code>.</p><p>Another thing which is necessary in more complicated scenarios is notifying a player asynchronously. While it is possible, of course, to allow a player to call <code>perspective_remoteListener(referencable)</code> that would mean both duplication of code and a higher latency in logging in, both bad.</p><p>In previous sections all realms looked to be identical. In this one we will show the usefulness of realms in accomplishing those two objectives.</p><h2>Managing Avatars<a name="auto1"></a></h2><p>The simplest way to manage persistent avatars is to use a straight-forward caching mechanism:</p><pre class="python"> <span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span> <span class="py-src-keyword">class</span> <span class="py-src-identifier">SimpleAvatar</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Avatar</span>): <span class="py-src-variable">greetings</span> = <span class="py-src-number">0</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">name</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_greet</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span> += <span class="py-src-number">1</span> <span class="py-src-keyword">return</span> <span class="py-src-string">"<%d>hello %s"</span> % (<span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span>) <span class="py-src-keyword">class</span> <span class="py-src-identifier">CachingRealm</span>: <span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>) <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span> = {} <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>): <span class="py-src-keyword">if</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">not</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>: <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span> <span class="py-src-keyword">if</span> <span class="py-src-variable">avatarId</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>: <span class="py-src-variable">p</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>[<span class="py-src-variable">avatarId</span>] <span class="py-src-keyword">else</span>: <span class="py-src-variable">p</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>[<span class="py-src-variable">avatarId</span>] = <span class="py-src-variable">SimpleAvatar</span>(<span class="py-src-variable">avatarId</span>) <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">p</span>, <span class="py-src-keyword">lambda</span>:<span class="py-src-variable">None</span> </pre><p>This gives us a perspective which counts the number of greetings it sent its client. Implementing a caching strategy, as opposed to generating a realm with the correct avatars already in it, is usually easier. This makes adding new checkers to the portal, or adding new users to a checker database, transparent. Otherwise, careful synchronization is needed between the checker and avatar is needed (much like the synchronization between UNIX's <code>/etc/shadow</code> and <code>/etc/passwd</code>).</p><p>Sometimes, however, an avatar will need enough per-connection state that it would be easier to generate a new avatar and cache something else. Here is an example of that:</p><pre class="python"> <span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span> <span class="py-src-keyword">class</span> <span class="py-src-identifier">Greeter</span>: <span class="py-src-variable">greetings</span> = <span class="py-src-number">0</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">hello</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span> += <span class="py-src-number">1</span> <span class="py-src-keyword">return</span> <span class="py-src-string">"<%d>hello"</span> % (<span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span>) <span class="py-src-keyword">class</span> <span class="py-src-identifier">SimpleAvatar</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Avatar</span>): <span class="py-src-variable">greetings</span> = <span class="py-src-number">0</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">name</span>, <span class="py-src-parameter">greeter</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">greeter</span> = <span class="py-src-variable">greeter</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_greet</span>(<span class="py-src-parameter">self</span>): <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">greeter</span>.<span class="py-src-variable">hello</span>()+<span class="py-src-string">' '</span>+<span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> <span class="py-src-keyword">class</span> <span class="py-src-identifier">CachingRealm</span>: <span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>) <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">greeters</span> = {} <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>): <span class="py-src-keyword">if</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">not</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>: <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span> <span class="py-src-keyword">if</span> <span class="py-src-variable">avatarId</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">greeters</span>: <span class="py-src-variable">p</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">greeters</span>[<span class="py-src-variable">avatarId</span>] <span class="py-src-keyword">else</span>: <span class="py-src-variable">p</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">greeters</span>[<span class="py-src-variable">avatarId</span>] = <span class="py-src-variable">Greeter</span>() <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">SimpleAvatar</span>(<span class="py-src-variable">avatarId</span>, <span class="py-src-variable">p</span>), <span class="py-src-keyword">lambda</span>:<span class="py-src-variable">None</span> </pre><p>It might seem tempting to use this pattern to have an avatar which is notified of new connections. However, the problems here are twofold: it would lead to a thin class which needs to forward all of its methods, and it would be impossible to know when disconnections occur. Luckily, there is a better pattern:</p><pre class="python"> <span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span> <span class="py-src-keyword">class</span> <span class="py-src-identifier">SimpleAvatar</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Avatar</span>): <span class="py-src-variable">greetings</span> = <span class="py-src-number">0</span> <span class="py-src-variable">connections</span> = <span class="py-src-number">0</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">name</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">connect</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">connections</span> += <span class="py-src-number">1</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">disconnect</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">connections</span> -= <span class="py-src-number">1</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_greet</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span> += <span class="py-src-number">1</span> <span class="py-src-keyword">return</span> <span class="py-src-string">"<%d>hello %s"</span> % (<span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span>) <span class="py-src-keyword">class</span> <span class="py-src-identifier">CachingRealm</span>: <span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>) <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span> = {} <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>): <span class="py-src-keyword">if</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">not</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>: <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span> <span class="py-src-keyword">if</span> <span class="py-src-variable">avatarId</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>: <span class="py-src-variable">p</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>[<span class="py-src-variable">avatarId</span>] <span class="py-src-keyword">else</span>: <span class="py-src-variable">p</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>[<span class="py-src-variable">avatarId</span>] = <span class="py-src-variable">SimpleAvatar</span>(<span class="py-src-variable">avatarId</span>) <span class="py-src-variable">p</span>.<span class="py-src-variable">connect</span>() <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">p</span>, <span class="py-src-variable">p</span>.<span class="py-src-variable">disconnect</span> </pre><p>It is possible to use such a pattern to define an arbitrary limit for the number of concurrent connections:</p><pre class="python"> <span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span> <span class="py-src-keyword">class</span> <span class="py-src-identifier">SimpleAvatar</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Avatar</span>): <span class="py-src-variable">greetings</span> = <span class="py-src-number">0</span> <span class="py-src-variable">connections</span> = <span class="py-src-number">0</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">name</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">connect</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">connections</span> += <span class="py-src-number">1</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">disconnect</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">connections</span> -= <span class="py-src-number">1</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_greet</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span> += <span class="py-src-number">1</span> <span class="py-src-keyword">return</span> <span class="py-src-string">"<%d>hello %s"</span> % (<span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span>) <span class="py-src-keyword">class</span> <span class="py-src-identifier">CachingRealm</span>: <span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>) <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">max</span>=<span class="py-src-number">1</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span> = {} <span class="py-src-variable">self</span>.<span class="py-src-variable">max</span> = <span class="py-src-variable">max</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>): <span class="py-src-keyword">if</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">not</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>: <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span> <span class="py-src-keyword">if</span> <span class="py-src-variable">avatarId</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>: <span class="py-src-variable">p</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>[<span class="py-src-variable">avatarId</span>] <span class="py-src-keyword">else</span>: <span class="py-src-variable">p</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>[<span class="py-src-variable">avatarId</span>] = <span class="py-src-variable">SimpleAvatar</span>(<span class="py-src-variable">avatarId</span>) <span class="py-src-keyword">if</span> <span class="py-src-variable">p</span>.<span class="py-src-variable">connections</span> >= <span class="py-src-variable">self</span>.<span class="py-src-variable">max</span>: <span class="py-src-keyword">raise</span> <span class="py-src-variable">ValueError</span>(<span class="py-src-string">"too many connections"</span>) <span class="py-src-variable">p</span>.<span class="py-src-variable">connect</span>() <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">p</span>, <span class="py-src-variable">p</span>.<span class="py-src-variable">disconnect</span> </pre><h2>Managing Clients<a name="auto2"></a></h2><p>So far, all our realms have ignored the <code>mind</code> argument. In the case of PB, the <code>mind</code> is an object supplied by the remote login method -- usually, when it passes over the wire, it becomes a <code>pb.RemoteReference</code>. This object allows sending messages to the client as soon as the connection is established and authenticated.</p><p>Here is a simple remote-clock application which shows the usefulness of the <code>mind</code> argument:</p><pre class="python"> <span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span> <span class="py-src-keyword">class</span> <span class="py-src-identifier">SimpleAvatar</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Avatar</span>): <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">client</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">s</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TimerService</span>(<span class="py-src-number">1</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">telltime</span>) <span class="py-src-variable">self</span>.<span class="py-src-variable">s</span>.<span class="py-src-variable">startService</span>() <span class="py-src-variable">self</span>.<span class="py-src-variable">client</span> = <span class="py-src-variable">client</span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">telltime</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">client</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">"notifyTime"</span>, <span class="py-src-variable">time</span>.<span class="py-src-variable">time</span>()) <span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_setperiod</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">period</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">s</span>.<span class="py-src-variable">stopService</span>() <span class="py-src-variable">self</span>.<span class="py-src-variable">s</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TimerService</span>(<span class="py-src-variable">period</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">telltime</span>) <span class="py-src-variable">self</span>.<span class="py-src-variable">s</span>.<span class="py-src-variable">startService</span>() <span class="py-src-keyword">def</span> <span class="py-src-identifier">logout</span>(<span class="py-src-parameter">self</span>): <span class="py-src-variable">self</span>.<span class="py-src-variable">s</span>.<span class="py-src-variable">stopService</span>() <span class="py-src-keyword">class</span> <span class="py-src-identifier">Realm</span>: <span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>) <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>): <span class="py-src-keyword">if</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">not</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>: <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span> <span class="py-src-variable">p</span> = <span class="py-src-variable">SimpleAvatar</span>(<span class="py-src-variable">mind</span>) <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">p</span>, <span class="py-src-variable">p</span>.<span class="py-src-variable">logout</span> </pre><p>In more complicated situations, you might want to cache the avatars and give each one a set of <q>current clients</q> or something similar.</p></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 2.5.0</span></body></html>