1
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
3
<title>Twisted Documentation: Managing Clients of Perspectives</title>
4
<link href="stylesheet.css" rel="stylesheet" type="text/css"/>
5
<link href="http://twistedmatrix.com/users/acapnotic/" rel="author" title="Kevin Turner"/></head>
8
<h1 class="title">Managing Clients of Perspectives</h1>
9
<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>
13
<h2>Overview<a name="auto0"/></h2>
15
<p>In all the <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.IPerspective.html" title="twisted.spread.pb.IPerspective">IPerspective</a></code>
16
we have shown so far, we ignored the <code>mind</code> argument and created
17
a new <code>Avatar</code> for every connection. This is usually an easy
18
design choice, and it works well for simple cases.</p>
20
<p>In more complicated cases, for example an <code>Avatar</code> that
21
represents a player object which is persistent in the game universe,
22
we will want connections from the same player to use the same
23
<code>Avatar</code>.</p>
25
<p>Another thing which is necessary in more complicated scenarios
26
is notifying a player asynchronously. While it is possible, of
27
course, to allow a player to call
28
<code>perspective_remoteListener(referencable)</code> that would
29
mean both duplication of code and a higher latency in logging in,
32
<p>In previous sections all realms looked to be identical.
33
In this one we will show the usefulness of realms in accomplishing
34
those two objectives.</p>
36
<h2>Managing Avatars<a name="auto1"/></h2>
38
<p>The simplest way to manage persistent avatars is to use a straight-forward
39
caching mechanism:</p>
41
<pre class="python"><p class="py-linenumber"> 1
64
</p><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>
66
<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>):
67
<span class="py-src-variable">greetings</span> = <span class="py-src-number">0</span>
68
<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>):
69
<span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
70
<span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_greet</span>(<span class="py-src-parameter">self</span>):
71
<span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span> += <span class="py-src-number">1</span>
72
<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>)
74
<span class="py-src-keyword">class</span> <span class="py-src-identifier">CachingRealm</span>:
75
<span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>)
77
<span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
78
<span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span> = {}
80
<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>):
81
<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>
82
<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>:
83
<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>]
84
<span class="py-src-keyword">else</span>:
85
<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>)
86
<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>
89
<p>This gives us a perspective which counts the number of greetings it
90
sent its client. Implementing a caching strategy, as opposed to generating
91
a realm with the correct avatars already in it, is usually easier. This
92
makes adding new checkers to the portal, or adding new users to a checker
93
database, transparent. Otherwise, careful synchronization is needed between
94
the checker and avatar is needed (much like the synchronization between
95
UNIX's <code>/etc/shadow</code> and <code>/etc/passwd</code>).</p>
97
<p>Sometimes, however, an avatar will need enough per-connection state
98
that it would be easier to generate a new avatar and cache something
99
else. Here is an example of that:</p>
101
<pre class="python"><p class="py-linenumber"> 1
130
</p><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>
132
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Greeter</span>:
133
<span class="py-src-variable">greetings</span> = <span class="py-src-number">0</span>
134
<span class="py-src-keyword">def</span> <span class="py-src-identifier">hello</span>(<span class="py-src-parameter">self</span>):
135
<span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span> += <span class="py-src-number">1</span>
136
<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>)
138
<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>):
139
<span class="py-src-variable">greetings</span> = <span class="py-src-number">0</span>
140
<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>):
141
<span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
142
<span class="py-src-variable">self</span>.<span class="py-src-variable">greeter</span> = <span class="py-src-variable">greeter</span>
143
<span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_greet</span>(<span class="py-src-parameter">self</span>):
144
<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>
146
<span class="py-src-keyword">class</span> <span class="py-src-identifier">CachingRealm</span>:
147
<span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>)
149
<span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
150
<span class="py-src-variable">self</span>.<span class="py-src-variable">greeters</span> = {}
152
<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>):
153
<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>
154
<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>:
155
<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>]
156
<span class="py-src-keyword">else</span>:
157
<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>()
158
<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>
161
<p>It might seem tempting to use this pattern to have an avatar which
162
is notified of new connections. However, the problems here are twofold:
163
it would lead to a thin class which needs to forward all of its methods,
164
and it would be impossible to know when disconnections occur. Luckily,
165
there is a better pattern:</p>
167
<pre class="python"><p class="py-linenumber"> 1
196
</p><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>
198
<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>):
199
<span class="py-src-variable">greetings</span> = <span class="py-src-number">0</span>
200
<span class="py-src-variable">connections</span> = <span class="py-src-number">0</span>
201
<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>):
202
<span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
203
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connect</span>(<span class="py-src-parameter">self</span>):
204
<span class="py-src-variable">self</span>.<span class="py-src-variable">connections</span> += <span class="py-src-number">1</span>
205
<span class="py-src-keyword">def</span> <span class="py-src-identifier">disconnect</span>(<span class="py-src-parameter">self</span>):
206
<span class="py-src-variable">self</span>.<span class="py-src-variable">connections</span> -= <span class="py-src-number">1</span>
207
<span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_greet</span>(<span class="py-src-parameter">self</span>):
208
<span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span> += <span class="py-src-number">1</span>
209
<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>)
211
<span class="py-src-keyword">class</span> <span class="py-src-identifier">CachingRealm</span>:
212
<span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>)
214
<span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
215
<span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span> = {}
217
<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>):
218
<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>
219
<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>:
220
<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>]
221
<span class="py-src-keyword">else</span>:
222
<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>)
223
<span class="py-src-variable">p</span>.<span class="py-src-variable">connect</span>()
224
<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>
227
<p>It is possible to use such a pattern to define an arbitrary limit for
228
the number of concurrent connections:</p>
230
<pre class="python"><p class="py-linenumber"> 1
262
</p><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>
264
<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>):
265
<span class="py-src-variable">greetings</span> = <span class="py-src-number">0</span>
266
<span class="py-src-variable">connections</span> = <span class="py-src-number">0</span>
267
<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>):
268
<span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
269
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connect</span>(<span class="py-src-parameter">self</span>):
270
<span class="py-src-variable">self</span>.<span class="py-src-variable">connections</span> += <span class="py-src-number">1</span>
271
<span class="py-src-keyword">def</span> <span class="py-src-identifier">disconnect</span>(<span class="py-src-parameter">self</span>):
272
<span class="py-src-variable">self</span>.<span class="py-src-variable">connections</span> -= <span class="py-src-number">1</span>
273
<span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_greet</span>(<span class="py-src-parameter">self</span>):
274
<span class="py-src-variable">self</span>.<span class="py-src-variable">greetings</span> += <span class="py-src-number">1</span>
275
<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>)
277
<span class="py-src-keyword">class</span> <span class="py-src-identifier">CachingRealm</span>:
278
<span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>)
280
<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>):
281
<span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span> = {}
282
<span class="py-src-variable">self</span>.<span class="py-src-variable">max</span> = <span class="py-src-variable">max</span>
284
<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>):
285
<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>
286
<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>:
287
<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>]
288
<span class="py-src-keyword">else</span>:
289
<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>)
290
<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>:
291
<span class="py-src-keyword">raise</span> <span class="py-src-variable">ValueError</span>(<span class="py-src-string">"too many connections"</span>)
292
<span class="py-src-variable">p</span>.<span class="py-src-variable">connect</span>()
293
<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>
296
<h2>Managing Clients<a name="auto2"/></h2>
298
<p>So far, all our realms have ignored the <code>mind</code> argument.
299
In the case of PB, the <code>mind</code> is an object supplied by
300
the remote login method -- usually, when it passes over the wire,
301
it becomes a <code>pb.RemoteReference</code>. This object allows
302
sending messages to the client as soon as the connection is established
303
and authenticated.</p>
305
<p>Here is a simple remote-clock application which shows the usefulness
306
of the <code>mind</code> argument:</p>
308
<pre class="python"><p class="py-linenumber"> 1
331
</p><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>
333
<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>):
334
<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>):
335
<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>)
336
<span class="py-src-variable">self</span>.<span class="py-src-variable">s</span>.<span class="py-src-variable">startService</span>()
337
<span class="py-src-variable">self</span>.<span class="py-src-variable">client</span> = <span class="py-src-variable">client</span>
338
<span class="py-src-keyword">def</span> <span class="py-src-identifier">telltime</span>(<span class="py-src-parameter">self</span>):
339
<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>())
340
<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>):
341
<span class="py-src-variable">self</span>.<span class="py-src-variable">s</span>.<span class="py-src-variable">stopService</span>()
342
<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>)
343
<span class="py-src-variable">self</span>.<span class="py-src-variable">s</span>.<span class="py-src-variable">startService</span>()
344
<span class="py-src-keyword">def</span> <span class="py-src-identifier">logout</span>(<span class="py-src-parameter">self</span>):
345
<span class="py-src-variable">self</span>.<span class="py-src-variable">s</span>.<span class="py-src-variable">stopService</span>()
347
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Realm</span>:
348
<span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>)
350
<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>):
351
<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>
352
<span class="py-src-variable">p</span> = <span class="py-src-variable">SimpleAvatar</span>(<span class="py-src-variable">mind</span>)
353
<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>
356
<p>In more complicated situations, you might want to cache the avatars
357
and give each one a set of <q>current clients</q> or something similar.</p>
361
<p><a href="index.html">Index</a></p>
362
<span class="version">Version: 10.0.0</span>
b'\\ No newline at end of file'