Merge lp:~exarkun/divmod.org/remove-epsilon-1325289 into lp:divmod.org

Proposed by Jean-Paul Calderone on 2014-06-28
Status: Merged
Approved by: Tristan Seligmann on 2014-06-29
Approved revision: 2750
Merged at revision: 2750
Proposed branch: lp:~exarkun/divmod.org/remove-epsilon-1325289
Merge into: lp:divmod.org
Diff against target: 11136 lines (+1/-10718)
80 files modified
Divmod.pth (+1/-2)
Epsilon/.coveragerc (+0/-8)
Epsilon/LICENSE (+0/-20)
Epsilon/MANIFEST.in (+0/-3)
Epsilon/NAME.txt (+0/-13)
Epsilon/NEWS.txt (+0/-113)
Epsilon/README (+0/-9)
Epsilon/bin/benchmark (+0/-4)
Epsilon/bin/certcreate (+0/-5)
Epsilon/doc/amp-auth.xhtml (+0/-115)
Epsilon/doc/amp-routes.xhtml (+0/-180)
Epsilon/doc/index.xhtml (+0/-21)
Epsilon/doc/listings/amp/amp_auth_client.py (+0/-42)
Epsilon/doc/listings/amp/amp_auth_server.py (+0/-76)
Epsilon/doc/listings/amp/auth_client.py (+0/-37)
Epsilon/doc/listings/amp/auth_server.py (+0/-77)
Epsilon/doc/listings/amp/route_client.py (+0/-60)
Epsilon/doc/listings/amp/route_server.py (+0/-37)
Epsilon/doc/listings/amp/route_setup.py (+0/-49)
Epsilon/doc/stylesheet.css (+0/-129)
Epsilon/doc/template.tpl (+0/-23)
Epsilon/epsilon/__init__.py (+0/-8)
Epsilon/epsilon/_version.py (+0/-1)
Epsilon/epsilon/ampauth.py (+0/-312)
Epsilon/epsilon/amprouter.py (+0/-205)
Epsilon/epsilon/asplode.py (+0/-34)
Epsilon/epsilon/caseless.py (+0/-135)
Epsilon/epsilon/cooperator.py (+0/-32)
Epsilon/epsilon/descriptor.py (+0/-147)
Epsilon/epsilon/expose.py (+0/-141)
Epsilon/epsilon/extime.py (+0/-980)
Epsilon/epsilon/hotfix.py (+0/-81)
Epsilon/epsilon/hotfixes/deferredgenerator_tfailure.py (+0/-59)
Epsilon/epsilon/hotfixes/delayedcall_seconds.py (+0/-160)
Epsilon/epsilon/hotfixes/filepath_copyTo.py (+0/-403)
Epsilon/epsilon/hotfixes/internet_task_clock.py (+0/-38)
Epsilon/epsilon/hotfixes/loopbackasync_reentrancy.py (+0/-26)
Epsilon/epsilon/hotfixes/plugin_package_paths.py (+0/-42)
Epsilon/epsilon/hotfixes/proto_helpers_stringtransport.py (+0/-11)
Epsilon/epsilon/hotfixes/timeoutmixin_calllater.py (+0/-60)
Epsilon/epsilon/hotfixes/trial_assertwarns.py (+0/-61)
Epsilon/epsilon/iepsilon.py (+0/-25)
Epsilon/epsilon/juice.py (+0/-1009)
Epsilon/epsilon/liner.py (+0/-67)
Epsilon/epsilon/modal.py (+0/-131)
Epsilon/epsilon/pending.py (+0/-26)
Epsilon/epsilon/process.py (+0/-67)
Epsilon/epsilon/react.py (+0/-35)
Epsilon/epsilon/remember.py (+0/-39)
Epsilon/epsilon/scripts/benchmark.py (+0/-581)
Epsilon/epsilon/scripts/certcreate.py (+0/-62)
Epsilon/epsilon/setuphelper.py (+0/-77)
Epsilon/epsilon/spewer.py (+0/-125)
Epsilon/epsilon/structlike.py (+0/-183)
Epsilon/epsilon/test/iosim.py (+0/-110)
Epsilon/epsilon/test/mantissa-structure.py (+0/-67)
Epsilon/epsilon/test/test_ampauth.py (+0/-430)
Epsilon/epsilon/test/test_amprouter.py (+0/-319)
Epsilon/epsilon/test/test_benchmark.py (+0/-426)
Epsilon/epsilon/test/test_caseless.py (+0/-291)
Epsilon/epsilon/test/test_descriptor.py (+0/-172)
Epsilon/epsilon/test/test_expose.py (+0/-321)
Epsilon/epsilon/test/test_extime.py (+0/-499)
Epsilon/epsilon/test/test_juice.py (+0/-287)
Epsilon/epsilon/test/test_modes.py (+0/-60)
Epsilon/epsilon/test/test_process.py (+0/-74)
Epsilon/epsilon/test/test_react.py (+0/-191)
Epsilon/epsilon/test/test_remember.py (+0/-90)
Epsilon/epsilon/test/test_setuphelper.py (+0/-63)
Epsilon/epsilon/test/test_structlike.py (+0/-186)
Epsilon/epsilon/test/test_unrepr.py (+0/-7)
Epsilon/epsilon/test/test_version.py (+0/-19)
Epsilon/epsilon/test/test_view.py (+0/-457)
Epsilon/epsilon/test/utils.py (+0/-84)
Epsilon/epsilon/unrepr.py (+0/-54)
Epsilon/epsilon/view.py (+0/-53)
Epsilon/requirements-testing.txt (+0/-2)
Epsilon/setup.py (+0/-35)
Epsilon/testhotfix (+0/-8)
Epsilon/tox.ini (+0/-27)
To merge this branch: bzr merge lp:~exarkun/divmod.org/remove-epsilon-1325289
Reviewer Review Type Date Requested Status
Tristan Seligmann 2014-06-28 Approve on 2014-06-29
Review via email: mp+224912@code.launchpad.net

Description of the change

To post a comment you must log in.
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Divmod.pth'
2--- Divmod.pth 2014-06-08 12:14:35 +0000
3+++ Divmod.pth 2014-06-28 16:02:12 +0000
4@@ -1,7 +1,6 @@
5-# -*- test-case-name: axiom,combinator,epsilon,xmantissa,xquotient,reverend,sine,hyperbola -*-
6+# -*- test-case-name: axiom,combinator,xmantissa,xquotient,reverend,sine,hyperbola -*-
7 Axiom
8 Combinator
9-Epsilon
10 Mantissa
11 Quotient
12 Reverend
13
14=== removed directory 'Epsilon'
15=== removed file 'Epsilon/.coveragerc'
16--- Epsilon/.coveragerc 2014-01-13 11:18:20 +0000
17+++ Epsilon/.coveragerc 1970-01-01 00:00:00 +0000
18@@ -1,8 +0,0 @@
19-[run]
20-branch = True
21-source =
22- epsilon
23-
24-[report]
25-exclude_lines =
26- pragma: no cover
27
28=== removed file 'Epsilon/LICENSE'
29--- Epsilon/LICENSE 2005-12-10 22:31:51 +0000
30+++ Epsilon/LICENSE 1970-01-01 00:00:00 +0000
31@@ -1,20 +0,0 @@
32-Copyright (c) 2005 Divmod Inc.
33-
34-Permission is hereby granted, free of charge, to any person obtaining
35-a copy of this software and associated documentation files (the
36-"Software"), to deal in the Software without restriction, including
37-without limitation the rights to use, copy, modify, merge, publish,
38-distribute, sublicense, and/or sell copies of the Software, and to
39-permit persons to whom the Software is furnished to do so, subject to
40-the following conditions:
41-
42-The above copyright notice and this permission notice shall be
43-included in all copies or substantial portions of the Software.
44-
45-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
46-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
47-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
48-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
49-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
50-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
51-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52\ No newline at end of file
53
54=== removed file 'Epsilon/MANIFEST.in'
55--- Epsilon/MANIFEST.in 2014-01-15 10:08:27 +0000
56+++ Epsilon/MANIFEST.in 1970-01-01 00:00:00 +0000
57@@ -1,3 +0,0 @@
58-include LICENSE
59-include NAME.txt
60-include NEWS.txt
61
62=== removed file 'Epsilon/NAME.txt'
63--- Epsilon/NAME.txt 2005-08-27 23:09:07 +0000
64+++ Epsilon/NAME.txt 1970-01-01 00:00:00 +0000
65@@ -1,13 +0,0 @@
66-
67-See: http://mathworld.wolfram.com/Epsilon.html
68-
69-The constant 'epsilon' is a value that is as close as possible to zero without
70-being zero. It is frequently used by computer scientists to refer to values
71-which are negligeable.
72-
73-Divmod Epsilon is named for that because it is a small body of code upon which
74-all of our other projects depend. It has no particular theme associated with
75-it, except to remain small and lightweight, and enforce certain conventions and
76-to provide common conveniences that do not belong in any lower level of
77-infrastructure.
78-
79
80=== removed file 'Epsilon/NEWS.txt'
81--- Epsilon/NEWS.txt 2014-01-15 10:21:17 +0000
82+++ Epsilon/NEWS.txt 1970-01-01 00:00:00 +0000
83@@ -1,113 +0,0 @@
84-0.7.0 (2014-01-15):
85- Major:
86-
87- - Only Python 2.6 and 2.7 are supported now. 2.4, 2.5 is deprecated.
88- - setup.py now uses setuptools, and stores its dependencies. This
89- means you no longer need to manually install dependencies.
90- - setup.py no longer requires Twisted for egg_info, making it easier
91- to install Epsilon using pip.
92- - Significant improvements to PyPy support. PyPy is now a supported
93- platform, with CI support.
94- - epsilon.release is now removed. It relied on a bunch of machinery
95- specific to divmod that no longer existed.
96- - epsilon.sslverify is now removed. Use twisted.internet.ssl instead.
97- - epsilon.asTwistedVersion takes a string version ("1.2.3") and
98- turns it into a twisted.python.versions.Version.
99-
100- Minor:
101-
102- - Several deprecation warnings have been cleaned up.
103-
104-0.6.0 (2009-11-25):
105- - Disable loopback hotfix on Twisted 8.2 and newer.
106- - Remove the implementation of Cooperator and use Twisted's implementation
107- instead.
108- - Use Twisted's deferLater implementation.
109- - Add a service for communicating via stdio.
110- - Add a `precision` argument to `Time.asHumanly` to control the precision
111- of the returned string.
112-
113-0.5.12 (2008-12-09):
114- - Added support for AMP authentication via one-time pads.
115-
116-0.5.11 (2008-10-02):
117- - epsilon.amprouter added, providing support for multiplexing
118- unrelated AMP communications over the same connection.
119-
120-0.5.10 (2008-08-12):
121- - Added the epsilon.caseless module, with case-insensitive string
122- wrappers.
123- - Better repr() for epsilon.structlike.record added.
124- - epsilon.juice now uses twisted.internet.ssl instead of epsilon.sslverify.
125-
126-0.5.9 (2008-01-18):
127-
128-0.5.8 (2007-11-27):
129- - extime.Time.asHumanly() no longer shows a time of day for all-day timestamps.
130-
131-0.5.7 (2007-04-27):
132- - view.SlicedView added, allowing slicing and indexing of large
133- sequences without copying.
134-
135-0.5.6 (2006-11-20):
136- - Added a --quiet option to Epsilon's certcreate and use it in a few unit
137- tests to avoid spewing garbage during test runs.
138-
139-0.5.5 (2006-10-21):
140- - extime.Time now accepts RFC2822-like dates with invalid fields: it
141- rounds them to the nearest valid value.
142-
143-0.5.4 (2006-10-17):
144- - extime.Time now accepts RFC2822-like dates with no timezone.
145-
146-0.5.3 (2006-09-20):
147- - structlike.Record now raises TypeError on unexpected args.
148-
149-0.5.2 (2006-09-12):
150- - extime.Time now avoids time_t overflow bugs.
151-
152-0.5.1 (2006-06-22):
153- - Added hotfix for twisted.test.proto_helpers.StringTransport.
154-
155-0.5.0 (2006-06-12):
156- - Replaced '%y' with '%Y' in Time.asHumanly() output - the year is now
157- four digits, rather than two.
158- - Added new 'epsilon.structlike' functionality for simple record.
159- - All uses of defer.wait and deferredResult were removed from the tests.
160- - Added epsilon.juice, an asynchronous messaging protocol slated for
161- inclusion in Twisted. Improved a few features, such as the repr() of
162- JuiceBox instances. This was moved from Vertex.
163- - Added epsilon.sslverify, a set of utilities for dealing with PyOpenSSL
164- using simple high-level objects, performing operations such as signing and
165- verifying certificates. This was also moved from Vertex, and slated for
166- inclusion in Twisted.
167- - Added epsilon.spewer, a prettier version of the spewer in
168- twisted.python.util.
169- - Added "benchmark" tool for measuring and reporting run-times of python
170- programs.
171-
172-0.4.0 (2005-12-20):
173- - Disabled crazy sys.modules hackery in test_setuphelper
174- - Added module for creating a directory structure from a string template
175- - Added support for 'now' to Time.fromHumanly()
176- - Added a structured "hotfix" system to abstract and formalize monkey
177- patches and version testing logic away from code which requires it.
178-
179-0.3.2 (2005-11-05):
180- - Added automatic support for Twisted plugins to autosetup
181-
182-0.3.1 (2005-11-02):
183- - Removed bogus dependency on Axiom.
184-
185-0.3.0 (2005-11-02):
186- - Added SchedulingService, an IService implementation, to epsilon.cooperator
187- - Added autosetup, a utility to actually include files in distutils releases,
188- to epsilon.setuphelper
189-
190-0.2.1 (2005-10-25):
191- - Added 'short()' to epsilon.versions.Version
192- - fixed setup.py to use epsilon.version.short() rather than static string.
193-
194-0.2.0 (2005-10-25):
195- - Added epsilon.modal.ModalType, metaclass for writing classes that
196- behave in some respects like state machines
197
198=== removed file 'Epsilon/README'
199--- Epsilon/README 2006-06-14 11:54:41 +0000
200+++ Epsilon/README 1970-01-01 00:00:00 +0000
201@@ -1,9 +0,0 @@
202-
203-Divmod Epsilon
204-==============
205-
206-Epsilon is a set of utility modules, commonly used by all Divmod projects.
207-
208-This is intended mainly as a support package for code used by Divmod projects,
209-and not as an external library. However, it contains many useful modules and
210-you can feel free to use them!
211
212=== removed directory 'Epsilon/bin'
213=== removed file 'Epsilon/bin/benchmark'
214--- Epsilon/bin/benchmark 2006-05-19 15:23:46 +0000
215+++ Epsilon/bin/benchmark 1970-01-01 00:00:00 +0000
216@@ -1,4 +0,0 @@
217-#!/usr/bin/python
218-
219-from epsilon.scripts import benchmark
220-benchmark.main()
221
222=== removed file 'Epsilon/bin/certcreate'
223--- Epsilon/bin/certcreate 2006-06-08 19:22:15 +0000
224+++ Epsilon/bin/certcreate 1970-01-01 00:00:00 +0000
225@@ -1,5 +0,0 @@
226-#!/usr/bin/python
227-# Copyright 2005 Divmod, Inc. See LICENSE file for details
228-
229-from epsilon.scripts import certcreate
230-certcreate.main()
231
232=== removed directory 'Epsilon/doc'
233=== removed file 'Epsilon/doc/amp-auth.xhtml'
234--- Epsilon/doc/amp-auth.xhtml 2008-11-27 07:37:39 +0000
235+++ Epsilon/doc/amp-auth.xhtml 1970-01-01 00:00:00 +0000
236@@ -1,115 +0,0 @@
237-<?xml version="1.0"?>
238-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
239- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
240-
241-<html xmlns="http://www.w3.org/1999/xhtml">
242- <head>
243- <title>AMP Authentication</title>
244- </head>
245- <body>
246- <h1>AMP Authentication</h1>
247-
248- <h2>Overview</h2>
249-
250- <p>
251- <code class="API">epsilon.ampauth</code> integrates Twisted Cred with
252- <code>twisted.protocols.amp</code>, providing support for selecting a
253- <code>IBoxReceiver</code> based on the result of a Cred login.
254- </p>
255-
256- <p>
257- Readers should familiarize themselves with the following concepts in
258- order to understand all sections of this document:
259- </p>
260-
261- <ul>
262- <li>
263- Twisted <a
264- href="http://twistedmatrix.com/projects/core/documentation/howto/clients.html">TCP
265- clients</a> and <a
266- href="http://twistedmatrix.com/projects/core/documentation/howto/servers.html">TCP
267- servers</a>
268- </li>
269- <li>
270- <a href="http://twistedmatrix.com/projects/core/documentation/howto/defer.html">
271- Using Deferreds
272- </a>
273- </li>
274- <li>
275- Twisted <code class="API" base="twisted.protocols.amp">AMP</code>
276- </li>
277- <li>
278- <a href="http://twistedmatrix.com/projects/core/documentation/howto/cred.html">
279- Twisted Cred
280- </a>
281- </li>
282- </ul>
283-
284- <h2>Servers</h2>
285-
286- <p>
287- <code class="API" base="epsilon.ampauth">CredAMPServerFactory</code>
288- is a factory for the <code class="API"
289- base="epsilon.ampauth">CredReceiver</code> protocol, an
290- <code>AMP</code> subclass which implements responders for commands
291- which allow a client to prove their identity. It uses a
292- <code>Portal</code> to handle these commands and retrieve an <code
293- class="API" base="twisted.protocols.amp">IBoxReceiver</code> which
294- will be used to handle all further AMP boxes it receives.
295- </p>
296-
297- <a href="listings/amp/auth_server.py" class="py-listing">
298- AMP server with authentication
299- </a>
300-
301- <p>
302- <code>Add</code> and <code>Adder</code> together define a simple AMP
303- protocol for adding two integers together. <code>AdditionRealm</code>
304- provides the necessary integration between this AMP protocol and Cred,
305- creating new <code>Adder</code> instances whenever an
306- <code>IBoxReceiver</code> is requested - which will be whenever a client
307- attempts to authenticate itself to the server.
308- </p>
309-
310- <h2>Clients</h2>
311-
312- <p>
313- AMP clients can authenticate with an AMP server using <code class="API"
314- base="epsilon.ampauth">login</code>. <code>login</code> takes a
315- connected AMP instance and a credentials object as arguments and returns
316- a <code>Deferred</code> which fires when authentication finishes.
317- </p>
318-
319- <a href="listings/amp/auth_client.py" class="py-listing">
320- Authenticating AMP client
321- </a>
322-
323- <p>
324- The TCP connection is set up as usual, and the <code>Add</code> command
325- is also issued in the usual way. The only change from a normal AMP
326- client is the use of <code>login</code> after a connection has been set
327- up but before any commands are issued.
328- </p>
329-
330- <h2>One-Time Pad Authentication</h2>
331-
332- <p>
333- <code class="API">epsilon.ampauth</code> includes an <code class="API"
334- base="twisted.cred.checkers">CredentialsChecker</code> for validating
335- one-time pads: <code class="API"
336- base="epsilon.ampauth">OneTimePadChecker</code>. If this checker is
337- registered with the portal, clients may use the <code class="API"
338- base="epsilon.ampauth">OTPLogin</code> command to authenticate.
339- </p>
340-
341- <a href="listings/amp/amp_auth_server.py" class="py-listing">
342- AMP server with OTP authentication
343- </a>
344-
345- <p></p>
346-
347- <a href="listings/amp/amp_auth_client.py" class="py-listing">
348- OTP-authenticating AMP client
349- </a>
350- </body>
351-</html>
352
353=== removed file 'Epsilon/doc/amp-routes.xhtml'
354--- Epsilon/doc/amp-routes.xhtml 2008-08-29 16:02:56 +0000
355+++ Epsilon/doc/amp-routes.xhtml 1970-01-01 00:00:00 +0000
356@@ -1,180 +0,0 @@
357-<?xml version="1.0"?>
358-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
359- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
360-
361-<html xmlns="http://www.w3.org/1999/xhtml">
362- <head>
363- <title>AMP Routes</title>
364- </head>
365- <body>
366- <h1>AMP Routes</h1>
367-
368- <h2>Overview</h2>
369-
370- <p>
371- Normally, an AMP connection is between two <code class="API"
372- base="twisted.protocols.amp">AMP</code> instances; each instance receives
373- all AMP boxes sent by the other side and handles them by interpreting
374- them as commands, responses to commands, or in some other way. This
375- typically means that the logic for handling boxes on each side of the
376- connection is completely defined by a single object. Sometimes it is
377- useful to allow multiple objects, perhaps of different types, to
378- participate in defining this logic.
379- </p>
380-
381- <p>
382- <code>epsilon.amprouter</code> implements utilities which allow an
383- arbitrary number of objects, providers of <code>IBoxReceiver</code> (for
384- example, instances of <code>AMP</code>), to define how received AMP boxes
385- are interpreted. This is useful to multiplex unrelated <code>AMP</code>
386- instances over a single TCP connection, to split up a single AMP protocol
387- into multiple simpler protocols, and for many other purposes.
388- </p>
389-
390- <p>
391- Readers should familiarize themselves with the following concepts in
392- order to understand all sections of this document:
393- </p>
394-
395- <ul>
396- <li>
397- Twisted <a
398- href="http://twistedmatrix.com/projects/core/documentation/howto/clients.html">TCP
399- clients</a> and <a
400- href="http://twistedmatrix.com/projects/core/documentation/howto/servers.html">TCP
401- servers</a>
402- </li>
403- <li>
404- <a href="http://twistedmatrix.com/projects/core/documentation/howto/defer.html">
405- Using Deferreds
406- </a>
407- </li>
408- <li>
409- Twisted <code class="API" base="twisted.protocols.amp">AMP</code>
410- </li>
411- </ul>
412-
413- <h2>Routers</h2>
414-
415- <p>
416- When working with routes, the object primarily of interest will be a <a
417- class="API" base="epsilon.amprouter">Router</a> instance. Each AMP
418- client and server will have a <code>Router</code> instance which they can
419- use to create new routes. They will use its <code class="API"
420- base="epsilon.amprouter">Router.bindRoute</code> method to set up
421- whatever routes they require.
422- </p>
423-
424- <h2>Servers</h2>
425-
426- <p>
427- <code>epsilon.amprouter</code> does not define a command for creating new
428- routes because different applications have different requirements for how
429- new routes are set up. An application may want to negotiate about the
430- <code>IBoxReceiver</code> implementation which is associated with a
431- route, it may want to supply initial arguments to that object, it may
432- want to do version negotiation, and so on. The first thing an
433- application using routes must do, then, is to define a way to create new
434- routes. Consider the following example which allows routes to be created
435- with a <code>NewRoute</code> AMP command and associates them with a
436- parameterized <code>IBoxReceiver</code> implementation.
437- </p>
438-
439- <a href="listings/amp/route_setup.py" class="py-listing">
440- Creation of new routes
441- </a>
442-
443- <p>
444- <code>AMPRouteServerFactory.buildProtocol</code> creates new
445- <code>RoutingAMP</code> instances, each with a new <code>Router</code>.
446- The <code>Router</code> instance will become the <code>RoutingAMP</code>
447- instance's <code>boxReceiver</code> attribute. This is important for two
448- reasons. First, it allows the router to work by causing all AMP boxes
449- received from the connection to be delivered to the router to be
450- dispatched appropriately. Second, it gives the <code>RoutingAMP</code>
451- instance a reference to the <code>Router</code> instance; this is
452- necessary so that new routes can be created.
453- </p>
454-
455- <p>
456- After creating the <code>Router</code> and <code>RoutingAMP</code>,
457- <code>buildProtocol</code> also sets up the <code>RoutingAMP</code>
458- instance to be the default receiver by binding it to the
459- <code>None</code>. All AMP boxes without routing information will be
460- delivered to the default receiver. This is important because it allows
461- the <code>NewRoute</code> command to be handled by the
462- <code>RoutingAMP</code> instance.
463- </p>
464-
465- <p>
466- <code>RoutingAMP</code>'s <code>NewRoute</code> responder uses
467- <code>self.boxReceiver</code>, the <code>Router</code> instance provided
468- by the factory, to <em>bind</em> the return value of
469- <code>self.factory.routeProtocol()</code> to a new route. Then, it
470- connects the route to the identifier specified in the
471- <code>NewRoute</code> command. Finally, it returns the identifier of the
472- route it has just created. Once this has happened, the route is
473- completely set up on the server.
474- </p>
475-
476- <p>
477- Finally, the <code>connect</code> function wraps up the necessary calls
478- to routing methods and a use of the <code>NewRoute</code> command to form
479- the client side of the setup.
480- </p>
481-
482- <p>
483- First, let's look at an example of using <code>AMPRouteServerFactory</code> and
484- <code>RoutingAMP</code> to run a server.
485- </p>
486-
487- <a href="listings/amp/route_server.py" class="py-listing">
488- Routed counters server
489- </a>
490-
491- <p>
492- In this example, a simple counting protocol is hooked up to the server.
493- Each route which is created is associated with a new instance of this
494- protocol. The protocol does just one simple thing, it keeps track of how
495- many times the <code>Count</code> command is issued to it and returns
496- this value in the response to that command.
497- </p>
498-
499- <p>
500- Next we'll look at how a client can connect to this server, create new
501- routes, and issue commands over them.
502- </p>
503-
504- <h2>Clients</h2>
505-
506- <p>
507- Just as servers must, clients must first set up a route before they can
508- send boxes over it. A client uses the same methods as the server,
509- <code>Router.bindRoute</code> and <code>Route.connectTo</code>, to set up
510- a new route. Here's an example which makes one TCP connection to an AMP
511- server, sets up three routes, and then issues multiple commands over each
512- of them.
513- </p>
514-
515- <a href="listings/amp/route_client.py" class="py-listing">
516- Routed counters client
517- </a>
518-
519- <p>
520- Note first how <code>main</code> creates an <code>AMP</code> with a
521- <code>Router</code> instance. Note also how <code>makeRoutes</code>
522- binds and connects the protocol to the default route. This mirrors the
523- route setup which was done on the server and is necessary for the same
524- reasons.
525- </p>
526-
527- <p>
528- Once an AMP connection is set up and the default route is bound,
529- <code>makeRoutes</code> uses the previously defined <code>connect</code>
530- function to establish three new routes. Each route is associated with a
531- <code>CountClient</code> instance which will issue several count commands
532- and report the results. The results of each command are tracked so that
533- when they have all been received the client can exit.
534- </p>
535- </body>
536-</html>
537
538=== removed file 'Epsilon/doc/index.xhtml'
539--- Epsilon/doc/index.xhtml 2008-08-29 16:02:56 +0000
540+++ Epsilon/doc/index.xhtml 1970-01-01 00:00:00 +0000
541@@ -1,21 +0,0 @@
542-<?xml version="1.0"?>
543-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
544- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
545-
546-<html xmlns="http://www.w3.org/1999/xhtml">
547- <head>
548- <title>Index</title>
549- </head>
550- <body>
551- <h1>Index</h1>
552-
553- <ul class="toc">
554- <li>
555- <a href="amp-auth.xhtml">AMP Authentication</a>
556- </li>
557- <li>
558- <a href="amp-routes.xhtml">AMP Routes</a>
559- </li>
560- </ul>
561- </body>
562-</html>
563
564=== removed directory 'Epsilon/doc/listings'
565=== removed directory 'Epsilon/doc/listings/amp'
566=== removed file 'Epsilon/doc/listings/amp/amp_auth_client.py'
567--- Epsilon/doc/listings/amp/amp_auth_client.py 2008-11-27 07:37:39 +0000
568+++ Epsilon/doc/listings/amp/amp_auth_client.py 1970-01-01 00:00:00 +0000
569@@ -1,42 +0,0 @@
570-# Copyright (c) 2008 Divmod. See LICENSE for details.
571-
572-"""
573-An AMP client which connects to and authenticates with an AMP server using OTP,
574-then issues a command.
575-"""
576-
577-from twisted.internet.protocol import ClientCreator
578-from twisted.cred.credentials import UsernamePassword
579-from twisted.protocols.amp import AMP
580-
581-from epsilon.react import react
582-from epsilon.ampauth import OTPLogin
583-
584-from auth_server import Add
585-
586-
587-def add(proto):
588- return proto.callRemote(Add, left=17, right=33)
589-
590-
591-def display(result):
592- print result
593-
594-
595-def otpLogin(client):
596- client.callRemote(OTPLogin, pad='pad')
597- return client
598-
599-
600-def main(reactor):
601- cc = ClientCreator(reactor, AMP)
602- d = cc.connectTCP('localhost', 7805)
603- d.addCallback(otpLogin)
604- d.addCallback(add)
605- d.addCallback(display)
606- return d
607-
608-
609-if __name__ == '__main__':
610- from twisted.internet import reactor
611- react(reactor, main, [])
612
613=== removed file 'Epsilon/doc/listings/amp/amp_auth_server.py'
614--- Epsilon/doc/listings/amp/amp_auth_server.py 2008-11-27 07:37:39 +0000
615+++ Epsilon/doc/listings/amp/amp_auth_server.py 1970-01-01 00:00:00 +0000
616@@ -1,76 +0,0 @@
617-# Copyright (c) 2008 Divmod. See LICENSE for details.
618-
619-"""
620-An AMP server which requires authentication of its clients before exposing an
621-addition command.
622-"""
623-
624-from sys import stdout
625-
626-from twisted.python.log import startLogging, msg
627-from twisted.internet import reactor
628-from twisted.cred.portal import Portal
629-from twisted.protocols.amp import IBoxReceiver, Command, Integer, AMP
630-
631-from epsilon.ampauth import CredAMPServerFactory, OneTimePadChecker
632-
633-
634-class Add(Command):
635- """
636- An example of an application-defined command which should be made available
637- to clients after they successfully authenticate.
638- """
639- arguments = [("left", Integer()),
640- ("right", Integer())]
641-
642- response = [("sum", Integer())]
643-
644-
645-
646-class Adder(AMP):
647- """
648- An example of an application-defined AMP protocol, the responders defined
649- by which should only be available to clients after they have successfully
650- authenticated.
651- """
652- def __init__(self, avatarId):
653- AMP.__init__(self)
654- self.avatarId = avatarId
655-
656-
657- @Add.responder
658- def add(self, left, right):
659- msg("Adding %d to %d for %s" % (left, right, self.avatarId))
660- return {'sum': left + right}
661-
662-
663-
664-class AdditionRealm(object):
665- """
666- An example of an application-defined realm.
667- """
668- def requestAvatar(self, avatarId, mind, *interfaces):
669- """
670- Create Adder avatars for any IBoxReceiver request.
671- """
672- if IBoxReceiver in interfaces:
673- return (IBoxReceiver, Adder(avatarId), lambda: None)
674- raise NotImplementedError()
675-
676-
677-
678-def main():
679- """
680- Start the AMP server and the reactor.
681- """
682- startLogging(stdout)
683- checker = OneTimePadChecker({'pad': 0})
684- realm = AdditionRealm()
685- factory = CredAMPServerFactory(Portal(realm, [checker]))
686- reactor.listenTCP(7805, factory)
687- reactor.run()
688-
689-
690-if __name__ == '__main__':
691- main()
692-
693
694=== removed file 'Epsilon/doc/listings/amp/auth_client.py'
695--- Epsilon/doc/listings/amp/auth_client.py 2008-08-29 16:02:56 +0000
696+++ Epsilon/doc/listings/amp/auth_client.py 1970-01-01 00:00:00 +0000
697@@ -1,37 +0,0 @@
698-# Copyright (c) 2008 Divmod. See LICENSE for details.
699-
700-"""
701-An AMP client which connects to and authenticates with an AMP server, then
702-issues a command.
703-"""
704-
705-from twisted.internet.protocol import ClientCreator
706-from twisted.cred.credentials import UsernamePassword
707-from twisted.protocols.amp import AMP
708-
709-from epsilon.react import react
710-from epsilon.ampauth import login
711-
712-from auth_server import Add
713-
714-
715-def add(proto):
716- return proto.callRemote(Add, left=17, right=33)
717-
718-
719-def display(result):
720- print result
721-
722-
723-def main(reactor):
724- cc = ClientCreator(reactor, AMP)
725- d = cc.connectTCP('localhost', 7805)
726- d.addCallback(login, UsernamePassword("testuser", "examplepass"))
727- d.addCallback(add)
728- d.addCallback(display)
729- return d
730-
731-
732-if __name__ == '__main__':
733- from twisted.internet import reactor
734- react(reactor, main, [])
735
736=== removed file 'Epsilon/doc/listings/amp/auth_server.py'
737--- Epsilon/doc/listings/amp/auth_server.py 2008-11-05 18:50:57 +0000
738+++ Epsilon/doc/listings/amp/auth_server.py 1970-01-01 00:00:00 +0000
739@@ -1,77 +0,0 @@
740-# Copyright (c) 2008 Divmod. See LICENSE for details.
741-
742-"""
743-An AMP server which requires authentication of its clients before exposing an
744-addition command.
745-"""
746-
747-from sys import stdout
748-
749-from twisted.python.log import startLogging, msg
750-from twisted.internet import reactor
751-from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse
752-from twisted.cred.portal import Portal
753-from twisted.protocols.amp import IBoxReceiver, Command, Integer, AMP
754-
755-from epsilon.ampauth import CredAMPServerFactory
756-
757-
758-class Add(Command):
759- """
760- An example of an application-defined command which should be made available
761- to clients after they successfully authenticate.
762- """
763- arguments = [("left", Integer()),
764- ("right", Integer())]
765-
766- response = [("sum", Integer())]
767-
768-
769-
770-class Adder(AMP):
771- """
772- An example of an application-defined AMP protocol, the responders defined
773- by which should only be available to clients after they have successfully
774- authenticated.
775- """
776- def __init__(self, avatarId):
777- AMP.__init__(self)
778- self.avatarId = avatarId
779-
780-
781- @Add.responder
782- def add(self, left, right):
783- msg("Adding %d to %d for %s" % (left, right, self.avatarId))
784- return {'sum': left + right}
785-
786-
787-
788-class AdditionRealm(object):
789- """
790- An example of an application-defined realm.
791- """
792- def requestAvatar(self, avatarId, mind, *interfaces):
793- """
794- Create Adder avatars for any IBoxReceiver request.
795- """
796- if IBoxReceiver in interfaces:
797- return (IBoxReceiver, Adder(avatarId), lambda: None)
798- raise NotImplementedError()
799-
800-
801-
802-def main():
803- """
804- Start the AMP server and the reactor.
805- """
806- startLogging(stdout)
807- checker = InMemoryUsernamePasswordDatabaseDontUse()
808- checker.addUser("testuser", "examplepass")
809- realm = AdditionRealm()
810- factory = CredAMPServerFactory(Portal(realm, [checker]))
811- reactor.listenTCP(7805, factory)
812- reactor.run()
813-
814-
815-if __name__ == '__main__':
816- main()
817
818=== removed file 'Epsilon/doc/listings/amp/route_client.py'
819--- Epsilon/doc/listings/amp/route_client.py 2008-08-29 16:02:56 +0000
820+++ Epsilon/doc/listings/amp/route_client.py 1970-01-01 00:00:00 +0000
821@@ -1,60 +0,0 @@
822-# Copyright (c) 2008 Divmod. See LICENSE for details.
823-
824-import random
825-
826-from twisted.internet.defer import Deferred, gatherResults
827-from twisted.internet.protocol import ClientCreator
828-from twisted.protocols.amp import AMP
829-
830-from epsilon.react import react
831-from epsilon.amprouter import Router
832-
833-from route_setup import connect
834-from route_server import Count
835-
836-
837-def display(value, id):
838- print id, value
839-
840-
841-class CountClient(AMP):
842- def __init__(self, identifier):
843- AMP.__init__(self)
844- self.identifier = identifier
845- self.finished = Deferred()
846-
847- def startReceivingBoxes(self, sender):
848- AMP.startReceivingBoxes(self, sender)
849-
850- counts = []
851- for i in range(random.randrange(1, 5)):
852- d = self.callRemote(Count)
853- d.addCallback(display, self.identifier)
854- counts.append(d)
855- gatherResults(counts).chainDeferred(self.finished)
856-
857-
858-
859-def makeRoutes(proto, router):
860- router.bindRoute(proto, None).connectTo(None)
861-
862- finish = []
863- for i in range(3):
864- client = CountClient(i)
865- finish.append(connect(proto, router, client))
866- finish.append(client.finished)
867- return gatherResults(finish)
868-
869-
870-
871-def main(reactor):
872- router = Router()
873- cc = ClientCreator(reactor, AMP, router)
874- d = cc.connectTCP('localhost', 7805)
875- d.addCallback(makeRoutes, router)
876- return d
877-
878-
879-if __name__ == '__main__':
880- from twisted.internet import reactor
881- react(reactor, main, [])
882
883=== removed file 'Epsilon/doc/listings/amp/route_server.py'
884--- Epsilon/doc/listings/amp/route_server.py 2008-11-05 18:50:57 +0000
885+++ Epsilon/doc/listings/amp/route_server.py 1970-01-01 00:00:00 +0000
886@@ -1,37 +0,0 @@
887-# Copyright (c) 2008 Divmod. See LICENSE for details.
888-
889-from sys import stdout
890-
891-from twisted.python.log import startLogging
892-from twisted.protocols.amp import Integer, Command, AMP
893-from twisted.internet import reactor
894-
895-from route_setup import AMPRouteServerFactory
896-
897-
898-class Count(Command):
899- response = [('value', Integer())]
900-
901-
902-
903-class Counter(AMP):
904- _valueCounter = 0
905-
906- @Count.responder
907- def count(self):
908- self._valueCounter += 1
909- return {'value': self._valueCounter}
910-
911-
912-
913-def main():
914- startLogging(stdout)
915- serverFactory = AMPRouteServerFactory()
916- serverFactory.routeProtocol = Counter
917- reactor.listenTCP(7805, serverFactory)
918- reactor.run()
919-
920-
921-
922-if __name__ == '__main__':
923- main()
924
925=== removed file 'Epsilon/doc/listings/amp/route_setup.py'
926--- Epsilon/doc/listings/amp/route_setup.py 2008-08-29 16:02:56 +0000
927+++ Epsilon/doc/listings/amp/route_setup.py 1970-01-01 00:00:00 +0000
928@@ -1,49 +0,0 @@
929-# Copyright (c) 2008 Divmod. See LICENSE for details.
930-
931-import operator
932-
933-from twisted.internet.protocol import ServerFactory
934-from twisted.protocols.amp import Unicode, Command, AMP
935-
936-from epsilon.amprouter import Router
937-
938-
939-class NewRoute(Command):
940- arguments = [('name', Unicode())]
941- response = [('name', Unicode())]
942-
943-
944-
945-class RoutingAMP(AMP):
946- @NewRoute.responder
947- def newRoute(self, name):
948- route = self.boxReceiver.bindRoute(self.factory.routeProtocol())
949- route.connectTo(name)
950- return {'name': route.localRouteName}
951-
952-
953-
954-class AMPRouteServerFactory(ServerFactory):
955- protocol = RoutingAMP
956- routeProtocol = None
957-
958- def buildProtocol(self, addr):
959- router = Router()
960- proto = self.protocol(router)
961- proto.factory = self
962- default = router.bindRoute(proto, None)
963- default.connectTo(None)
964- return proto
965-
966-
967-
968-def connect(proto, router, receiver):
969- route = router.bindRoute(receiver)
970- d = proto.callRemote(NewRoute, name=route.localRouteName)
971- d.addCallback(operator.getitem, 'name')
972- d.addCallback(lambda name: route.connectTo(name))
973- def connectionFailed(err):
974- route.unbind()
975- return err
976- d.addErrback(connectionFailed)
977- return d
978
979=== removed file 'Epsilon/doc/stylesheet.css'
980--- Epsilon/doc/stylesheet.css 2008-08-25 16:20:43 +0000
981+++ Epsilon/doc/stylesheet.css 1970-01-01 00:00:00 +0000
982@@ -1,129 +0,0 @@
983-body
984-{
985- margin-left: 2em;
986- margin-right: 2em;
987- border: 0px;
988- padding: 0px;
989- font-family: sans-serif;
990-}
991-
992-pre
993-{
994- padding: 1em;
995- font-family: Neep Alt, Courier New, Courier;
996- font-size: 12pt;
997- border: thin black solid;
998-}
999-
1000-.python
1001-{
1002- background-color: #dddddd;
1003-}
1004-
1005-.py-listing, .html-listing, .listing
1006-{
1007- margin: 1ex;
1008- border: thin solid black;
1009- background-color: #eee;
1010-}
1011-
1012-.py-listing pre, .html-listing pre, .listing pre
1013-{
1014- margin: 0px;
1015- border: none;
1016- border-bottom: thin solid black;
1017-}
1018-
1019-.py-listing .python
1020-{
1021- margin-top: 0;
1022- margin-bottom: 0;
1023- border: none;
1024- border-bottom: thin solid black;
1025-}
1026-
1027-.py-src-comment
1028-{
1029- color: #1111CC
1030-}
1031-
1032-.py-src-keyword
1033-{
1034- color: #3333CC;
1035- font-weight: bold;
1036-}
1037-
1038-.py-src-parameter
1039-{
1040- color: #000066;
1041- font-weight: bold;
1042-}
1043-
1044-.py-src-identifier
1045-{
1046- color: #CC0000
1047-}
1048-
1049-.py-src-string
1050-{
1051- color: #115511
1052-}
1053-
1054-.py-src-endmarker
1055-{
1056- display: block; /* IE hack; prevents following line from being sucked into the py-listing box. */
1057-}
1058-
1059-hr
1060-{
1061- display: inline;
1062-}
1063-
1064-ul
1065-{
1066- padding: 0px;
1067- margin: 0px;
1068- margin-left: 1em;
1069- padding-left: 1em;
1070- border-left: 1em;
1071-}
1072-
1073-li
1074-{
1075- padding: 2px;
1076-}
1077-
1078-dt
1079-{
1080- font-weight: bold;
1081- margin-left: 1ex;
1082-}
1083-
1084-dd
1085-{
1086- margin-bottom: 1em;
1087-}
1088-
1089-div.note
1090-{
1091- background-color: #FFFFCC;
1092- margin-top: 1ex;
1093- margin-left: 5%;
1094- margin-right: 5%;
1095- padding-top: 1ex;
1096- padding-left: 5%;
1097- padding-right: 5%;
1098- border: thin black solid;
1099-}
1100-
1101-.caption
1102-{
1103- text-align: center;
1104- padding-top: 0.5em;
1105- padding-bottom: 0.5em;
1106-}
1107-
1108-.filename
1109-{
1110- font-style: italic;
1111-}
1112
1113=== removed file 'Epsilon/doc/template.tpl'
1114--- Epsilon/doc/template.tpl 2008-08-25 16:20:43 +0000
1115+++ Epsilon/doc/template.tpl 1970-01-01 00:00:00 +0000
1116@@ -1,23 +0,0 @@
1117-<?xml version="1.0"?>
1118-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
1119- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1120-
1121-<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
1122- <head>
1123- <title>
1124- Epsilon:
1125- </title>
1126- <link type="text/css" rel="stylesheet" href="stylesheet.css" />
1127- </head>
1128-
1129- <body bgcolor="white">
1130- <h1 class="title"></h1>
1131- <div class="toc"></div>
1132- <div class="body">
1133-
1134- </div>
1135-
1136- <p><a href="index.html">Index</a></p>
1137- <span class="version">Version: </span>
1138- </body>
1139-</html>
1140
1141=== removed directory 'Epsilon/epsilon'
1142=== removed file 'Epsilon/epsilon/__init__.py'
1143--- Epsilon/epsilon/__init__.py 2014-01-12 10:53:44 +0000
1144+++ Epsilon/epsilon/__init__.py 1970-01-01 00:00:00 +0000
1145@@ -1,8 +0,0 @@
1146-# -*- test-case-name: epsilon.test -*-
1147-from epsilon._version import __version__
1148-from twisted.python import versions
1149-
1150-def asTwistedVersion(packageName, versionString):
1151- return versions.Version(packageName, *map(int, versionString.split(".")))
1152-
1153-version = asTwistedVersion("epsilon", __version__)
1154
1155=== removed file 'Epsilon/epsilon/_version.py'
1156--- Epsilon/epsilon/_version.py 2014-01-15 10:21:17 +0000
1157+++ Epsilon/epsilon/_version.py 1970-01-01 00:00:00 +0000
1158@@ -1,1 +0,0 @@
1159-__version__ = "0.7.0"
1160
1161=== removed file 'Epsilon/epsilon/ampauth.py'
1162--- Epsilon/epsilon/ampauth.py 2011-07-18 12:37:13 +0000
1163+++ Epsilon/epsilon/ampauth.py 1970-01-01 00:00:00 +0000
1164@@ -1,312 +0,0 @@
1165-# -*- test-case-name: epsilon.test.test_ampauth -*-
1166-# Copyright (c) 2008 Divmod. See LICENSE for details.
1167-
1168-"""
1169-This module provides integration between L{AMP<twisted.protocols.amp.AMP>} and
1170-L{cred<twisted.cred>}.
1171-"""
1172-
1173-from hashlib import sha1
1174-
1175-from zope.interface import implements
1176-
1177-from twisted.python.randbytes import secureRandom
1178-from twisted.cred.error import UnauthorizedLogin
1179-from twisted.cred.credentials import IUsernameHashedPassword, IUsernamePassword
1180-from twisted.cred.checkers import ICredentialsChecker
1181-from twisted.protocols.amp import IBoxReceiver, String, Command, AMP
1182-from twisted.internet.protocol import ServerFactory
1183-
1184-from epsilon.iepsilon import IOneTimePad
1185-from epsilon.structlike import record
1186-
1187-__metaclass__ = type
1188-
1189-
1190-class UnhandledCredentials(Exception):
1191- """
1192- L{login} was passed a credentials object which did not provide a recognized
1193- credentials interface.
1194- """
1195-
1196-
1197-
1198-class OTPLogin(Command):
1199- """
1200- Command to initiate a login attempt where a one-time pad is to be used in
1201- place of username/password credentials.
1202- """
1203- arguments = [('pad', String())]
1204-
1205- errors = {
1206- # Invalid username or password
1207- UnauthorizedLogin: 'UNAUTHORIZED_LOGIN',
1208- # No IBoxReceiver avatar
1209- NotImplementedError: 'NOT_IMPLEMENTED_ERROR'}
1210-
1211-
1212-
1213-class PasswordLogin(Command):
1214- """
1215- Command to initiate a username/password-based login attempt. The response
1216- to this command is a challenge which must be responded to based on the
1217- correct password associated with the username given to this command.
1218- """
1219- arguments = [('username', String())]
1220- response = [('challenge', String())]
1221-
1222-
1223-
1224-def _calcResponse(challenge, nonce, password):
1225- """
1226- Compute the response to the given challenge.
1227-
1228- @type challenge: C{str}
1229- @param challenge: An arbitrary byte string, probably received in response
1230- to (or generated for) the L{PasswordLogin} command.
1231-
1232- @type nonce: C{str}
1233- @param nonce: An arbitrary byte string, generated by the client to include
1234- in the hash to avoid making the client an oracle.
1235-
1236- @type password: C{str}
1237- @param password: The known correct password for the account being
1238- authenticated.
1239-
1240- @rtype: C{str}
1241- @return: A hash constructed from the three parameters.
1242- """
1243- return sha1('%s %s %s' % (challenge, nonce, password)).digest()
1244-
1245-
1246-
1247-class PasswordChallengeResponse(Command):
1248- """
1249- Command to respond to a challenge issued in the response to a
1250- L{PasswordLogin} command and complete a username/password-based login
1251- attempt.
1252-
1253- @param cnonce: A randomly generated string used only in this response.
1254- @param response: The SHA-1 hash of the challenge, cnonce, and password.
1255- """
1256- arguments = [('cnonce', String()),
1257- ('response', String())]
1258-
1259- errors = {
1260- # Invalid username or password
1261- UnauthorizedLogin: 'UNAUTHORIZED_LOGIN',
1262- # No IBoxReceiver avatar
1263- NotImplementedError: 'NOT_IMPLEMENTED_ERROR'}
1264-
1265- @classmethod
1266- def determineFrom(cls, challenge, password):
1267- """
1268- Create a nonce and use it, along with the given challenge and password,
1269- to generate the parameters for a response.
1270-
1271- @return: A C{dict} suitable to be used as the keyword arguments when
1272- calling this command.
1273- """
1274- nonce = secureRandom(16)
1275- response = _calcResponse(challenge, nonce, password)
1276- return dict(cnonce=nonce, response=response)
1277-
1278-
1279-
1280-class _AMPUsernamePassword(record('username challenge nonce response')):
1281- """
1282- L{IUsernameHashedPassword} implementation used by L{PasswordLogin} and
1283- related commands.
1284- """
1285- implements(IUsernameHashedPassword)
1286-
1287- def checkPassword(self, password):
1288- """
1289- Check the given plaintext password against the response in this
1290- credentials object.
1291-
1292- @type password: C{str}
1293- @param password: The known correct password associated with
1294- C{self.username}.
1295-
1296- @return: A C{bool}, C{True} if this credentials object agrees with the
1297- given password, C{False} otherwise.
1298- """
1299- if isinstance(password, unicode):
1300- password = password.encode('utf-8')
1301- correctResponse = _calcResponse(self.challenge, self.nonce, password)
1302- return correctResponse == self.response
1303-
1304-
1305-
1306-class _AMPOneTimePad(record('padValue')):
1307- """
1308- L{IOneTimePad} implementation used by L{OTPLogin}.
1309-
1310- @ivar padValue: The value of the one-time pad.
1311- @type padValue: C{str}
1312- """
1313- implements(IOneTimePad)
1314-
1315-
1316-
1317-class CredReceiver(AMP):
1318- """
1319- Integration between AMP and L{twisted.cred}.
1320-
1321- This implementation is limited to a single authentication per connection.
1322- A future implementation may use I{routes} to allow multiple authentications
1323- over the same connection.
1324-
1325- @ivar portal: The L{Portal} against which login will be performed. This is
1326- expected to be set by the factory which creates instances of this
1327- class.
1328-
1329- @ivar logout: C{None} or a no-argument callable. This is set to the logout
1330- object returned by L{Portal.login} and is set while an avatar is logged
1331- in.
1332-
1333- @ivar challenge: The C{str} which was sent as a challenge in response to
1334- the L{PasswordLogin} command. If multiple L{PasswordLogin} commands
1335- are sent, this is the challenge sent in response to the most recent of
1336- them. It is not set before L{PasswordLogin} is received.
1337-
1338- @ivar username: The C{str} which was received for the I{username} parameter
1339- of the L{PasswordLogin} command. The lifetime is the same as that of
1340- the I{challenge} attribute.
1341- """
1342- portal = None
1343- logout = None
1344-
1345- @PasswordLogin.responder
1346- def passwordLogin(self, username):
1347- """
1348- Generate a new challenge for the given username.
1349- """
1350- self.challenge = secureRandom(16)
1351- self.username = username
1352- return {'challenge': self.challenge}
1353-
1354-
1355- def _login(self, credentials):
1356- """
1357- Actually login to our portal with the given credentials.
1358- """
1359- d = self.portal.login(credentials, None, IBoxReceiver)
1360- def cbLoggedIn((interface, avatar, logout)):
1361- self.logout = logout
1362- self.boxReceiver = avatar
1363- self.boxReceiver.startReceivingBoxes(self.boxSender)
1364- return {}
1365- d.addCallback(cbLoggedIn)
1366- return d
1367-
1368-
1369- @PasswordChallengeResponse.responder
1370- def passwordChallengeResponse(self, cnonce, response):
1371- """
1372- Verify the response to a challenge.
1373- """
1374- return self._login(_AMPUsernamePassword(
1375- self.username, self.challenge, cnonce, response))
1376-
1377-
1378- @OTPLogin.responder
1379- def otpLogin(self, pad):
1380- """
1381- Verify the given pad.
1382- """
1383- return self._login(_AMPOneTimePad(pad))
1384-
1385-
1386- def connectionLost(self, reason):
1387- """
1388- If a login has happened, perform a logout.
1389- """
1390- AMP.connectionLost(self, reason)
1391- if self.logout is not None:
1392- self.logout()
1393- self.boxReceiver = self.logout = None
1394-
1395-
1396-
1397-class OneTimePadChecker(record('pads')):
1398- """
1399- Checker which validates one-time pads.
1400-
1401- @ivar pads: Mapping between valid one-time pads and avatar IDs.
1402- @type pads: C{dict}
1403- """
1404- implements(ICredentialsChecker)
1405-
1406- credentialInterfaces = (IOneTimePad,)
1407-
1408- # ICredentialsChecker
1409- def requestAvatarId(self, credentials):
1410- if credentials.padValue in self.pads:
1411- return self.pads.pop(credentials.padValue)
1412- raise UnauthorizedLogin('Unknown one-time pad')
1413-
1414-
1415-
1416-class CredAMPServerFactory(ServerFactory):
1417- """
1418- Server factory useful for creating L{CredReceiver} instances.
1419-
1420- This factory takes care of associating a L{Portal} with L{CredReceiver}
1421- instances it creates.
1422-
1423- @ivar portal: The portal which will be used by L{CredReceiver} instances
1424- created by this factory.
1425- """
1426- protocol = CredReceiver
1427-
1428- def __init__(self, portal):
1429- self.portal = portal
1430-
1431-
1432- def buildProtocol(self, addr):
1433- proto = ServerFactory.buildProtocol(self, addr)
1434- proto.portal = self.portal
1435- return proto
1436-
1437-
1438-
1439-def login(client, credentials):
1440- """
1441- Authenticate using the given L{AMP} instance. The protocol must be
1442- connected to a server with responders for L{PasswordLogin} and
1443- L{PasswordChallengeResponse}.
1444-
1445- @param client: A connected L{AMP} instance which will be used to issue
1446- authentication commands.
1447-
1448- @param credentials: An object providing L{IUsernamePassword} which will
1449- be used to authenticate this connection to the server.
1450-
1451- @return: A L{Deferred} which fires when authentication has succeeded or
1452- which fails with L{UnauthorizedLogin} if the server rejects the
1453- authentication attempt.
1454- """
1455- if not IUsernamePassword.providedBy(credentials):
1456- raise UnhandledCredentials()
1457- d = client.callRemote(
1458- PasswordLogin, username=credentials.username)
1459- def cbChallenge(response):
1460- args = PasswordChallengeResponse.determineFrom(
1461- response['challenge'], credentials.password)
1462- d = client.callRemote(PasswordChallengeResponse, **args)
1463- return d.addCallback(lambda ignored: client)
1464- d.addCallback(cbChallenge)
1465- return d
1466-
1467-
1468-
1469-__all__ = [
1470- 'UnhandledCredentials',
1471-
1472- 'OTPLogin', 'OneTimePadChecker',
1473-
1474- 'PasswordLogin', 'PasswordChallengeResponse', 'CredReceiver',
1475-
1476- 'CredAMPServerFactory', 'login']
1477
1478=== removed file 'Epsilon/epsilon/amprouter.py'
1479--- Epsilon/epsilon/amprouter.py 2008-08-29 16:02:56 +0000
1480+++ Epsilon/epsilon/amprouter.py 1970-01-01 00:00:00 +0000
1481@@ -1,205 +0,0 @@
1482-# -*- test-case-name: epsilon.test.test_amprouter -*-
1483-# Copyright (c) 2008 Divmod. See LICENSE for details.
1484-
1485-"""
1486-This module provides an implementation of I{Routes}, a system for multiplexing
1487-multiple L{IBoxReceiver}/I{IBoxSender} pairs over a single L{AMP} connection.
1488-"""
1489-
1490-from itertools import count
1491-
1492-from zope.interface import implements
1493-
1494-from twisted.protocols.amp import IBoxReceiver, IBoxSender
1495-
1496-from epsilon.structlike import record
1497-
1498-__metaclass__ = type
1499-
1500-_ROUTE = '_route'
1501-_unspecified = object()
1502-
1503-
1504-class RouteNotConnected(Exception):
1505- """
1506- An attempt was made to send AMP boxes through a L{Route} which is not yet
1507- connected to anything.
1508- """
1509-
1510-
1511-
1512-class Route(record('router receiver localRouteName remoteRouteName',
1513- remoteRouteName=_unspecified)):
1514- """
1515- Wrap up a route name and a box sender to transparently add the route name
1516- to boxes sent through this box sender.
1517-
1518- @ivar router: The L{Router} which created this route. This will be used
1519- for route tear down and for its L{IBoxSender}, to send boxes.
1520-
1521- @ivar receiver: The receiver which will be started with this object as its
1522- sender.
1523-
1524- @type localRouteName: C{unicode}
1525- @ivar localRouteName: The name of this route as known by the other side of
1526- the AMP connection. AMP boxes with this route are expected to be
1527- routed to this object.
1528-
1529- @type remoteRouteName: C{unicode} or L{NoneType}
1530- @ivar remoteRouteName: The name of the route which will be added to all
1531- boxes sent to this sender. If C{None}, no route will be added.
1532- """
1533- implements(IBoxSender)
1534-
1535- def connectTo(self, remoteRouteName):
1536- """
1537- Set the name of the route which will be added to outgoing boxes.
1538- """
1539- self.remoteRouteName = remoteRouteName
1540- # This route must not be started before its router is started. If
1541- # sender is None, then the router is not started. When the router is
1542- # started, it will start this route.
1543- if self.router._sender is not None:
1544- self.start()
1545-
1546-
1547- def unbind(self):
1548- """
1549- Remove the association between this route and its router.
1550- """
1551- del self.router._routes[self.localRouteName]
1552-
1553-
1554- def start(self):
1555- """
1556- Associate this object with a receiver as its L{IBoxSender}.
1557- """
1558- self.receiver.startReceivingBoxes(self)
1559-
1560-
1561- def stop(self, reason):
1562- """
1563- Shut down the underlying receiver.
1564- """
1565- self.receiver.stopReceivingBoxes(reason)
1566-
1567-
1568- def sendBox(self, box):
1569- """
1570- Add the route and send the box.
1571- """
1572- if self.remoteRouteName is _unspecified:
1573- raise RouteNotConnected()
1574- if self.remoteRouteName is not None:
1575- box[_ROUTE] = self.remoteRouteName.encode('ascii')
1576- self.router._sender.sendBox(box)
1577-
1578-
1579- def unhandledError(self, failure):
1580- """
1581- Pass failures through to the wrapped L{IBoxSender} without
1582- modification.
1583- """
1584- self.router._sender.unhandledError(failure)
1585-
1586-
1587-
1588-class Router:
1589- """
1590- An L{IBoxReceiver} implementation which demultiplexes boxes from an AMP
1591- connection being used with zero, one, or more routes.
1592-
1593- @ivar _sender: An L{IBoxSender} provider which is used to allow
1594- L{IBoxReceiver}s added to this router to send boxes.
1595-
1596- @ivar _unstarted: A C{dict} similar to C{_routes} set before
1597- C{startReceivingBoxes} is called and containing all routes which have
1598- been added but not yet started. These are started and moved to the
1599- C{_routes} dict when the router is started.
1600-
1601- @ivar _routes: A C{dict} mapping local route identifiers to
1602- L{IBoxReceivers} associated with them. This is only initialized after
1603- C{startReceivingBoxes} is called.
1604-
1605- @ivar _routeCounter: A L{itertools.count} instance used to generate unique
1606- identifiers for routes in this router.
1607- """
1608- implements(IBoxReceiver)
1609-
1610- _routes = None
1611- _sender = None
1612-
1613- def __init__(self):
1614- self._routeCounter = count()
1615- self._unstarted = {}
1616-
1617-
1618- def createRouteIdentifier(self):
1619- """
1620- Return a route identifier which is not yet associated with a route on
1621- this dispatcher.
1622-
1623- @rtype: C{unicode}
1624- """
1625- return unicode(self._routeCounter.next())
1626-
1627-
1628- def bindRoute(self, receiver, routeName=_unspecified):
1629- """
1630- Create a new route to associate the given route name with the given
1631- receiver.
1632-
1633- @type routeName: C{unicode} or L{NoneType}
1634- @param routeName: The identifier for the newly created route. If
1635- C{None}, boxes with no route in them will be delivered to this
1636- receiver.
1637-
1638- @rtype: L{Route}
1639- """
1640- if routeName is _unspecified:
1641- routeName = self.createRouteIdentifier()
1642- # self._sender may yet be None; if so, this route goes into _unstarted
1643- # and will have its sender set correctly in startReceivingBoxes below.
1644- route = Route(self, receiver, routeName)
1645- mapping = self._routes
1646- if mapping is None:
1647- mapping = self._unstarted
1648- mapping[routeName] = route
1649- return route
1650-
1651-
1652- def startReceivingBoxes(self, sender):
1653- """
1654- Initialize route tracking objects.
1655- """
1656- self._sender = sender
1657- for routeName, route in self._unstarted.iteritems():
1658- # Any route which has been bound but which does not yet have a
1659- # remote route name should not yet be started. These will be
1660- # started in Route.connectTo.
1661- if route.remoteRouteName is not _unspecified:
1662- route.start()
1663- self._routes = self._unstarted
1664- self._unstarted = None
1665-
1666-
1667- def ampBoxReceived(self, box):
1668- """
1669- Dispatch the given box to the L{IBoxReceiver} associated with the route
1670- indicated by the box, or handle it directly if there is no route.
1671- """
1672- route = box.pop(_ROUTE, None)
1673- self._routes[route].receiver.ampBoxReceived(box)
1674-
1675-
1676- def stopReceivingBoxes(self, reason):
1677- """
1678- Stop all the L{IBoxReceiver}s which have been added to this router.
1679- """
1680- for routeName, route in self._routes.iteritems():
1681- route.stop(reason)
1682- self._routes = None
1683-
1684-
1685-
1686-__all__ = ['Router', 'Route']
1687
1688=== removed file 'Epsilon/epsilon/asplode.py'
1689--- Epsilon/epsilon/asplode.py 2005-12-02 20:28:41 +0000
1690+++ Epsilon/epsilon/asplode.py 1970-01-01 00:00:00 +0000
1691@@ -1,34 +0,0 @@
1692-
1693-import sys, os
1694-from datetime import date
1695-
1696-def status(x):
1697- sys.stderr.write(x+'\n')
1698- sys.stderr.flush()
1699-
1700-def splode(linerator, proj, capproj):
1701- current = None
1702- for line in linerator:
1703- line = line.replace('_project_', proj)
1704- line = line.replace('_Project_', capproj)
1705- line = line.replace('_date_', str(date.today()))
1706- ls = line.split("###file:")
1707- if len(ls) > 1:
1708- fname = ls[1].strip()
1709- if current is not None:
1710- current.close()
1711- try:
1712- os.makedirs(os.path.dirname(fname))
1713- except:
1714- pass
1715- current = file(fname, 'wb')
1716- status('Created: ' + fname)
1717- else:
1718- current.write(line)
1719- current.close()
1720-
1721-def main(argv):
1722- splode(sys.stdin.readlines(), 'zoop', 'Zoop')
1723-
1724-if __name__ == '__main__':
1725- main(sys.argv)
1726
1727=== removed file 'Epsilon/epsilon/caseless.py'
1728--- Epsilon/epsilon/caseless.py 2008-04-16 12:34:02 +0000
1729+++ Epsilon/epsilon/caseless.py 1970-01-01 00:00:00 +0000
1730@@ -1,135 +0,0 @@
1731-# -*- test-case-name: epsilon.test.test_caseless -*-
1732-"""
1733-Helpers for case-insensitive string handling.
1734-"""
1735-
1736-class Caseless(object):
1737- """
1738- Case-insensitive string wrapper type.
1739-
1740- This wrapper is intended for use with strings that have case-insensitive
1741- semantics, such as HTTP/MIME header values. It implements comparison-based
1742- operations case-insensitively, avoiding the need to manually call C{lower}
1743- where appropriate, or keep track of which strings are case-insensitive
1744- throughout various function calls.
1745-
1746- Example usage:
1747-
1748- >>> Caseless('Spam') == Caseless('spam')
1749- True
1750- >>> 'spam' in Caseless('Eggs and Spam')
1751- True
1752-
1753- >>> sorted(['FOO', 'bar'], key=Caseless)
1754- ['bar', 'FOO']
1755-
1756- >>> d = {Caseless('Content-type'): Caseless('Text/Plain')}
1757- >>> d[Caseless('Content-Type')].startswith('text/')
1758- True
1759-
1760- Note: String methods that return modified strings (such as
1761- C{decode}/C{encode}, C{join}, C{partition}, C{replace}, C{strip}/C{split})
1762- don't have an unambiguous return types with regards to case sensitivity, so
1763- they are not implemented by L{Caseless}. They should be accessed on the
1764- underlying cased string instead. (Excepted are methods like
1765- C{lower}/C{upper}, whose return case is unambiguous.)
1766-
1767- @ivar cased: the wrapped string-like object
1768- """
1769-
1770- def __init__(self, cased):
1771- if isinstance(cased, Caseless):
1772- cased = cased.cased
1773- self.cased = cased
1774-
1775-
1776- def __repr__(self):
1777- return '%s(%r)' % (type(self).__name__, self.cased)
1778-
1779-
1780- # Methods delegated to cased
1781-
1782- def __str__(self):
1783- return str(self.cased)
1784-
1785-
1786- def __unicode__(self):
1787- return unicode(self.cased)
1788-
1789-
1790- def __len__(self):
1791- return len(self.cased)
1792-
1793-
1794- def __getitem__(self, key):
1795- return self.cased[key]
1796-
1797-
1798- def __iter__(self):
1799- return iter(self.cased)
1800-
1801-
1802- def lower(self):
1803- return self.cased.lower()
1804-
1805-
1806- def upper(self):
1807- return self.cased.upper()
1808-
1809-
1810- def title(self):
1811- return self.cased.title()
1812-
1813-
1814- def swapcase(self):
1815- return self.cased.swapcase()
1816-
1817-
1818- # Methods delegated to lower()
1819-
1820- def __cmp__(self, other):
1821- return cmp(self.lower(), other.lower())
1822-
1823-
1824- def __hash__(self):
1825- return hash(self.lower())
1826-
1827-
1828- def __contains__(self, substring):
1829- return substring.lower() in self.lower()
1830-
1831-
1832- def startswith(self, prefix, *rest):
1833- if isinstance(prefix, tuple):
1834- lprefix = tuple(s.lower() for s in prefix)
1835- else:
1836- lprefix = prefix.lower()
1837- return self.lower().startswith(lprefix, *rest)
1838-
1839-
1840- def endswith(self, suffix, *rest):
1841- if isinstance(suffix, tuple):
1842- lsuffix = tuple(s.lower() for s in suffix)
1843- else:
1844- lsuffix = suffix.lower()
1845- return self.lower().endswith(lsuffix, *rest)
1846-
1847-
1848- def count(self, substring, *rest):
1849- return self.lower().count(substring.lower(), *rest)
1850-
1851-
1852- def find(self, substring, *rest):
1853- return self.lower().find(substring.lower(), *rest)
1854-
1855-
1856- def index(self, substring, *rest):
1857- return self.lower().index(substring.lower(), *rest)
1858-
1859-
1860- def rfind(self, substring, *rest):
1861- return self.lower().rfind(substring.lower(), *rest)
1862-
1863-
1864- def rindex(self, substring, *rest):
1865- return self.lower().rindex(substring.lower(), *rest)
1866
1867=== removed file 'Epsilon/epsilon/cooperator.py'
1868--- Epsilon/epsilon/cooperator.py 2009-05-22 13:21:37 +0000
1869+++ Epsilon/epsilon/cooperator.py 1970-01-01 00:00:00 +0000
1870@@ -1,32 +0,0 @@
1871-
1872-from twisted.application.service import Service
1873-from twisted.internet.task import SchedulerStopped, Cooperator, coiterate
1874-
1875-def iterateInReactor(i, delay=None):
1876- """
1877- Cooperatively iterate over the given iterator.
1878-
1879- @see: L{twisted.internet.task.coiterate}.
1880- """
1881- return coiterate(i)
1882-
1883-
1884-class SchedulingService(Service):
1885- """
1886- Simple L{IService} implementation.
1887- """
1888- def __init__(self):
1889- self.coop = Cooperator(started=False)
1890-
1891- def addIterator(self, iterator):
1892- return self.coop.coiterate(iterator)
1893-
1894- def startService(self):
1895- self.coop.start()
1896-
1897- def stopService(self):
1898- self.coop.stop()
1899-
1900-__all__ = [
1901- 'SchedulerStopped', 'Cooperator',
1902- 'SchedulingService', 'iterateInReactor']
1903
1904=== removed file 'Epsilon/epsilon/descriptor.py'
1905--- Epsilon/epsilon/descriptor.py 2008-02-08 14:07:46 +0000
1906+++ Epsilon/epsilon/descriptor.py 1970-01-01 00:00:00 +0000
1907@@ -1,147 +0,0 @@
1908-# -*- test-case-name: epsilon.test.test_descriptor -*-
1909-
1910-"""
1911-Provides an 'attribute' class for one-use descriptors.
1912-"""
1913-
1914-attribute = None
1915-
1916-class _MetaAttribute(type):
1917- def __new__(meta, name, bases, dict):
1918- # for reals, yo.
1919- for kw in ['get', 'set', 'delete']:
1920- if kw in dict:
1921- dict[kw] = staticmethod(dict[kw])
1922- secretClass = type.__new__(meta, name, bases, dict)
1923- if attribute is None:
1924- return secretClass
1925- return secretClass()
1926-
1927-class attribute(object):
1928- """
1929- Convenience class for providing one-shot descriptors, similar to
1930- 'property'. For example:
1931-
1932- >>> from epsilon.descriptor import attribute
1933- >>> class Dynamo(object):
1934- ... class dynamic(attribute):
1935- ... def get(self):
1936- ... self.dynCount += 1
1937- ... return self.dynCount
1938- ... def set(self, value):
1939- ... self.dynCount += value
1940- ... dynCount = 0
1941- ...
1942- >>> d = Dynamo()
1943- >>> d.dynamic
1944- 1
1945- >>> d.dynamic
1946- 2
1947- >>> d.dynamic = 6
1948- >>> d.dynamic
1949- 9
1950- >>> d.dynamic
1951- 10
1952- >>> del d.dynamic
1953- Traceback (most recent call last):
1954- ...
1955- AttributeError: attribute cannot be removed
1956- """
1957-
1958- __metaclass__ = _MetaAttribute
1959-
1960- def __get__(self, oself, type):
1961- """
1962- Private implementation of descriptor interface.
1963- """
1964- if oself is None:
1965- return self
1966- return self.get(oself)
1967-
1968- def __set__(self, oself, value):
1969- """
1970- Private implementation of descriptor interface.
1971- """
1972- return self.set(oself, value)
1973-
1974- def __delete__(self, oself):
1975- """
1976- Private implementation of descriptor interface.
1977- """
1978- return self.delete(oself)
1979-
1980- def set(self, value):
1981- """
1982- Implement this method to provide attribute setting. Default behavior
1983- is that attributes are not settable.
1984- """
1985- raise AttributeError('read only attribute')
1986-
1987- def get(self):
1988- """
1989- Implement this method to provide attribute retrieval. Default behavior
1990- is that unset attributes do not have any value.
1991- """
1992- raise AttributeError('attribute has no value')
1993-
1994- def delete(self):
1995- """
1996- Implement this method to provide attribute deletion. Default behavior
1997- is that attributes cannot be deleted.
1998- """
1999- raise AttributeError('attribute cannot be removed')
2000-
2001-
2002-
2003-def requiredAttribute(requiredAttributeName):
2004- """
2005- Utility for defining attributes on base classes/mixins which require their
2006- values to be supplied by their derived classes. C{None} is a common, but
2007- almost never suitable default value for these kinds of attributes, as it
2008- may cause operations in the derived class to fail silently in peculiar
2009- ways. If a C{requiredAttribute} is accessed before having its value
2010- changed, a C{AttributeError} will be raised with a helpful error message.
2011-
2012- @param requiredAttributeName: The name of the required attribute.
2013- @type requiredAttributeName: C{str}
2014-
2015- Example:
2016- >>> from epsilon.descriptor import requiredAttribute
2017- ...
2018- >>> class FooTestMixin:
2019- ... expectedResult = requiredAttribute('expectedResult')
2020- ...
2021- >>> class BrokenFooTestCase(TestCase, FooTestMixin):
2022- ... pass
2023- ...
2024- >>> brokenFoo = BrokenFooTestCase()
2025- >>> print brokenFoo.expectedResult
2026- Traceback (most recent call last):
2027- ...
2028- AttributeError: Required attribute 'expectedResult' has not been
2029- changed from its default value on '<BrokenFooTestCase
2030- instance>'.
2031- ...
2032- >>> class WorkingFooTestCase(TestCase, FooTestMixin):
2033- ... expectedResult = 7
2034- ...
2035- >>> workingFoo = WorkingFooTestCase()
2036- >>> print workingFoo.expectedResult
2037- ... 7
2038- >>>
2039- """
2040- class RequiredAttribute(attribute):
2041- def get(self):
2042- if requiredAttributeName not in self.__dict__:
2043- raise AttributeError(
2044- ('Required attribute %r has not been changed'
2045- ' from its default value on %r' % (
2046- requiredAttributeName, self)))
2047- return self.__dict__[requiredAttributeName]
2048- def set(self, value):
2049- self.__dict__[requiredAttributeName] = value
2050- return RequiredAttribute
2051-
2052-
2053-
2054-__all__ = ['attribute', 'requiredAttribute']
2055
2056=== removed file 'Epsilon/epsilon/expose.py'
2057--- Epsilon/epsilon/expose.py 2008-08-13 02:55:58 +0000
2058+++ Epsilon/epsilon/expose.py 1970-01-01 00:00:00 +0000
2059@@ -1,141 +0,0 @@
2060-# Copright 2008 Divmod, Inc. See LICENSE file for details.
2061-# -*- test-case-name: epsilon.test.test_expose -*-
2062-
2063-"""
2064-This module provides L{Exposer}, a utility for creating decorators that expose
2065-methods on types for a particular purpose.
2066-
2067-The typical usage of this module is for an infrastructure layer (usually one
2068-that allows methods to be invoked from the network, directly or indirectly) to
2069-provide an explicit API for exposing those methods securely.
2070-
2071-For example, a sketch of a finger protocol implementation which could use this
2072-to expose the results of certain methods as finger results::
2073-
2074- # tx_finger.py
2075- fingermethod = Exposer("This object exposes finger methods.")
2076- ...
2077- class FingerProtocol(Protocol):
2078- def __init__(self, fingerModel):
2079- self.model = fingerModel
2080- ...
2081- def fingerQuestionReceived(self, whichUser):
2082- try:
2083- method = fingermethod.get(self.model, whichUser)
2084- except MethodNotExposed:
2085- method = lambda : "Unknown user"
2086- return method()
2087-
2088- # myfingerserver.py
2089- from tx_finger import fingermethod
2090- ...
2091- class MyFingerModel(object):
2092- @fingermethod.expose("bob")
2093- def someMethod(self):
2094- return "Bob is great."
2095-
2096-Assuming lots of protocol code to hook everything together, this would then
2097-allow you to use MyFingerModel and 'finger bob' to get the message 'Bob is
2098-great.'
2099-"""
2100-
2101-import inspect
2102-
2103-from types import FunctionType
2104-
2105-
2106-class MethodNotExposed(Exception):
2107- """
2108- The requested method was not exposed for the purpose requested. More
2109- specifically, L{Exposer.get} was used to retrieve a key from an object
2110- which does not expose that key with that exposer.
2111- """
2112-
2113-
2114-class NameRequired(Exception):
2115- """
2116- L{Exposer.expose} was used to decorate a non-function object without having
2117- a key explicitly specified.
2118- """
2119-
2120-
2121-
2122-class Exposer(object):
2123- """
2124- This is an object that can expose and retrieve methods on classes.
2125-
2126- @ivar _exposed: a dict mapping exposed keys to exposed function objects.
2127- """
2128-
2129- def __init__(self, doc):
2130- """
2131- Create an exposer.
2132- """
2133- self.__doc__ = doc
2134- self._exposed = {}
2135-
2136-
2137- def expose(self, key=None):
2138- """
2139- Expose the decorated method for this L{Exposer} with the given key. A
2140- method which is exposed will be able to be retrieved by this
2141- L{Exposer}'s C{get} method with that key. If no key is provided, the
2142- key is the method name of the exposed method.
2143-
2144- Use like so::
2145-
2146- class MyClass:
2147- @someExposer.expose()
2148- def foo(): ...
2149-
2150- or::
2151-
2152- class MyClass:
2153- @someExposer.expose('foo')
2154- def unrelatedMethodName(): ...
2155-
2156- @param key: a hashable object, used by L{Exposer.get} to look up the
2157- decorated method later. If None, the key is the exposed method's name.
2158-
2159- @return: a 1-argument callable which records its input as exposed, then
2160- returns it.
2161- """
2162- def decorator(function):
2163- rkey = key
2164- if rkey is None:
2165- if isinstance(function, FunctionType):
2166- rkey = function.__name__
2167- else:
2168- raise NameRequired()
2169- if rkey not in self._exposed:
2170- self._exposed[rkey] = []
2171- self._exposed[rkey].append(function)
2172- return function
2173- return decorator
2174-
2175-
2176- def get(self, obj, key):
2177- """
2178- Retrieve 'key' from an instance of a class which previously exposed it.
2179-
2180- @param key: a hashable object, previously passed to L{Exposer.expose}.
2181-
2182- @return: the object which was exposed with the given name on obj's key.
2183-
2184- @raise MethodNotExposed: when the key in question was not exposed with
2185- this exposer.
2186- """
2187- if key not in self._exposed:
2188- raise MethodNotExposed()
2189- rightFuncs = self._exposed[key]
2190- T = obj.__class__
2191- seen = {}
2192- for subT in inspect.getmro(T):
2193- for name, value in subT.__dict__.items():
2194- for rightFunc in rightFuncs:
2195- if value is rightFunc:
2196- if name in seen:
2197- raise MethodNotExposed()
2198- return value.__get__(obj, T)
2199- seen[name] = True
2200- raise MethodNotExposed()
2201
2202=== removed file 'Epsilon/epsilon/extime.py'
2203--- Epsilon/epsilon/extime.py 2009-11-16 19:09:42 +0000
2204+++ Epsilon/epsilon/extime.py 1970-01-01 00:00:00 +0000
2205@@ -1,980 +0,0 @@
2206-# -*- test-case-name: epsilon.test.test_extime -*-
2207-"""
2208-Extended date/time formatting and miscellaneous functionality.
2209-
2210-See the class 'Time' for details.
2211-"""
2212-
2213-import datetime
2214-import re
2215-
2216-from email.Utils import parsedate_tz
2217-
2218-_EPOCH = datetime.datetime.utcfromtimestamp(0)
2219-
2220-
2221-class InvalidPrecision(Exception):
2222- """
2223- L{Time.asHumanly} was passed an invalid precision value.
2224- """
2225-
2226-
2227-
2228-def sanitizeStructTime(struct):
2229- """
2230- Convert struct_time tuples with possibly invalid values to valid
2231- ones by substituting the closest valid value.
2232- """
2233- maxValues = (9999, 12, 31, 23, 59, 59)
2234- minValues = (1, 1, 1, 0, 0, 0)
2235- newstruct = []
2236- for value, maxValue, minValue in zip(struct[:6], maxValues, minValues):
2237- newstruct.append(max(minValue, min(value, maxValue)))
2238- return tuple(newstruct) + struct[6:]
2239-
2240-def _timedeltaToSignHrMin(offset):
2241- """
2242- Return a (sign, hour, minute) triple for the offset described by timedelta.
2243-
2244- sign is a string, either "+" or "-". In the case of 0 offset, sign is "+".
2245- """
2246- minutes = round((offset.days * 3600000000 * 24
2247- + offset.seconds * 1000000
2248- + offset.microseconds)
2249- / 60000000.0)
2250- if minutes < 0:
2251- sign = '-'
2252- minutes = -minutes
2253- else:
2254- sign = '+'
2255- return (sign, minutes // 60, minutes % 60)
2256-
2257-def _timedeltaToSeconds(offset):
2258- """
2259- Convert a datetime.timedelta instance to simply a number of seconds.
2260-
2261- For example, you can specify purely second intervals with timedelta's
2262- constructor:
2263-
2264- >>> td = datetime.timedelta(seconds=99999999)
2265-
2266- but then you can't get them out again:
2267-
2268- >>> td.seconds
2269- 35199
2270-
2271- This allows you to:
2272-
2273- >>> import epsilon.extime
2274- >>> epsilon.extime._timedeltaToSeconds(td)
2275- 99999999.0
2276-
2277- @param offset: a L{datetime.timedelta} representing an interval that we
2278- wish to know the total number of seconds for.
2279-
2280- @return: a number of seconds
2281- @rtype: float
2282- """
2283- return ((offset.days * 60*60*24) +
2284- (offset.seconds) +
2285- (offset.microseconds * 1e-6))
2286-
2287-class FixedOffset(datetime.tzinfo):
2288- _zeroOffset = datetime.timedelta()
2289-
2290- def __init__(self, hours, minutes):
2291- self.offset = datetime.timedelta(minutes = hours * 60 + minutes)
2292-
2293- def utcoffset(self, dt):
2294- return self.offset
2295-
2296- def tzname(self, dt):
2297- return _timedeltaToSignHrMin(self.offset)
2298-
2299- def dst(self, tz):
2300- return self._zeroOffset
2301-
2302- def __repr__(self):
2303- return '<%s.%s object at 0x%x offset %r>' % (
2304- self.__module__, type(self).__name__, id(self), self.offset)
2305-
2306-
2307-
2308-class Time(object):
2309- """An object representing a well defined instant in time.
2310-
2311- A Time object unambiguously addresses some time, independent of timezones,
2312- contorted base-60 counting schemes, leap seconds, and the effects of
2313- general relativity. It provides methods for returning a representation of
2314- this time in various ways that a human or a programmer might find more
2315- useful in various applications.
2316-
2317- Every Time instance has an attribute 'resolution'. This can be ignored, or
2318- the instance can be considered to address a span of time. This resolution
2319- is determined by the value used to initalize the instance, or the
2320- resolution of the internal representation, whichever is greater. It is
2321- mostly useful when using input formats that allow the specification of
2322- whole days or weeks. For example, ISO 8601 allows one to state a time as,
2323- "2005-W03", meaning "the third week of 2005". In this case the resolution
2324- is set to one week. Other formats are considered to express only an instant
2325- in time, such as a POSIX timestamp, because the resolution of the time is
2326- limited only by the hardware's representation of a real number.
2327-
2328- Timezones are significant only for instances with a resolution greater than
2329- one day. When the timezone is insignificant, the result of methods like
2330- asISO8601TimeAndDate is the same for any given tzinfo parameter. Sort order
2331- is determined by the start of the period in UTC. For example, "today" sorts
2332- after "midnight today, central Europe", and before "midnight today, US
2333- Eastern". For applications that need to store a mix of timezone dependent
2334- and independent instances, it may be wise to store them separately, since
2335- the time between the start and end of today in the local timezone may not
2336- include the start of today in UTC, and thus not independent instances
2337- addressing the whole day. In other words, the desired sort order (the one
2338- where just "Monday" sorts before any more precise time in "Monday", and
2339- after any in "Sunday") of Time instances is dependant on the timezone
2340- context.
2341-
2342- Date arithmetic and boolean operations operate on instants in time, not
2343- periods. In this case, the start of the period is used as the value, and
2344- the result has a resolution of 0.
2345-
2346- For containment tests with the 'in' operator, the period addressed by the
2347- instance is used.
2348-
2349- The methods beginning with 'from' are constructors to create instances from
2350- various formats. Some of them are textual formats, and others are other
2351- time types commonly found in Python code.
2352-
2353- Likewise, methods beginning with 'as' return the represented time in
2354- various formats. Some of these methods should try to reflect the resolution
2355- of the instance. However, they don't yet.
2356-
2357- For formats with both a constructor and a formatter, d == fromFu(d.asFu())
2358-
2359- @type resolution: datetime.timedelta
2360- @ivar resolution: the length of the period to which this instance could
2361- refer. For example, "Today, 13:38" could refer to any time between 13:38
2362- until but not including 13:39. In this case resolution would be
2363- timedelta(minutes=1).
2364- """
2365-
2366- # the instance variable _time is the internal representation of time. It
2367- # is a naive datetime object which is always UTC. A UTC tzinfo would be
2368- # great, if one existed, and anyway it complicates pickling.
2369-
2370-
2371- class Precision(object):
2372- MINUTES = object()
2373- SECONDS = object()
2374-
2375-
2376- _timeFormat = {
2377- Precision.MINUTES: '%I:%M %p',
2378- Precision.SECONDS: '%I:%M:%S %p'}
2379-
2380- rfc2822Weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
2381-
2382- rfc2822Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',
2383- 'Sep', 'Oct', 'Nov', 'Dec']
2384-
2385- resolution = datetime.timedelta.resolution
2386-
2387- #
2388- # Methods to create new instances
2389- #
2390-
2391- def __init__(self):
2392- """Return a new Time instance representing the time now.
2393-
2394- See also the fromFu methods to create new instances from other types of
2395- initializers.
2396- """
2397- self._time = datetime.datetime.utcnow()
2398-
2399-
2400- def _fromWeekday(klass, match, tzinfo, now):
2401- weekday = klass.weekdays.index(match.group('weekday').lower())
2402- dtnow = now.asDatetime().replace(
2403- hour=0, minute=0, second=0, microsecond=0)
2404- daysInFuture = (weekday - dtnow.weekday()) % len(klass.weekdays)
2405- if daysInFuture == 0:
2406- daysInFuture = 7
2407- self = klass.fromDatetime(dtnow + datetime.timedelta(days=daysInFuture))
2408- assert self.asDatetime().weekday() == weekday
2409- self.resolution = datetime.timedelta(days=1)
2410- return self
2411-
2412-
2413- def _fromTodayOrTomorrow(klass, match, tzinfo, now):
2414- dtnow = now.asDatetime().replace(
2415- hour=0, minute=0, second=0, microsecond=0)
2416- when = match.group(0).lower()
2417- if when == 'tomorrow':
2418- dtnow += datetime.timedelta(days=1)
2419- elif when == 'yesterday':
2420- dtnow -= datetime.timedelta(days=1)
2421- else:
2422- assert when == 'today'
2423- self = klass.fromDatetime(dtnow)
2424- self.resolution = datetime.timedelta(days=1)
2425- return self
2426-
2427-
2428- def _fromTime(klass, match, tzinfo, now):
2429- minute = int(match.group('minute'))
2430- hour = int(match.group('hour'))
2431- ampm = (match.group('ampm') or '').lower()
2432- if ampm:
2433- if not 1 <= hour <= 12:
2434- raise ValueError, 'hour %i is not in 1..12' % (hour,)
2435- if hour == 12 and ampm == 'am':
2436- hour = 0
2437- elif ampm == 'pm':
2438- hour += 12
2439- if not 0 <= hour <= 23:
2440- raise ValueError, 'hour %i is not in 0..23' % (hour,)
2441-
2442- dtnow = now.asDatetime(tzinfo).replace(second=0, microsecond=0)
2443- dtthen = dtnow.replace(hour=hour, minute=minute)
2444- if dtthen < dtnow:
2445- dtthen += datetime.timedelta(days=1)
2446-
2447- self = klass.fromDatetime(dtthen)
2448- self.resolution = datetime.timedelta(minutes=1)
2449- return self
2450-
2451-
2452- def _fromNoonOrMidnight(klass, match, tzinfo, now):
2453- when = match.group(0).lower()
2454- if when == 'noon':
2455- hour = 12
2456- else:
2457- assert when == 'midnight'
2458- hour = 0
2459- dtnow = now.asDatetime(tzinfo).replace(
2460- minute=0, second=0, microsecond=0)
2461- dtthen = dtnow.replace(hour=hour)
2462- if dtthen < dtnow:
2463- dtthen += datetime.timedelta(days=1)
2464-
2465- self = klass.fromDatetime(dtthen)
2466- self.resolution = datetime.timedelta(minutes=1)
2467- return self
2468-
2469- def _fromNow(klass, match, tzinfo, now):
2470- # coerce our 'now' argument to an instant
2471- return now + datetime.timedelta(0)
2472-
2473- weekdays = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday',
2474- 'saturday', 'sunday']
2475-
2476- humanlyPatterns = [
2477- (re.compile(r"""
2478- \b
2479- ((next|this)\s+)?
2480- (?P<weekday>
2481- monday
2482- | tuesday
2483- | wednesday
2484- | thursday
2485- | friday
2486- | saturday
2487- | sunday
2488- )
2489- \b
2490- """, re.IGNORECASE | re.VERBOSE),
2491- _fromWeekday),
2492- (re.compile(r"\b(today|tomorrow|yesterday)\b", re.IGNORECASE),
2493- _fromTodayOrTomorrow),
2494- (re.compile(r"""
2495- \b
2496- (?P<hour>\d{1,2}):(?P<minute>\d{2})
2497- (\s*(?P<ampm>am|pm))?
2498- \b
2499- """, re.IGNORECASE | re.VERBOSE),
2500- _fromTime),
2501- (re.compile(r"\b(noon|midnight)\b", re.IGNORECASE),
2502- _fromNoonOrMidnight),
2503- (re.compile(r"\b(now)\b", re.IGNORECASE),
2504- _fromNow),
2505- ]
2506-
2507- _fromWeekday = classmethod(_fromWeekday)
2508- _fromTodayOrTomorrow = classmethod(_fromTodayOrTomorrow)
2509- _fromTime = classmethod(_fromTime)
2510- _fromNoonOrMidnight = classmethod(_fromNoonOrMidnight)
2511- _fromNow = classmethod(_fromNow)
2512-
2513-
2514- def fromHumanly(klass, humanStr, tzinfo=None, now=None):
2515- """Return a new Time instance from a string a human might type.
2516-
2517- @param humanStr: the string to be parsed.
2518-
2519- @param tzinfo: A tzinfo instance indicating the timezone to assume if
2520- none is specified in humanStr. If None, assume UTC.
2521-
2522- @param now: A Time instance to be considered "now" for when
2523- interpreting relative dates like "tomorrow". If None, use the real now.
2524-
2525- Total crap now, it just supports weekdays, "today" and "tomorrow" for
2526- now. This is pretty insufficient and useless, but good enough for some
2527- demo functionality, or something.
2528- """
2529- humanStr = humanStr.strip()
2530- if now is None:
2531- now = Time()
2532- if tzinfo is None:
2533- tzinfo = FixedOffset(0, 0)
2534-
2535- for pattern, creator in klass.humanlyPatterns:
2536- match = pattern.match(humanStr)
2537- if not match \
2538- or match.span()[1] != len(humanStr):
2539- continue
2540- try:
2541- return creator(klass, match, tzinfo, now)
2542- except ValueError:
2543- continue
2544- raise ValueError, 'could not parse date: %r' % (humanStr,)
2545-
2546- fromHumanly = classmethod(fromHumanly)
2547-
2548-
2549- iso8601pattern = re.compile(r"""
2550- ^ (?P<year> \d{4})
2551- (
2552- # a year may optionally be followed by one of:
2553- # - a month
2554- # - a week
2555- # - a specific day, and an optional time
2556- # a specific day is one of:
2557- # - a month and day
2558- # - week and weekday
2559- # - a day of the year
2560- (
2561- -? (?P<month1> \d{2})
2562- |
2563- -? W (?P<week1> \d{2})
2564- |
2565- (
2566- -? (?P<month2> \d{2})
2567- -? (?P<day> \d{2})
2568- |
2569- -? W (?P<week2> \d{2})
2570- -? (?P<weekday> \d)
2571- |
2572- -? (?P<dayofyear> \d{3})
2573- )
2574- (
2575- T (?P<hour> \d{2})
2576- (
2577- :? (?P<minute> \d{2})
2578- (
2579- :? (?P<second> \d{2})
2580- (
2581- [\.,] (?P<fractionalsec> \d+)
2582- )?
2583- )?
2584- )?
2585- (
2586- (?P<zulu> Z)
2587- |
2588- (?P<tzhour> [+\-]\d{2})
2589- (
2590- :? (?P<tzmin> \d{2})
2591- )?
2592- )?
2593- )?
2594- )?
2595- )? $""", re.VERBOSE)
2596-
2597-
2598- def fromISO8601TimeAndDate(klass, iso8601string, tzinfo=None):
2599- """Return a new Time instance from a string formated as in ISO 8601.
2600-
2601- If the given string contains no timezone, it is assumed to be in the
2602- timezone specified by the parameter `tzinfo`, or UTC if tzinfo is None.
2603- An input string with an explicit timezone will always override tzinfo.
2604-
2605- If the given iso8601string does not contain all parts of the time, they
2606- will default to 0 in the timezone given by `tzinfo`.
2607-
2608- WARNING: this function is incomplete. ISO is dumb and their standards
2609- are not free. Only a subset of all valid ISO 8601 dates are parsed,
2610- because I can't find a formal description of the format. However,
2611- common ones should work.
2612- """
2613-
2614- def calculateTimezone():
2615- if groups['zulu'] == 'Z':
2616- return FixedOffset(0, 0)
2617- else:
2618- tzhour = groups.pop('tzhour')
2619- tzmin = groups.pop('tzmin')
2620- if tzhour is not None:
2621- return FixedOffset(int(tzhour), int(tzmin or 0))
2622- return tzinfo or FixedOffset(0, 0)
2623-
2624- def coerceGroups():
2625- groups['month'] = groups['month1'] or groups['month2']
2626- groups['week'] = groups['week1'] or groups['week2']
2627- # don't include fractional seconds, because it's not an integer.
2628- defaultTo0 = ['hour', 'minute', 'second']
2629- defaultTo1 = ['month', 'day', 'week', 'weekday', 'dayofyear']
2630- if groups['fractionalsec'] is None:
2631- groups['fractionalsec'] = '0'
2632- for key in defaultTo0:
2633- if groups[key] is None:
2634- groups[key] = 0
2635- for key in defaultTo1:
2636- if groups[key] is None:
2637- groups[key] = 1
2638- groups['fractionalsec'] = float('.'+groups['fractionalsec'])
2639- for key in defaultTo0 + defaultTo1 + ['year']:
2640- groups[key] = int(groups[key])
2641-
2642- for group, min, max in [
2643- # some years have only 52 weeks
2644- ('week', 1, 53),
2645- ('weekday', 1, 7),
2646- ('month', 1, 12),
2647- ('day', 1, 31),
2648- ('hour', 0, 24),
2649- ('minute', 0, 59),
2650-
2651- # Sometime in the 22nd century AD, two leap seconds will be
2652- # required every year. In the 25th century AD, four every
2653- # year. We'll ignore that for now though because it would be
2654- # tricky to get right and we certainly don't need it for our
2655- # target applications. In other words, post-singularity
2656- # Martian users, please do not rely on this code for
2657- # compatibility with Greater Galactic Protectorate of Earth
2658- # date/time formatting! Apologies, but no library I know of in
2659- # Python is sufficient for processing their dates and times
2660- # without ADA bindings to get the radiation-safety zone counter
2661- # correct. -glyph
2662-
2663- ('second', 0, 61),
2664- # don't forget leap years
2665- ('dayofyear', 1, 366)]:
2666- if not min <= groups[group] <= max:
2667- raise ValueError, '%s must be in %i..%i' % (group, min, max)
2668-
2669- def determineResolution():
2670- if match.group('fractionalsec') is not None:
2671- return max(datetime.timedelta.resolution,
2672- datetime.timedelta(
2673- microseconds=1 * 10 ** -len(
2674- match.group('fractionalsec')) * 1000000))
2675-
2676- for testGroup, resolution in [
2677- ('second', datetime.timedelta(seconds=1)),
2678- ('minute', datetime.timedelta(minutes=1)),
2679- ('hour', datetime.timedelta(hours=1)),
2680- ('weekday', datetime.timedelta(days=1)),
2681- ('dayofyear', datetime.timedelta(days=1)),
2682- ('day', datetime.timedelta(days=1)),
2683- ('week1', datetime.timedelta(weeks=1)),
2684- ('week2', datetime.timedelta(weeks=1))]:
2685- if match.group(testGroup) is not None:
2686- return resolution
2687-
2688- if match.group('month1') is not None \
2689- or match.group('month2') is not None:
2690- if self._time.month == 12:
2691- return datetime.timedelta(days=31)
2692- nextMonth = self._time.replace(month=self._time.month+1)
2693- return nextMonth - self._time
2694- else:
2695- nextYear = self._time.replace(year=self._time.year+1)
2696- return nextYear - self._time
2697-
2698- def calculateDtime(tzinfo):
2699- """Calculate a datetime for the start of the addressed period."""
2700-
2701- if match.group('week1') is not None \
2702- or match.group('week2') is not None:
2703- if not 0 < groups['week'] <= 53:
2704- raise ValueError(
2705- 'week must be in 1..53 (was %i)' % (groups['week'],))
2706- dtime = datetime.datetime(
2707- groups['year'],
2708- 1,
2709- 4,
2710- groups['hour'],
2711- groups['minute'],
2712- groups['second'],
2713- int(round(groups['fractionalsec'] * 1000000)),
2714- tzinfo=tzinfo
2715- )
2716- dtime -= datetime.timedelta(days = dtime.weekday())
2717- dtime += datetime.timedelta(
2718- days = (groups['week']-1) * 7 + groups['weekday'] - 1)
2719- if dtime.isocalendar() != (
2720- groups['year'], groups['week'], groups['weekday']):
2721- # actually the problem could be an error in my logic, but
2722- # nothing should cause this but requesting week 53 of a
2723- # year with 52 weeks.
2724- raise ValueError('year %04i has no week %02i' %
2725- (groups['year'], groups['week']))
2726- return dtime
2727-
2728- if match.group('dayofyear') is not None:
2729- dtime = datetime.datetime(
2730- groups['year'],
2731- 1,
2732- 1,
2733- groups['hour'],
2734- groups['minute'],
2735- groups['second'],
2736- int(round(groups['fractionalsec'] * 1000000)),
2737- tzinfo=tzinfo
2738- )
2739- dtime += datetime.timedelta(days=groups['dayofyear']-1)
2740- if dtime.year != groups['year']:
2741- raise ValueError(
2742- 'year %04i has no day of year %03i' %
2743- (groups['year'], groups['dayofyear']))
2744- return dtime
2745-
2746- else:
2747- return datetime.datetime(
2748- groups['year'],
2749- groups['month'],
2750- groups['day'],
2751- groups['hour'],
2752- groups['minute'],
2753- groups['second'],
2754- int(round(groups['fractionalsec'] * 1000000)),
2755- tzinfo=tzinfo
2756- )
2757-
2758-
2759- match = klass.iso8601pattern.match(iso8601string)
2760- if match is None:
2761- raise ValueError(
2762- '%r could not be parsed as an ISO 8601 date and time' %
2763- (iso8601string,))
2764-
2765- groups = match.groupdict()
2766- coerceGroups()
2767- if match.group('hour') is not None:
2768- timezone = calculateTimezone()
2769- else:
2770- timezone = None
2771- self = klass.fromDatetime(calculateDtime(timezone))
2772- self.resolution = determineResolution()
2773- return self
2774-
2775- fromISO8601TimeAndDate = classmethod(fromISO8601TimeAndDate)
2776-
2777- def fromStructTime(klass, structTime, tzinfo=None):
2778- """Return a new Time instance from a time.struct_time.
2779-
2780- If tzinfo is None, structTime is in UTC. Otherwise, tzinfo is a
2781- datetime.tzinfo instance coresponding to the timezone in which
2782- structTime is.
2783-
2784- Many of the functions in the standard time module return these things.
2785- This will also work with a plain 9-tuple, for parity with the time
2786- module. The last three elements, or tm_wday, tm_yday, and tm_isdst are
2787- ignored.
2788- """
2789- dtime = datetime.datetime(tzinfo=tzinfo, *structTime[:6])
2790- self = klass.fromDatetime(dtime)
2791- self.resolution = datetime.timedelta(seconds=1)
2792- return self
2793-
2794- fromStructTime = classmethod(fromStructTime)
2795-
2796- def fromDatetime(klass, dtime):
2797- """Return a new Time instance from a datetime.datetime instance.
2798-
2799- If the datetime instance does not have an associated timezone, it is
2800- assumed to be UTC.
2801- """
2802- self = klass.__new__(klass)
2803- if dtime.tzinfo is not None:
2804- self._time = dtime.astimezone(FixedOffset(0, 0)).replace(tzinfo=None)
2805- else:
2806- self._time = dtime
2807- self.resolution = datetime.timedelta.resolution
2808- return self
2809-
2810- fromDatetime = classmethod(fromDatetime)
2811-
2812- def fromPOSIXTimestamp(klass, secs):
2813- """Return a new Time instance from seconds since the POSIX epoch.
2814-
2815- The POSIX epoch is midnight Jan 1, 1970 UTC. According to POSIX, leap
2816- seconds don't exist, so one UTC day is exactly 86400 seconds, even if
2817- it wasn't.
2818-
2819- @param secs: a number of seconds, represented as an integer, long or
2820- float.
2821- """
2822- self = klass.fromDatetime(_EPOCH + datetime.timedelta(seconds=secs))
2823- self.resolution = datetime.timedelta()
2824- return self
2825-
2826- fromPOSIXTimestamp = classmethod(fromPOSIXTimestamp)
2827-
2828- def fromRFC2822(klass, rfc822string):
2829- """
2830- Return a new Time instance from a string formated as described in RFC 2822.
2831-
2832- @type rfc822string: str
2833-
2834- @raise ValueError: if the timestamp is not formatted properly (or if
2835- certain obsoleted elements of the specification are used).
2836-
2837- @return: a new L{Time}
2838- """
2839-
2840- # parsedate_tz is going to give us a "struct_time plus", a 10-tuple
2841- # containing the 9 values a struct_time would, i.e.: (tm_year, tm_mon,
2842- # tm_day, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst), plus a
2843- # bonus "offset", which is an offset (in _seconds_, of all things).
2844-
2845- maybeStructTimePlus = parsedate_tz(rfc822string)
2846-
2847- if maybeStructTimePlus is None:
2848- raise ValueError, 'could not parse RFC 2822 date %r' % (rfc822string,)
2849- structTimePlus = sanitizeStructTime(maybeStructTimePlus)
2850- offsetInSeconds = structTimePlus[-1]
2851- if offsetInSeconds is None:
2852- offsetInSeconds = 0
2853- self = klass.fromStructTime(
2854- structTimePlus,
2855- FixedOffset(
2856- hours=0,
2857- minutes=offsetInSeconds // 60))
2858- self.resolution = datetime.timedelta(seconds=1)
2859- return self
2860-
2861- fromRFC2822 = classmethod(fromRFC2822)
2862-
2863- #
2864- # Methods to produce various formats
2865- #
2866-
2867- def asPOSIXTimestamp(self):
2868- """Return this time as a timestamp as specified by POSIX.
2869-
2870- This timestamp is the count of the number of seconds since Midnight,
2871- Jan 1 1970 UTC, ignoring leap seconds.
2872- """
2873- mytimedelta = self._time - _EPOCH
2874- return _timedeltaToSeconds(mytimedelta)
2875-
2876- def asDatetime(self, tzinfo=None):
2877- """Return this time as an aware datetime.datetime instance.
2878-
2879- The returned datetime object has the specified tzinfo, or a tzinfo
2880- describing UTC if the tzinfo parameter is None.
2881- """
2882- if tzinfo is None:
2883- tzinfo = FixedOffset(0, 0)
2884-
2885- if not self.isTimezoneDependent():
2886- return self._time.replace(tzinfo=tzinfo)
2887- else:
2888- return self._time.replace(tzinfo=FixedOffset(0, 0)).astimezone(tzinfo)
2889-
2890- def asNaiveDatetime(self, tzinfo=None):
2891- """Return this time as a naive datetime.datetime instance.
2892-
2893- The returned datetime object has its tzinfo set to None, but is in the
2894- timezone given by the tzinfo parameter, or UTC if the parameter is
2895- None.
2896- """
2897- return self.asDatetime(tzinfo).replace(tzinfo=None)
2898-
2899- def asRFC2822(self, tzinfo=None, includeDayOfWeek=True):
2900- """Return this Time formatted as specified in RFC 2822.
2901-
2902- RFC 2822 specifies the format of email messages.
2903-
2904- RFC 2822 says times in email addresses should reflect the local
2905- timezone. If tzinfo is a datetime.tzinfo instance, the returned
2906- formatted string will reflect that timezone. Otherwise, the timezone
2907- will be '-0000', which RFC 2822 defines as UTC, but with an unknown
2908- local timezone.
2909-
2910- RFC 2822 states that the weekday is optional. The parameter
2911- includeDayOfWeek indicates whether or not to include it.
2912- """
2913- dtime = self.asDatetime(tzinfo)
2914-
2915- if tzinfo is None:
2916- rfcoffset = '-0000'
2917- else:
2918- rfcoffset = '%s%02i%02i' % _timedeltaToSignHrMin(dtime.utcoffset())
2919-
2920- rfcstring = ''
2921- if includeDayOfWeek:
2922- rfcstring += self.rfc2822Weekdays[dtime.weekday()] + ', '
2923-
2924- rfcstring += '%i %s %4i %02i:%02i:%02i %s' % (
2925- dtime.day,
2926- self.rfc2822Months[dtime.month - 1],
2927- dtime.year,
2928- dtime.hour,
2929- dtime.minute,
2930- dtime.second,
2931- rfcoffset)
2932-
2933- return rfcstring
2934-
2935- def asISO8601TimeAndDate(self, includeDelimiters=True, tzinfo=None,
2936- includeTimezone=True):
2937- """Return this time formatted as specified by ISO 8861.
2938-
2939- ISO 8601 allows optional dashes to delimit dates and colons to delimit
2940- times. The parameter includeDelimiters (default True) defines the
2941- inclusion of these delimiters in the output.
2942-
2943- If tzinfo is a datetime.tzinfo instance, the output time will be in the
2944- timezone given. If it is None (the default), then the timezone string
2945- will not be included in the output, and the time will be in UTC.
2946-
2947- The includeTimezone parameter coresponds to the inclusion of an
2948- explicit timezone. The default is True.
2949- """
2950- if not self.isTimezoneDependent():
2951- tzinfo = None
2952- dtime = self.asDatetime(tzinfo)
2953-
2954- if includeDelimiters:
2955- dateSep = '-'
2956- timeSep = ':'
2957- else:
2958- dateSep = timeSep = ''
2959-
2960- if includeTimezone:
2961- if tzinfo is None:
2962- timezone = '+00%s00' % (timeSep,)
2963- else:
2964- sign, hour, min = _timedeltaToSignHrMin(dtime.utcoffset())
2965- timezone = '%s%02i%s%02i' % (sign, hour, timeSep, min)
2966- else:
2967- timezone = ''
2968-
2969- microsecond = ('%06i' % (dtime.microsecond,)).rstrip('0')
2970- if microsecond:
2971- microsecond = '.' + microsecond
2972-
2973- parts = [
2974- ('%04i' % (dtime.year,), datetime.timedelta(days=366)),
2975- ('%s%02i' % (dateSep, dtime.month), datetime.timedelta(days=31)),
2976- ('%s%02i' % (dateSep, dtime.day), datetime.timedelta(days=1)),
2977- ('T', datetime.timedelta(hours=1)),
2978- ('%02i' % (dtime.hour,), datetime.timedelta(hours=1)),
2979- ('%s%02i' % (timeSep, dtime.minute), datetime.timedelta(minutes=1)),
2980- ('%s%02i' % (timeSep, dtime.second), datetime.timedelta(seconds=1)),
2981- (microsecond, datetime.timedelta(microseconds=1)),
2982- (timezone, datetime.timedelta(hours=1))
2983- ]
2984-
2985- formatted = ''
2986- for part, minResolution in parts:
2987- if self.resolution <= minResolution:
2988- formatted += part
2989-
2990- return formatted
2991-
2992- def asStructTime(self, tzinfo=None):
2993- """Return this time represented as a time.struct_time.
2994-
2995- tzinfo is a datetime.tzinfo instance coresponding to the desired
2996- timezone of the output. If is is the default None, UTC is assumed.
2997- """
2998- dtime = self.asDatetime(tzinfo)
2999- if tzinfo is None:
3000- return dtime.utctimetuple()
3001- else:
3002- return dtime.timetuple()
3003-
3004- def asHumanly(self, tzinfo=None, now=None, precision=Precision.MINUTES):
3005- """Return this time as a short string, tailored to the current time.
3006-
3007- Parts of the date that can be assumed are omitted. Consequently, the
3008- output string depends on the current time. This is the format used for
3009- displaying dates in most user visible places in the quotient web UI.
3010-
3011- By default, the current time is determined by the system clock. The
3012- current time used for formatting the time can be changed by providing a
3013- Time instance as the parameter 'now'.
3014-
3015- @param precision: The smallest unit of time that will be represented
3016- in the returned string. Valid values are L{Time.Precision.MINUTES} and
3017- L{Time.Precision.SECONDS}.
3018-
3019- @raise InvalidPrecision: if the specified precision is not either
3020- L{Time.Precision.MINUTES} or L{Time.Precision.SECONDS}.
3021- """
3022- try:
3023- timeFormat = Time._timeFormat[precision]
3024- except KeyError:
3025- raise InvalidPrecision(
3026- 'Use Time.Precision.MINUTES or Time.Precision.SECONDS')
3027-
3028- if now is None:
3029- now = Time().asDatetime(tzinfo)
3030- else:
3031- now = now.asDatetime(tzinfo)
3032- dtime = self.asDatetime(tzinfo)
3033-
3034- # Same day?
3035- if dtime.date() == now.date():
3036- if self.isAllDay():
3037- return 'all day'
3038- return dtime.strftime(timeFormat).lower()
3039- else:
3040- res = str(dtime.date().day) + dtime.strftime(' %b') # day + month
3041- # Different year?
3042- if not dtime.date().year == now.date().year:
3043- res += dtime.strftime(' %Y')
3044- if not self.isAllDay():
3045- res += dtime.strftime(', %s' % (timeFormat,)).lower()
3046- return res
3047-
3048- #
3049- # methods to return related times
3050- #
3051-
3052- def getBounds(self, tzinfo=None):
3053- """
3054- Return a pair describing the bounds of self.
3055-
3056- This returns a pair (min, max) of Time instances. It is not quite the
3057- same as (self, self + self.resolution). This is because timezones are
3058- insignificant for instances with a resolution greater or equal to 1
3059- day.
3060-
3061- To illustrate the problem, consider a Time instance::
3062-
3063- T = Time.fromHumanly('today', tzinfo=anything)
3064-
3065- This will return an equivalent instance independent of the tzinfo used.
3066- The hour, minute, and second of this instance are 0, and its resolution
3067- is one day.
3068-
3069- Now say we have a sorted list of times, and we want to get all times
3070- for 'today', where whoever said 'today' is in a timezone that's 5 hours
3071- ahead of UTC. The start of 'today' in this timezone is UTC 05:00. The
3072- example instance T above is before this, but obviously it is today.
3073-
3074- The min and max times this returns are such that all potentially
3075- matching instances are within this range. However, this range might
3076- contain unmatching instances.
3077-
3078- As an example of this, if 'today' is April first 2005, then
3079- Time.fromISO8601TimeAndDate('2005-04-01T00:00:00') sorts in the same
3080- place as T from above, but is not in the UTC+5 'today'.
3081-
3082- TIME IS FUN!
3083- """
3084- if self.resolution >= datetime.timedelta(days=1) \
3085- and tzinfo is not None:
3086- time = self._time.replace(tzinfo=tzinfo)
3087- else:
3088- time = self._time
3089-
3090- return (
3091- min(self.fromDatetime(time), self.fromDatetime(self._time)),
3092- max(self.fromDatetime(time + self.resolution),
3093- self.fromDatetime(self._time + self.resolution))
3094- )
3095-
3096- def oneDay(self):
3097- """Return a Time instance representing the day of the start of self.
3098-
3099- The returned new instance will be set to midnight of the day containing
3100- the first instant of self in the specified timezone, and have a
3101- resolution of datetime.timedelta(days=1).
3102- """
3103- day = self.__class__.fromDatetime(self.asDatetime().replace(
3104- hour=0, minute=0, second=0, microsecond=0))
3105- day.resolution = datetime.timedelta(days=1)
3106- return day
3107-
3108- #
3109- # useful predicates
3110- #
3111-
3112- def isAllDay(self):
3113- """Return True iff this instance represents exactly all day."""
3114- return self.resolution == datetime.timedelta(days=1)
3115-
3116- def isTimezoneDependent(self):
3117- """Return True iff timezone is relevant for this instance.
3118-
3119- Timezone is only relevent for instances with a resolution better than
3120- one day.
3121- """
3122- return self.resolution < datetime.timedelta(days=1)
3123-
3124- #
3125- # other magic methods
3126- #
3127-
3128- def __cmp__(self, other):
3129- if not isinstance(other, Time):
3130- raise TypeError("Cannot meaningfully compare %r with %r" % (self, other))
3131- return cmp(self._time, other._time)
3132-
3133- def __eq__(self, other):
3134- if isinstance(other, Time):
3135- return cmp(self._time, other._time) == 0
3136- return False
3137-
3138- def __ne__(self, other):
3139- return not (self == other)
3140-
3141- def __repr__(self):
3142- return 'extime.Time.fromDatetime(%r)' % (self._time,)
3143-
3144- __str__ = asISO8601TimeAndDate
3145-
3146- def __contains__(self, other):
3147- """Test if another Time instance is entirely within the period addressed by this one."""
3148- if not isinstance(other, Time):
3149- raise TypeError(
3150- '%r is not a Time instance; can not test for containment'
3151- % (other,))
3152- if other._time < self._time:
3153- return False
3154- if self._time + self.resolution < other._time + other.resolution:
3155- return False
3156- return True
3157-
3158- def __add__(self, addend):
3159- if not isinstance(addend, datetime.timedelta):
3160- raise TypeError, 'expected a datetime.timedelta instance'
3161- return Time.fromDatetime(self._time + addend)
3162-
3163- def __sub__(self, subtrahend):
3164- """
3165- Implement subtraction of an interval or another time from this one.
3166-
3167- @type subtrahend: L{datetime.timedelta} or L{Time}
3168-
3169- @param subtrahend: The object to be subtracted from this one.
3170-
3171- @rtype: L{datetime.timedelta} or L{Time}
3172-
3173- @return: If C{subtrahend} is a L{datetime.timedelta}, the result is
3174- a L{Time} instance which is offset from this one by that amount. If
3175- C{subtrahend} is a L{Time}, the result is a L{datetime.timedelta}
3176- instance which gives the difference between it and this L{Time}
3177- instance.
3178- """
3179- if isinstance(subtrahend, datetime.timedelta):
3180- return Time.fromDatetime(self._time - subtrahend)
3181-
3182- if isinstance(subtrahend, Time):
3183- return self.asDatetime() - subtrahend.asDatetime()
3184-
3185- return NotImplemented
3186
3187=== removed file 'Epsilon/epsilon/hotfix.py'
3188--- Epsilon/epsilon/hotfix.py 2009-05-22 13:03:43 +0000
3189+++ Epsilon/epsilon/hotfix.py 1970-01-01 00:00:00 +0000
3190@@ -1,81 +0,0 @@
3191-
3192-import inspect
3193-
3194-class NoSuchHotfix(Exception):
3195- """
3196- Man you must be pretty stupid.
3197- """
3198-
3199-_alreadyInstalled = set()
3200-def require(packageName, fixName):
3201- if (packageName, fixName) in _alreadyInstalled:
3202- return
3203-
3204- if (packageName, fixName) == ('twisted', 'filepath_copyTo'):
3205- from twisted.python import filepath
3206- if filepath.FilePath('a') != filepath.FilePath('a'):
3207- from epsilon.hotfixes import filepath_copyTo
3208- filepath_copyTo.install()
3209- elif (packageName, fixName) == ('twisted', 'timeoutmixin_calllater'):
3210- from twisted.protocols import policies
3211- if not hasattr(policies.TimeoutMixin, 'callLater'):
3212- from epsilon.hotfixes import timeoutmixin_calllater
3213- timeoutmixin_calllater.install()
3214- elif (packageName, fixName) == ('twisted', 'delayedcall_seconds'):
3215- from twisted.internet import base
3216- args = inspect.getargs(base.DelayedCall.__init__.func_code)[0]
3217- if 'seconds' not in args:
3218- from epsilon.hotfixes import delayedcall_seconds
3219- delayedcall_seconds.install()
3220- elif (packageName, fixName) == ('twisted', 'deferredgenerator_tfailure'):
3221- from twisted.internet import defer
3222- result = []
3223- def test():
3224- d = defer.waitForDeferred(defer.succeed(1))
3225- yield d
3226- result.append(d.getResult())
3227- defer.deferredGenerator(test)()
3228- if result == [1]:
3229- from epsilon.hotfixes import deferredgenerator_tfailure
3230- deferredgenerator_tfailure.install()
3231- else:
3232- assert result == [None]
3233- elif (packageName, fixName) == ("twisted", "proto_helpers_stringtransport"):
3234- from twisted.test.proto_helpers import StringTransport
3235- st = StringTransport()
3236- try:
3237- st.write(u'foo')
3238- except TypeError, e:
3239- pass
3240- else:
3241- from epsilon.hotfixes import proto_helpers_stringtransport
3242- proto_helpers_stringtransport.install()
3243- elif (packageName, fixName) == ("twisted", "internet_task_Clock"):
3244- from twisted.internet.task import Clock
3245- from twisted.internet import base
3246- from twisted import version
3247- from epsilon.hotfixes import internet_task_clock
3248- if internet_task_clock.clockIsBroken():
3249- internet_task_clock.install()
3250- elif (packageName, fixName) == ("twisted", "trial_assertwarns"):
3251- from twisted.trial.unittest import TestCase
3252- if not hasattr(TestCase, "failUnlessWarns"):
3253- from epsilon.hotfixes import trial_assertwarns
3254- trial_assertwarns.install()
3255- elif (packageName, fixName) == ("twisted", "plugin_package_paths"):
3256- try:
3257- from twisted.plugin import pluginPackagePaths
3258- except ImportError:
3259- from epsilon.hotfixes import plugin_package_paths
3260- plugin_package_paths.install()
3261- elif (packageName, fixName) == ("twisted", "loopbackasync_reentrancy"):
3262- # This one is really hard to detect reasonably. Invoking the code
3263- # involves triggering the reactor, which it would be good to avoid.
3264- from twisted import version
3265- if (version.major, version.minor) < (8, 2):
3266- from epsilon.hotfixes import loopbackasync_reentrancy
3267- loopbackasync_reentrancy.install()
3268- else:
3269- raise NoSuchHotfix(packageName, fixName)
3270-
3271- _alreadyInstalled.add((packageName, fixName))
3272
3273=== removed directory 'Epsilon/epsilon/hotfixes'
3274=== removed file 'Epsilon/epsilon/hotfixes/__init__.py'
3275=== removed file 'Epsilon/epsilon/hotfixes/deferredgenerator_tfailure.py'
3276--- Epsilon/epsilon/hotfixes/deferredgenerator_tfailure.py 2006-05-22 15:35:56 +0000
3277+++ Epsilon/epsilon/hotfixes/deferredgenerator_tfailure.py 1970-01-01 00:00:00 +0000
3278@@ -1,59 +0,0 @@
3279-
3280-from twisted.python import failure
3281-from twisted.internet import defer
3282-
3283-def getResult(self):
3284- if isinstance(self.result, failure.Failure):
3285- self.result.raiseException()
3286- return self.result
3287-
3288-
3289-def _deferGenerator(g, deferred=None):
3290- """
3291- See L{waitForDeferred}.
3292- """
3293- result = None
3294- while 1:
3295- if deferred is None:
3296- deferred = defer.Deferred()
3297- try:
3298- result = g.next()
3299- except StopIteration:
3300- deferred.callback(result)
3301- return deferred
3302- except:
3303- deferred.errback()
3304- return deferred
3305-
3306- # Deferred.callback(Deferred) raises an error; we catch this case
3307- # early here and give a nicer error message to the user in case
3308- # they yield a Deferred. Perhaps eventually these semantics may
3309- # change.
3310- if isinstance(result, defer.Deferred):
3311- return defer.fail(TypeError("Yield waitForDeferred(d), not d!"))
3312-
3313- if isinstance(result, defer.waitForDeferred):
3314- waiting = [True, None]
3315- # Pass vars in so they don't get changed going around the loop
3316- def gotResult(r, waiting=waiting, result=result):
3317- result.result = r
3318- if waiting[0]:
3319- waiting[0] = False
3320- waiting[1] = r
3321- else:
3322- _deferGenerator(g, deferred)
3323- result.d.addBoth(gotResult)
3324- if waiting[0]:
3325- # Haven't called back yet, set flag so that we get reinvoked
3326- # and return from the loop
3327- waiting[0] = False
3328- return deferred
3329- result = None # waiting[1]
3330-
3331-
3332-def install():
3333- getResult.__module__ = 'twisted.internet.defer'
3334- defer.waitForDeferred.getResult = getResult
3335-
3336- _deferGenerator.__module__ = 'twisted.internet.defer'
3337- defer._deferGenerator = _deferGenerator
3338
3339=== removed file 'Epsilon/epsilon/hotfixes/delayedcall_seconds.py'
3340--- Epsilon/epsilon/hotfixes/delayedcall_seconds.py 2006-05-19 15:23:46 +0000
3341+++ Epsilon/epsilon/hotfixes/delayedcall_seconds.py 1970-01-01 00:00:00 +0000
3342@@ -1,160 +0,0 @@
3343-
3344-import traceback
3345-
3346-from zope.interface import implements
3347-
3348-from twisted.persisted import styles
3349-from twisted.internet.interfaces import IDelayedCall
3350-from twisted.internet import error, base
3351-from twisted.python import reflect
3352-
3353-class DelayedCall(styles.Ephemeral):
3354-
3355- implements(IDelayedCall)
3356- # enable .debug to record creator call stack, and it will be logged if
3357- # an exception occurs while the function is being run
3358- debug = False
3359- _str = None
3360-
3361- def __init__(self, time, func, args, kw, cancel, reset, seconds=None):
3362- self.time, self.func, self.args, self.kw = time, func, args, kw
3363- self.resetter = reset
3364- self.canceller = cancel
3365- self.seconds = seconds
3366- self.cancelled = self.called = 0
3367- self.delayed_time = 0
3368- if self.debug:
3369- self.creator = traceback.format_stack()[:-2]
3370-
3371- def getTime(self):
3372- """Return the time at which this call will fire
3373-
3374- @rtype: C{float}
3375- @return: The number of seconds after the epoch at which this call is
3376- scheduled to be made.
3377- """
3378- return self.time + self.delayed_time
3379-
3380- def cancel(self):
3381- """Unschedule this call
3382-
3383- @raise AlreadyCancelled: Raised if this call has already been
3384- unscheduled.
3385-
3386- @raise AlreadyCalled: Raised if this call has already been made.
3387- """
3388- if self.cancelled:
3389- raise error.AlreadyCancelled
3390- elif self.called:
3391- raise error.AlreadyCalled
3392- else:
3393- self.canceller(self)
3394- self.cancelled = 1
3395- if self.debug:
3396- self._str = str(self)
3397- del self.func, self.args, self.kw
3398-
3399- def reset(self, secondsFromNow):
3400- """Reschedule this call for a different time
3401-
3402- @type secondsFromNow: C{float}
3403- @param secondsFromNow: The number of seconds from the time of the
3404- C{reset} call at which this call will be scheduled.
3405-
3406- @raise AlreadyCancelled: Raised if this call has been cancelled.
3407- @raise AlreadyCalled: Raised if this call has already been made.
3408- """
3409- if self.cancelled:
3410- raise error.AlreadyCancelled
3411- elif self.called:
3412- raise error.AlreadyCalled
3413- else:
3414- if self.seconds is None:
3415- new_time = base.seconds() + secondsFromNow
3416- else:
3417- new_time = self.seconds() + secondsFromNow
3418- if new_time < self.time:
3419- self.delayed_time = 0
3420- self.time = new_time
3421- self.resetter(self)
3422- else:
3423- self.delayed_time = new_time - self.time
3424-
3425- def delay(self, secondsLater):
3426- """Reschedule this call for a later time
3427-
3428- @type secondsLater: C{float}
3429- @param secondsLater: The number of seconds after the originally
3430- scheduled time for which to reschedule this call.
3431-
3432- @raise AlreadyCancelled: Raised if this call has been cancelled.
3433- @raise AlreadyCalled: Raised if this call has already been made.
3434- """
3435- if self.cancelled:
3436- raise error.AlreadyCancelled
3437- elif self.called:
3438- raise error.AlreadyCalled
3439- else:
3440- self.delayed_time += secondsLater
3441- if self.delayed_time < 0:
3442- self.activate_delay()
3443- self.resetter(self)
3444-
3445- def activate_delay(self):
3446- self.time += self.delayed_time
3447- self.delayed_time = 0
3448-
3449- def active(self):
3450- """Determine whether this call is still pending
3451-
3452- @rtype: C{bool}
3453- @return: True if this call has not yet been made or cancelled,
3454- False otherwise.
3455- """
3456- return not (self.cancelled or self.called)
3457-
3458- def __le__(self, other):
3459- return self.time <= other.time
3460-
3461- def __str__(self):
3462- if self._str is not None:
3463- return self._str
3464- if hasattr(self, 'func'):
3465- if hasattr(self.func, 'func_name'):
3466- func = self.func.func_name
3467- if hasattr(self.func, 'im_class'):
3468- func = self.func.im_class.__name__ + '.' + func
3469- else:
3470- func = reflect.safe_repr(self.func)
3471- else:
3472- func = None
3473-
3474- if self.seconds is None:
3475- now = base.seconds()
3476- else:
3477- now = self.seconds()
3478- L = ["<DelayedCall %s [%ss] called=%s cancelled=%s" % (
3479- id(self), self.time - now, self.called, self.cancelled)]
3480- if func is not None:
3481- L.extend((" ", func, "("))
3482- if self.args:
3483- L.append(", ".join([reflect.safe_repr(e) for e in self.args]))
3484- if self.kw:
3485- L.append(", ")
3486- if self.kw:
3487- L.append(", ".join(['%s=%s' % (k, reflect.safe_repr(v)) for (k, v) in self.kw.iteritems()]))
3488- L.append(")")
3489-
3490- if self.debug:
3491- L.append("\n\ntraceback at creation: \n\n%s" % (' '.join(self.creator)))
3492- L.append('>')
3493-
3494- return "".join(L)
3495-
3496-
3497-def install():
3498- global DelayedCall
3499-
3500- base.DelayedCall.__dict__ = DelayedCall.__dict__
3501- base.DelayedCall.__dict__['__module__'] = 'twisted.internet.base'
3502- DelayedCall = base.DelayedCall
3503
3504=== removed file 'Epsilon/epsilon/hotfixes/filepath_copyTo.py'
3505--- Epsilon/epsilon/hotfixes/filepath_copyTo.py 2005-12-06 07:36:12 +0000
3506+++ Epsilon/epsilon/hotfixes/filepath_copyTo.py 1970-01-01 00:00:00 +0000
3507@@ -1,403 +0,0 @@
3508-# -*- test-case-name: twisted.test.test_paths -*-
3509-# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3510-# See LICENSE for details.
3511-
3512-from __future__ import generators
3513-
3514-import os
3515-import errno
3516-import base64
3517-import random
3518-import sha
3519-
3520-from os.path import isabs, exists, normpath, abspath, splitext
3521-from os.path import basename, dirname
3522-from os.path import join as joinpath
3523-from os import sep as slash
3524-from os import listdir, utime, stat
3525-from os import remove
3526-
3527-from stat import ST_MODE, ST_MTIME, ST_ATIME, ST_CTIME, ST_SIZE
3528-
3529-from stat import S_ISREG, S_ISDIR, S_ISLNK
3530-
3531-try:
3532- from os.path import islink
3533-except ImportError:
3534- def islink(path):
3535- return False
3536-
3537-try:
3538- from os import urandom as randomBytes
3539-except ImportError:
3540- def randomBytes(n):
3541- randomData = [random.randrange(256) for n in xrange(n)]
3542- return ''.join(map(chr, randomData))
3543-
3544-try:
3545- from base64 import urlsafe_b64encode as armor
3546-except ImportError:
3547- def armor(s):
3548- return s.encode('hex')
3549-
3550-class InsecurePath(Exception):
3551- pass
3552-
3553-def _secureEnoughString():
3554- """
3555- Create a pseudorandom, 16-character string for use in secure filenames.
3556- """
3557- return armor(sha.new(randomBytes(64)).digest())[:16]
3558-
3559-class FilePath:
3560- """I am a path on the filesystem that only permits 'downwards' access.
3561-
3562- Instantiate me with a pathname (for example,
3563- FilePath('/home/myuser/public_html')) and I will attempt to only provide
3564- access to files which reside inside that path. I may be a path to a file,
3565- a directory, or a file which does not exist.
3566-
3567- The correct way to use me is to instantiate me, and then do ALL filesystem
3568- access through me. In other words, do not import the 'os' module; if you
3569- need to open a file, call my 'open' method. If you need to list a
3570- directory, call my 'path' method.
3571-
3572- Even if you pass me a relative path, I will convert that to an absolute
3573- path internally.
3574-
3575- @type alwaysCreate: C{bool}
3576- @ivar alwaysCreate: When opening this file, only succeed if the file does not
3577- already exist.
3578- """
3579-
3580- # __slots__ = 'path abs'.split()
3581-
3582- statinfo = None
3583-
3584- def __init__(self, path, alwaysCreate=False):
3585- self.path = abspath(path)
3586- self.alwaysCreate = alwaysCreate
3587-
3588- def __getstate__(self):
3589- d = self.__dict__.copy()
3590- if d.has_key('statinfo'):
3591- del d['statinfo']
3592- return d
3593-
3594- def child(self, path):
3595- norm = normpath(path)
3596- if slash in norm:
3597- raise InsecurePath("%r contains one or more directory separators" % (path,))
3598- newpath = abspath(joinpath(self.path, norm))
3599- if not newpath.startswith(self.path):
3600- raise InsecurePath("%r is not a child of %s" % (newpath, self.path))
3601- return self.clonePath(newpath)
3602-
3603- def preauthChild(self, path):
3604- """
3605- Use me if `path' might have slashes in it, but you know they're safe.
3606-
3607- (NOT slashes at the beginning. It still needs to be a _child_).
3608- """
3609- newpath = abspath(joinpath(self.path, normpath(path)))
3610- if not newpath.startswith(self.path):
3611- raise InsecurePath("%s is not a child of %s" % (newpath, self.path))
3612- return self.clonePath(newpath)
3613-
3614- def childSearchPreauth(self, *paths):
3615- """Return my first existing child with a name in 'paths'.
3616-
3617- paths is expected to be a list of *pre-secured* path fragments; in most
3618- cases this will be specified by a system administrator and not an
3619- arbitrary user.
3620-
3621- If no appropriately-named children exist, this will return None.
3622- """
3623- p = self.path
3624- for child in paths:
3625- jp = joinpath(p, child)
3626- if exists(jp):
3627- return self.clonePath(jp)
3628-
3629- def siblingExtensionSearch(self, *exts):
3630- """Attempt to return a path with my name, given multiple possible
3631- extensions.
3632-
3633- Each extension in exts will be tested and the first path which exists
3634- will be returned. If no path exists, None will be returned. If '' is
3635- in exts, then if the file referred to by this path exists, 'self' will
3636- be returned.
3637-
3638- The extension '*' has a magic meaning, which means "any path that
3639- begins with self.path+'.' is acceptable".
3640- """
3641- p = self.path
3642- for ext in exts:
3643- if not ext and self.exists():
3644- return self
3645- if ext == '*':
3646- basedot = basename(p)+'.'
3647- for fn in listdir(dirname(p)):
3648- if fn.startswith(basedot):
3649- return self.clonePath(joinpath(dirname(p), fn))
3650- p2 = p + ext
3651- if exists(p2):
3652- return self.clonePath(p2)
3653-
3654- def siblingExtension(self, ext):
3655- return self.clonePath(self.path+ext)
3656-
3657- def open(self, mode='r'):
3658- if self.alwaysCreate:
3659- assert 'a' not in mode, "Appending not supported when alwaysCreate == True"
3660- return self.create()
3661- return open(self.path, mode+'b')
3662-
3663- # stat methods below
3664-
3665- def restat(self, reraise=True):
3666- try:
3667- self.statinfo = stat(self.path)
3668- except OSError:
3669- self.statinfo = 0
3670- if reraise:
3671- raise
3672-
3673- def getsize(self):
3674- st = self.statinfo
3675- if not st:
3676- self.restat()
3677- st = self.statinfo
3678- return st[ST_SIZE]
3679-
3680- def getmtime(self):
3681- st = self.statinfo
3682- if not st:
3683- self.restat()
3684- st = self.statinfo
3685- return st[ST_MTIME]
3686-
3687- def getctime(self):
3688- st = self.statinfo
3689- if not st:
3690- self.restat()
3691- st = self.statinfo
3692- return st[ST_CTIME]
3693-
3694- def getatime(self):
3695- st = self.statinfo
3696- if not st:
3697- self.restat()
3698- st = self.statinfo
3699- return st[ST_ATIME]
3700-
3701- def exists(self):
3702- if self.statinfo:
3703- return True
3704- elif self.statinfo is None:
3705- self.restat(False)
3706- return self.exists()
3707- else:
3708- return False
3709-
3710- def isdir(self):
3711- st = self.statinfo
3712- if not st:
3713- self.restat(False)
3714- st = self.statinfo
3715- if not st:
3716- return False
3717- return S_ISDIR(st[ST_MODE])
3718-
3719- def isfile(self):
3720- st = self.statinfo
3721- if not st:
3722- self.restat(False)
3723- st = self.statinfo
3724- if not st:
3725- return False
3726- return S_ISREG(st[ST_MODE])
3727-
3728- def islink(self):
3729- st = self.statinfo
3730- if not st:
3731- self.restat(False)
3732- st = self.statinfo
3733- if not st:
3734- return False
3735- return S_ISLNK(st[ST_MODE])
3736-
3737- def isabs(self):
3738- return isabs(self.path)
3739-
3740- def listdir(self):
3741- return listdir(self.path)
3742-
3743- def splitext(self):
3744- return splitext(self.path)
3745-
3746- def __repr__(self):
3747- return 'FilePath(%r)' % self.path
3748-
3749- def touch(self):
3750- try:
3751- self.open('a').close()
3752- except IOError:
3753- pass
3754- utime(self.path, None)
3755-
3756- def remove(self):
3757- if self.isdir():
3758- for child in self.children():
3759- child.remove()
3760- os.rmdir(self.path)
3761- else:
3762- os.remove(self.path)
3763- self.restat(False)
3764-
3765- def makedirs(self):
3766- return os.makedirs(self.path)
3767-
3768- def globChildren(self, pattern):
3769- """
3770- Assuming I am representing a directory, return a list of
3771- FilePaths representing my children that match the given
3772- pattern.
3773- """
3774- import glob
3775- path = self.path[-1] == '/' and self.path + pattern or slash.join([self.path, pattern])
3776- return map(self.clonePath, glob.glob(path))
3777-
3778- def basename(self):
3779- return basename(self.path)
3780-
3781- def dirname(self):
3782- return dirname(self.path)
3783-
3784- def parent(self):
3785- return self.clonePath(self.dirname())
3786-
3787- def setContent(self, content, ext='.new'):
3788- sib = self.siblingExtension(ext)
3789- sib.open('w').write(content)
3790- os.rename(sib.path, self.path)
3791-
3792- def getContent(self):
3793- return self.open().read()
3794-
3795- # new in 2.2.0
3796-
3797- def __cmp__(self, other):
3798- if not isinstance(other, FilePath):
3799- return NotImplemented
3800- return cmp(self.path, other.path)
3801-
3802- def createDirectory(self):
3803- os.mkdir(self.path)
3804-
3805- def requireCreate(self, val=1):
3806- self.alwaysCreate = val
3807-
3808- def create(self):
3809- """Exclusively create a file, only if this file previously did not exist.
3810- """
3811- fdint = os.open(self.path, (os.O_EXCL |
3812- os.O_CREAT |
3813- os.O_RDWR))
3814-
3815- # XXX TODO: 'name' attribute of returned files is not mutable or
3816- # settable via fdopen, so this file is slighly less functional than the
3817- # one returned from 'open' by default. send a patch to Python...
3818-
3819- return os.fdopen(fdint, 'w+b')
3820-
3821- def temporarySibling(self):
3822- """
3823- Create a path naming a temporary sibling of this path in a secure fashion.
3824- """
3825- sib = self.parent().child(_secureEnoughString() + self.basename())
3826- sib.requireCreate()
3827- return sib
3828-
3829- def children(self):
3830- return map(self.child, self.listdir())
3831-
3832- def walk(self):
3833- yield self
3834- if self.isdir():
3835- for c in self.children():
3836- for subc in c.walk():
3837- yield subc
3838-
3839- _chunkSize = 2 ** 2 ** 2 ** 2
3840-
3841- def copyTo(self, destination):
3842- # XXX TODO: *thorough* audit and documentation of the exact desired
3843- # semantics of this code. Right now the behavior of existent
3844- # destination symlinks is convenient, and quite possibly correct, but
3845- # its security properties need to be explained.
3846- if self.isdir():
3847- if not destination.exists():
3848- destination.createDirectory()
3849- for child in self.children():
3850- destChild = destination.child(child.basename())
3851- child.copyTo(destChild)
3852- elif self.isfile():
3853- writefile = destination.open('w')
3854- readfile = self.open()
3855- while 1:
3856- # XXX TODO: optionally use os.open, os.read and O_DIRECT and
3857- # use os.fstatvfs to determine chunk sizes and make
3858- # *****sure**** copy is page-atomic; the following is good
3859- # enough for 99.9% of everybody and won't take a week to audit
3860- # though.
3861- chunk = readfile.read(self._chunkSize)
3862- writefile.write(chunk)
3863- if len(chunk) < self._chunkSize:
3864- break
3865- writefile.close()
3866- readfile.close()
3867- else:
3868- # If you see the following message because you want to copy
3869- # symlinks, fifos, block devices, character devices, or unix
3870- # sockets, please feel free to add support to do sensible things in
3871- # reaction to those types!
3872- raise NotImplementedError(
3873- "Only copying of files and directories supported")
3874-
3875- def moveTo(self, destination):
3876- try:
3877- os.rename(self.path, destination.path)
3878- self.restat(False)
3879- except OSError, ose:
3880- if ose.errno == errno.EXDEV:
3881- # man 2 rename, ubuntu linux 5.10 "breezy":
3882-
3883- # oldpath and newpath are not on the same mounted filesystem.
3884- # (Linux permits a filesystem to be mounted at multiple
3885- # points, but rename(2) does not work across different mount
3886- # points, even if the same filesystem is mounted on both.)
3887-
3888- # that means it's time to copy trees of directories!
3889- secsib = destination.secureSibling()
3890- self.copyTo(secsib) # slow
3891- secsib.moveTo(destination) # visible
3892-
3893- # done creating new stuff. let's clean me up.
3894- mysecsib = self.secureSibling()
3895- self.moveTo(mysecsib) # visible
3896- mysecsib.remove() # slow
3897- else:
3898- raise
3899-
3900-
3901-FilePath.clonePath = FilePath
3902-
3903-
3904-def install():
3905- global FilePath
3906-
3907- from twisted.python import filepath
3908- filepath.FilePath.__dict__ = FilePath.__dict__
3909- filepath.FilePath.__dict__['__module__'] = 'twisted.python.filepath'
3910- FilePath = filepath.FilePath
3911
3912=== removed file 'Epsilon/epsilon/hotfixes/internet_task_clock.py'
3913--- Epsilon/epsilon/hotfixes/internet_task_clock.py 2007-06-22 20:06:46 +0000
3914+++ Epsilon/epsilon/hotfixes/internet_task_clock.py 1970-01-01 00:00:00 +0000
3915@@ -1,38 +0,0 @@
3916-"""
3917-Fix from Twisted r20480.
3918-"""
3919-from twisted.internet.task import Clock
3920-from twisted.internet import base
3921-
3922-def callLater(self, when, what, *a, **kw):
3923- """
3924- Copied from twisted.internet.task.Clock, r20480. Fixes the bug
3925- where the wrong DelayedCall would sometimes be returned.
3926- """
3927- dc = base.DelayedCall(self.seconds() + when,
3928- what, a, kw,
3929- self.calls.remove,
3930- lambda c: None,
3931- self.seconds)
3932- self.calls.append(dc)
3933- self.calls.sort(lambda a, b: cmp(a.getTime(), b.getTime()))
3934- return dc
3935-
3936-def clockIsBroken():
3937- """
3938- Returns whether twisted.internet.task.Clock has the bug that
3939- returns the wrong DelayedCall or not.
3940- """
3941- clock = Clock()
3942- dc1 = clock.callLater(10, lambda: None)
3943- dc2 = clock.callLater(1, lambda: None)
3944- if dc1 is dc2:
3945- return True
3946- else:
3947- return False
3948-
3949-def install():
3950- """
3951- Insert the fixed callLater method.
3952- """
3953- Clock.callLater = callLater
3954
3955=== removed file 'Epsilon/epsilon/hotfixes/loopbackasync_reentrancy.py'
3956--- Epsilon/epsilon/hotfixes/loopbackasync_reentrancy.py 2008-08-28 14:40:39 +0000
3957+++ Epsilon/epsilon/hotfixes/loopbackasync_reentrancy.py 1970-01-01 00:00:00 +0000
3958@@ -1,26 +0,0 @@
3959-
3960-"""
3961-Fix from Twisted r23970
3962-"""
3963-
3964-from twisted.internet.task import deferLater
3965-from twisted.protocols.loopback import _loopbackAsyncBody
3966-
3967-def _loopbackAsyncContinue(ignored, server, serverToClient, client, clientToServer):
3968- # Clear the Deferred from each message queue, since it has already fired
3969- # and cannot be used again.
3970- clientToServer._notificationDeferred = serverToClient._notificationDeferred = None
3971-
3972- # Schedule some more byte-pushing to happen. This isn't done
3973- # synchronously because no actual transport can re-enter dataReceived as
3974- # a result of calling write, and doing this synchronously could result
3975- # in that.
3976- from twisted.internet import reactor
3977- return deferLater(
3978- reactor, 0,
3979- _loopbackAsyncBody, server, serverToClient, client, clientToServer)
3980-
3981-
3982-def install():
3983- from twisted.protocols import loopback
3984- loopback._loopbackAsyncContinue = _loopbackAsyncContinue
3985
3986=== removed file 'Epsilon/epsilon/hotfixes/plugin_package_paths.py'
3987--- Epsilon/epsilon/hotfixes/plugin_package_paths.py 2008-08-07 14:03:07 +0000
3988+++ Epsilon/epsilon/hotfixes/plugin_package_paths.py 1970-01-01 00:00:00 +0000
3989@@ -1,42 +0,0 @@
3990-# Copyright (c) 2007 Twisted Matrix Laboratories.
3991-# Copyright (c) 2008 Divmod.
3992-# See LICENSE for details.
3993-
3994-
3995-
3996-import sys, os
3997-
3998-def pluginPackagePaths(name):
3999- """
4000- Return a list of additional directories which should be searched for
4001- modules to be included as part of the named plugin package.
4002-
4003- @type name: C{str}
4004- @param name: The fully-qualified Python name of a plugin package, eg
4005- C{'twisted.plugins'}.
4006-
4007- @rtype: C{list} of C{str}
4008- @return: The absolute paths to other directories which may contain plugin
4009- modules for the named plugin package.
4010- """
4011- package = name.split('.')
4012- # Note that this may include directories which do not exist. It may be
4013- # preferable to remove such directories at this point, rather than allow
4014- # them to be searched later on.
4015- #
4016- # Note as well that only '__init__.py' will be considered to make a
4017- # directory a package (and thus exclude it from this list). This means
4018- # that if you create a master plugin package which has some other kind of
4019- # __init__ (eg, __init__.pyc) it will be incorrectly treated as a
4020- # supplementary plugin directory.
4021- return [
4022- os.path.abspath(os.path.join(x, *package))
4023- for x
4024- in sys.path
4025- if
4026- not os.path.exists(os.path.join(x, *package + ['__init__.py']))]
4027-
4028-
4029-def install():
4030- import twisted.plugin
4031- twisted.plugin.pluginPackagePaths = pluginPackagePaths
4032
4033=== removed file 'Epsilon/epsilon/hotfixes/proto_helpers_stringtransport.py'
4034--- Epsilon/epsilon/hotfixes/proto_helpers_stringtransport.py 2006-06-22 20:48:07 +0000
4035+++ Epsilon/epsilon/hotfixes/proto_helpers_stringtransport.py 1970-01-01 00:00:00 +0000
4036@@ -1,11 +0,0 @@
4037-from twisted.test import proto_helpers
4038-
4039-class StringTransport:
4040-
4041- def write(self, data):
4042- if isinstance(data, unicode): # no, really, I mean it
4043- raise TypeError("Data must not be unicode")
4044- self.io.write(data)
4045-
4046-def install():
4047- proto_helpers.StringTransport.__dict__['write'] = StringTransport.__dict__['write']
4048
4049=== removed file 'Epsilon/epsilon/hotfixes/timeoutmixin_calllater.py'
4050--- Epsilon/epsilon/hotfixes/timeoutmixin_calllater.py 2006-05-19 15:23:46 +0000
4051+++ Epsilon/epsilon/hotfixes/timeoutmixin_calllater.py 1970-01-01 00:00:00 +0000
4052@@ -1,60 +0,0 @@
4053-
4054-from twisted.internet import reactor
4055-
4056-class TimeoutMixin:
4057- """Mixin for protocols which wish to timeout connections
4058-
4059- @cvar timeOut: The number of seconds after which to timeout the connection.
4060- """
4061- timeOut = None
4062-
4063- __timeoutCall = None
4064-
4065- def callLater(self, period, func):
4066- return reactor.callLater(period, func)
4067-
4068-
4069- def resetTimeout(self):
4070- """Reset the timeout count down"""
4071- if self.__timeoutCall is not None and self.timeOut is not None:
4072- self.__timeoutCall.reset(self.timeOut)
4073-
4074- def setTimeout(self, period):
4075- """Change the timeout period
4076-
4077- @type period: C{int} or C{NoneType}
4078- @param period: The period, in seconds, to change the timeout to, or
4079- C{None} to disable the timeout.
4080- """
4081- prev = self.timeOut
4082- self.timeOut = period
4083-
4084- if self.__timeoutCall is not None:
4085- if period is None:
4086- self.__timeoutCall.cancel()
4087- self.__timeoutCall = None
4088- else:
4089- self.__timeoutCall.reset(period)
4090- elif period is not None:
4091- self.__timeoutCall = self.callLater(period, self.__timedOut)
4092-
4093- return prev
4094-
4095- def __timedOut(self):
4096- self.__timeoutCall = None
4097- self.timeoutConnection()
4098-
4099- def timeoutConnection(self):
4100- """Called when the connection times out.
4101- Override to define behavior other than dropping the connection.
4102- """
4103- self.transport.loseConnection()
4104-
4105-
4106-def install():
4107- global TimeoutMixin
4108-
4109- from twisted.protocols import policies
4110- policies.TimeoutMixin.__dict__ = TimeoutMixin.__dict__
4111- policies.TimeoutMixin.__dict__['module'] = 'twisted.protocols.policies'
4112- TimeoutMixin = policies.TimeoutMixin
4113
4114=== removed file 'Epsilon/epsilon/hotfixes/trial_assertwarns.py'
4115--- Epsilon/epsilon/hotfixes/trial_assertwarns.py 2008-05-13 19:40:37 +0000
4116+++ Epsilon/epsilon/hotfixes/trial_assertwarns.py 1970-01-01 00:00:00 +0000
4117@@ -1,61 +0,0 @@
4118-
4119-"""
4120-failUnlessWarns assertion from twisted.trial in Twisted 8.0.
4121-"""
4122-
4123-import warnings
4124-
4125-def failUnlessWarns(self, category, message, filename, f,
4126- *args, **kwargs):
4127- """
4128- Fail if the given function doesn't generate the specified warning when
4129- called. It calls the function, checks the warning, and forwards the
4130- result of the function if everything is fine.
4131-
4132- @param category: the category of the warning to check.
4133- @param message: the output message of the warning to check.
4134- @param filename: the filename where the warning should come from.
4135- @param f: the function which is supposed to generate the warning.
4136- @type f: any callable.
4137- @param args: the arguments to C{f}.
4138- @param kwargs: the keywords arguments to C{f}.
4139-
4140- @return: the result of the original function C{f}.
4141- """
4142- warningsShown = []
4143- def warnExplicit(*args):
4144- warningsShown.append(args)
4145-
4146- origExplicit = warnings.warn_explicit
4147- try:
4148- warnings.warn_explicit = warnExplicit
4149- result = f(*args, **kwargs)
4150- finally:
4151- warnings.warn_explicit = origExplicit
4152-
4153- if not warningsShown:
4154- self.fail("No warnings emitted")
4155- first = warningsShown[0]
4156- for other in warningsShown[1:]:
4157- if other[:2] != first[:2]:
4158- self.fail("Can't handle different warnings")
4159- gotMessage, gotCategory, gotFilename, lineno = first[:4]
4160- self.assertEqual(gotMessage, message)
4161- self.assertIdentical(gotCategory, category)
4162-
4163- # Use starts with because of .pyc/.pyo issues.
4164- self.failUnless(
4165- filename.startswith(gotFilename),
4166- 'Warning in %r, expected %r' % (gotFilename, filename))
4167-
4168- # It would be nice to be able to check the line number as well, but
4169- # different configurations actually end up reporting different line
4170- # numbers (generally the variation is only 1 line, but that's enough
4171- # to fail the test erroneously...).
4172- # self.assertEqual(lineno, xxx)
4173-
4174- return result
4175-
4176-def install():
4177- from twisted.trial.unittest import TestCase
4178- TestCase.failUnlessWarns = TestCase.assertWarns = failUnlessWarns
4179
4180=== removed file 'Epsilon/epsilon/iepsilon.py'
4181--- Epsilon/epsilon/iepsilon.py 2008-11-10 20:26:55 +0000
4182+++ Epsilon/epsilon/iepsilon.py 1970-01-01 00:00:00 +0000
4183@@ -1,25 +0,0 @@
4184-# Copyright (c) 2008 Divmod. See LICENSE for details.
4185-
4186-"""
4187-Epsilon interfaces.
4188-"""
4189-from zope.interface import Attribute
4190-
4191-from twisted.cred.credentials import ICredentials
4192-
4193-
4194-class IOneTimePad(ICredentials):
4195- """
4196- A type of opaque credential for authenticating users, which can be used
4197- only a single time.
4198-
4199- This interface should also be responsible for authenticating. See #2784.
4200- """
4201- padValue = Attribute(
4202- """
4203- C{str} giving the value of the one-time pad. The value will be
4204- compared by a L{twisted.cred.checkers.ICredentialsChecker} (e.g.
4205- L{epsilon.ampauth.OneTimePadChecker}) against all valid one-time pads.
4206- If there is a match, login will be successful and the pad will be
4207- invalidated (further attempts to use it will fail).
4208- """)
4209
4210=== removed file 'Epsilon/epsilon/juice.py'
4211--- Epsilon/epsilon/juice.py 2009-07-06 11:51:08 +0000
4212+++ Epsilon/epsilon/juice.py 1970-01-01 00:00:00 +0000
4213@@ -1,1009 +0,0 @@
4214-# -*- test-case-name: epsilon.test.test_juice -*-
4215-# Copyright 2005 Divmod, Inc. See LICENSE file for details
4216-
4217-__metaclass__ = type
4218-
4219-import warnings, pprint
4220-
4221-from twisted.internet.main import CONNECTION_LOST
4222-from twisted.internet.defer import Deferred, maybeDeferred, fail
4223-from twisted.internet.protocol import ServerFactory, ClientFactory
4224-from twisted.internet.ssl import Certificate
4225-from twisted.python.failure import Failure
4226-from twisted.python import log, filepath
4227-
4228-from epsilon.liner import LineReceiver
4229-from epsilon import extime
4230-
4231-ASK = '_ask'
4232-ANSWER = '_answer'
4233-COMMAND = '_command'
4234-ERROR = '_error'
4235-ERROR_CODE = '_error_code'
4236-ERROR_DESCRIPTION = '_error_description'
4237-LENGTH = '_length'
4238-BODY = 'body'
4239-
4240-debug = False
4241-
4242-class JuiceBox(dict):
4243- """ I am a packet in the JUICE protocol. """
4244-
4245- def __init__(self, __body='', **kw):
4246- self.update(kw)
4247- if __body:
4248- assert isinstance(__body, str), "body must be a string: %r" % ( repr(__body),)
4249- self['body'] = __body
4250-
4251- def body():
4252- def get(self):
4253- warnings.warn("body attribute of boxes is now just a regular field",
4254- stacklevel=2)
4255- return self['body']
4256- def set(self, newbody):
4257- warnings.warn("body attribute of boxes is now just a regular field",
4258- stacklevel=2)
4259- self['body'] = newbody
4260- return get,set
4261- body = property(*body())
4262-
4263- def copy(self):
4264- newBox = self.__class__()
4265- newBox.update(self)
4266- return newBox
4267-
4268- def serialize(self,
4269- delimiter='\r\n',
4270- escaped='\r\n '):
4271- assert LENGTH not in self
4272-
4273- L = []
4274- for (k, v) in self.iteritems():
4275- if k == BODY:
4276- k = LENGTH
4277- v = str(len(self[BODY]))
4278- L.append(k.replace('_', '-').title())
4279- L.append(': ')
4280- L.append(v.replace(delimiter, escaped))
4281- L.append(delimiter)
4282-
4283- L.append(delimiter)
4284- if BODY in self:
4285- L.append(self[BODY])
4286-
4287- bytes = ''.join(L)
4288- return bytes
4289-
4290- def sendTo(self, proto):
4291- """
4292- Serialize and send this box to a Juice instance. By the time it is
4293- being sent, several keys are required. I must have exactly ONE of::
4294-
4295- -ask
4296- -answer
4297- -error
4298-
4299- If the '-ask' header is set, then the '-command' header must also be
4300- set.
4301- """
4302- proto.sendPacket(self)
4303-
4304-# juice.Box => JuiceBox
4305-
4306-Box = JuiceBox
4307-
4308-class TLSBox(JuiceBox):
4309- def __repr__(self):
4310- return 'TLS(**%s)' % (super(TLSBox, self).__repr__(),)
4311-
4312-
4313- def __init__(self, __certificate, __verify=None, __sslstarted=None, **kw):
4314- super(TLSBox, self).__init__(**kw)
4315- self.certificate = __certificate
4316- self.verify = __verify
4317- self.sslstarted = __sslstarted
4318-
4319- def sendTo(self, proto):
4320- super(TLSBox, self).sendTo(proto)
4321- if self.verify is None:
4322- proto.startTLS(self.certificate)
4323- else:
4324- proto.startTLS(self.certificate, self.verify)
4325- if self.sslstarted is not None:
4326- self.sslstarted()
4327-
4328-class QuitBox(JuiceBox):
4329- def __repr__(self):
4330- return 'Quit(**%s)' % (super(QuitBox, self).__repr__(),)
4331-
4332-
4333- def sendTo(self, proto):
4334- super(QuitBox, self).sendTo(proto)
4335- proto.transport.loseConnection()
4336-
4337-class _SwitchBox(JuiceBox):
4338- def __repr__(self):
4339- return 'Switch(**%s)' % (super(_SwitchBox, self).__repr__(),)
4340-
4341-
4342- def __init__(self, __proto, **kw):
4343- super(_SwitchBox, self).__init__(**kw)
4344- self.innerProto = __proto
4345-
4346- def sendTo(self, proto):
4347- super(_SwitchBox, self).sendTo(proto)
4348- proto._switchTo(self.innerProto)
4349-
4350-
4351-
4352-class NegotiateBox(JuiceBox):
4353- def __repr__(self):
4354- return 'Negotiate(**%s)' % (super(NegotiateBox, self).__repr__(),)
4355-
4356-
4357- def sendTo(self, proto):
4358- super(NegotiateBox, self).sendTo(proto)
4359- proto._setProtocolVersion(int(self['version']))
4360-
4361-
4362-
4363-class JuiceError(Exception):
4364- pass
4365-
4366-class RemoteJuiceError(JuiceError):
4367- """
4368- This error indicates that something went wrong on the remote end of the
4369- connection, and the error was serialized and transmitted to you.
4370- """
4371- def __init__(self, errorCode, description, fatal=False):
4372- """Create a remote error with an error code and description.
4373- """
4374- Exception.__init__(self, "Remote[%s]: %s" % (errorCode, description))
4375- self.errorCode = errorCode
4376- self.description = description
4377- self.fatal = fatal
4378-
4379-class UnhandledRemoteJuiceError(RemoteJuiceError):
4380- def __init__(self, description):
4381- errorCode = "UNHANDLED"
4382- RemoteJuiceError.__init__(self, errorCode, description)
4383-
4384-class JuiceBoxError(JuiceError):
4385- pass
4386-
4387-class MalformedJuiceBox(JuiceBoxError):
4388- pass
4389-
4390-class UnhandledCommand(JuiceError):
4391- pass
4392-
4393-
4394-class IncompatibleVersions(JuiceError):
4395- pass
4396-
4397-class _Transactor:
4398- def __init__(self, store, callable):
4399- self.store = store
4400- self.callable = callable
4401-
4402- def __call__(self, box):
4403- return self.store.transact(self.callable, box)
4404-
4405- def __repr__(self):
4406- return '<Transaction in: %s of: %s>' % (self.store, self.callable)
4407-
4408-class DispatchMixin:
4409- baseDispatchPrefix = 'juice_'
4410- autoDispatchPrefix = 'command_'
4411-
4412- wrapper = None
4413-
4414- def _auto(self, aCallable, proto, namespace=None):
4415- if aCallable is None:
4416- return None
4417- command = aCallable.command
4418- if namespace not in command.namespaces:
4419- # if you're in the wrong namespace, you are very likely not allowed
4420- # to invoke the command you are trying to invoke. some objects
4421- # have commands exposed in a separate namespace for security
4422- # reasons, since the security model is a role : namespace mapping.
4423- log.msg('WRONG NAMESPACE: %r, %r' % (namespace, command.namespaces))
4424- return None
4425- def doit(box):
4426- kw = stringsToObjects(box, command.arguments, proto)
4427- for name, extraArg in command.extra:
4428- kw[name] = extraArg.fromTransport(proto.transport)
4429-# def checkIsDict(result):
4430-# if not isinstance(result, dict):
4431-# raise RuntimeError("%r returned %r, not dictionary" % (
4432-# aCallable, result))
4433-# return result
4434- def checkKnownErrors(error):
4435- key = error.trap(*command.allErrors)
4436- code = command.allErrors[key]
4437- desc = str(error.value)
4438- return Failure(RemoteJuiceError(
4439- code, desc, error in command.fatalErrors))
4440- return maybeDeferred(aCallable, **kw).addCallback(
4441- command.makeResponse, proto).addErrback(
4442- checkKnownErrors)
4443- return doit
4444-
4445- def _wrap(self, aCallable):
4446- if aCallable is None:
4447- return None
4448- wrap = self.wrapper
4449- if wrap is not None:
4450- return wrap(aCallable)
4451- else:
4452- return aCallable
4453-
4454- def normalizeCommand(self, cmd):
4455- """Return the canonical form of a command.
4456- """
4457- return cmd.upper().strip().replace('-', '_')
4458-
4459- def lookupFunction(self, proto, name, namespace):
4460- """Return a callable to invoke when executing the named command.
4461- """
4462- # Try to find a method to be invoked in a transaction first
4463- # Otherwise fallback to a "regular" method
4464- fName = self.autoDispatchPrefix + name
4465- fObj = getattr(self, fName, None)
4466- if fObj is not None:
4467- # pass the namespace along
4468- return self._auto(fObj, proto, namespace)
4469-
4470- assert namespace is None, 'Old-style parsing'
4471- # Fall back to simplistic command dispatching - we probably want to get
4472- # rid of this eventually, there's no reason to do extra work and write
4473- # fewer docs all the time.
4474- fName = self.baseDispatchPrefix + name
4475- return getattr(self, fName, None)
4476-
4477- def dispatchCommand(self, proto, cmd, box, namespace=None):
4478- fObj = self.lookupFunction(proto, self.normalizeCommand(cmd), namespace)
4479- if fObj is None:
4480- return fail(UnhandledCommand(cmd))
4481- return maybeDeferred(self._wrap(fObj), box)
4482-
4483-PYTHON_KEYWORDS = [
4484- 'and', 'del', 'for', 'is', 'raise', 'assert', 'elif', 'from', 'lambda',
4485- 'return', 'break', 'else', 'global', 'not', 'try', 'class', 'except',
4486- 'if', 'or', 'while', 'continue', 'exec', 'import', 'pass', 'yield',
4487- 'def', 'finally', 'in', 'print']
4488-
4489-def normalizeKey(key):
4490- lkey = key.lower().replace('-', '_')
4491- if lkey in PYTHON_KEYWORDS:
4492- return lkey.title()
4493- return lkey
4494-
4495-
4496-def parseJuiceHeaders(lines):
4497- """
4498- Create a JuiceBox from a list of header lines.
4499-
4500- @param lines: a list of lines.
4501- """
4502- b = JuiceBox()
4503- bodylen = 0
4504- key = None
4505- for L in lines:
4506- if L[0] == ' ':
4507- # continuation
4508- assert key is not None
4509- b[key] += '\r\n'+L[1:]
4510- continue
4511- parts = L.split(': ', 1)
4512- if len(parts) != 2:
4513- raise MalformedJuiceBox("Wrong number of parts: %r" % (L,))
4514- key, value = parts
4515- key = normalizeKey(key)
4516- b[key] = value
4517- return int(b.pop(LENGTH, 0)), b
4518-
4519-class JuiceParserBase(DispatchMixin):
4520-
4521- def __init__(self):
4522- self._outstandingRequests = {}
4523-
4524- def _puke(self, failure):
4525- log.msg("Juice server or network failure "
4526- "unhandled by client application:")
4527- log.err(failure)
4528- log.msg(
4529- "Dropping connection! "
4530- "To avoid, add errbacks to ALL remote commands!")
4531- if self.transport is not None:
4532- self.transport.loseConnection()
4533-
4534- _counter = 0L
4535-
4536- def _nextTag(self):
4537- self._counter += 1
4538- return '%x' % (self._counter,)
4539-
4540- def failAllOutgoing(self, reason):
4541- OR = self._outstandingRequests.items()
4542- self._outstandingRequests = None # we can never send another request
4543- for key, value in OR:
4544- value.errback(reason)
4545-
4546- def juiceBoxReceived(self, box):
4547- if debug:
4548- log.msg("Juice receive: %s" % pprint.pformat(dict(box.iteritems())))
4549-
4550- if ANSWER in box:
4551- question = self._outstandingRequests.pop(box[ANSWER])
4552- question.addErrback(self._puke)
4553- self._wrap(question.callback)(box)
4554- elif ERROR in box:
4555- question = self._outstandingRequests.pop(box[ERROR])
4556- question.addErrback(self._puke)
4557- self._wrap(question.errback)(
4558- Failure(RemoteJuiceError(box[ERROR_CODE],
4559- box[ERROR_DESCRIPTION])))
4560- elif COMMAND in box:
4561- cmd = box[COMMAND]
4562- def sendAnswer(answerBox):
4563- if ASK not in box:
4564- return
4565- if self.transport is None:
4566- return
4567- answerBox[ANSWER] = box[ASK]
4568- answerBox.sendTo(self)
4569- def sendError(error):
4570- if ASK not in box:
4571- return error
4572- if error.check(RemoteJuiceError):
4573- code = error.value.errorCode
4574- desc = error.value.description
4575- if error.value.fatal:
4576- errorBox = QuitBox()
4577- else:
4578- errorBox = JuiceBox()
4579- else:
4580- errorBox = QuitBox()
4581- log.err(error) # here is where server-side logging happens
4582- # if the error isn't handled
4583- code = 'UNHANDLED'
4584- desc = "Unhandled Remote System Exception "
4585- errorBox[ERROR] = box[ASK]
4586- errorBox[ERROR_DESCRIPTION] = desc
4587- errorBox[ERROR_CODE] = code
4588- if self.transport is not None:
4589- errorBox.sendTo(self)
4590- return None # intentionally stop the error here: don't log the
4591- # traceback if it's handled, do log it (earlier) if
4592- # it isn't
4593- self.dispatchCommand(self, cmd, box).addCallbacks(sendAnswer, sendError
4594- ).addErrback(self._puke)
4595- else:
4596- raise RuntimeError(
4597- "Empty packet received over connection-oriented juice: %r" % (box,))
4598-
4599- def sendBoxCommand(self, command, box, requiresAnswer=True):
4600- """
4601- Send a command across the wire with the given C{juice.Box}.
4602-
4603- Returns a Deferred which fires with the response C{juice.Box} when it
4604- is received, or fails with a C{juice.RemoteJuiceError} if an error is
4605- received.
4606-
4607- If the Deferred fails and the error is not handled by the caller of
4608- this method, the failure will be logged and the connection dropped.
4609- """
4610- if self._outstandingRequests is None:
4611- return fail(CONNECTION_LOST)
4612- box[COMMAND] = command
4613- tag = self._nextTag()
4614- if requiresAnswer:
4615- box[ASK] = tag
4616- result = self._outstandingRequests[tag] = Deferred()
4617- else:
4618- result = None
4619- box.sendTo(self)
4620- return result
4621-
4622-
4623-
4624-
4625-
4626-
4627-class Argument:
4628- optional = False
4629-
4630- def __init__(self, optional=False):
4631- self.optional = optional
4632-
4633- def retrieve(self, d, name):
4634- if self.optional:
4635- value = d.get(name)
4636- if value is not None:
4637- del d[name]
4638- else:
4639- value = d.pop(name)
4640- return value
4641-
4642- def fromBox(self, name, strings, objects, proto):
4643- st = self.retrieve(strings, name)
4644- if self.optional and st is None:
4645- objects[name] = None
4646- else:
4647- objects[name] = self.fromStringProto(st, proto)
4648-
4649- def toBox(self, name, strings, objects, proto):
4650- obj = self.retrieve(objects, name)
4651- if self.optional and obj is None:
4652- # strings[name] = None
4653- return
4654- else:
4655- strings[name] = self.toStringProto(obj, proto)
4656-
4657- def fromStringProto(self, inString, proto):
4658- return self.fromString(inString)
4659-
4660- def toStringProto(self, inObject, proto):
4661- return self.toString(inObject)
4662-
4663- def fromString(self, inString):
4664- raise NotImplementedError()
4665-
4666- def toString(self, inObject):
4667- raise NotImplementedError()
4668-
4669-class JuiceList(Argument):
4670- def __init__(self, subargs):
4671- self.subargs = subargs
4672-
4673- def fromStringProto(self, inString, proto):
4674- boxes = parseString(inString)
4675- values = [stringsToObjects(box, self.subargs, proto)
4676- for box in boxes]
4677- return values
4678-
4679- def toStringProto(self, inObject, proto):
4680- return ''.join([objectsToStrings(
4681- objects, self.subargs, Box(), proto
4682- ).serialize() for objects in inObject])
4683-
4684-class ListOf(Argument):
4685- def __init__(self, subarg, delimiter=', '):
4686- self.subarg = subarg
4687- self.delimiter = delimiter
4688-
4689- def fromStringProto(self, inString, proto):
4690- strings = inString.split(self.delimiter)
4691- L = [self.subarg.fromStringProto(string, proto)
4692- for string in strings]
4693- return L
4694-
4695- def toStringProto(self, inObject, proto):
4696- L = []
4697- for inSingle in inObject:
4698- outString = self.subarg.toStringProto(inSingle, proto)
4699- assert self.delimiter not in outString
4700- L.append(outString)
4701- return self.delimiter.join(L)
4702-
4703-class Integer(Argument):
4704- fromString = int
4705- def toString(self, inObject):
4706- return str(int(inObject))
4707-
4708-class String(Argument):
4709- def toString(self, inObject):
4710- return inObject
4711- def fromString(self, inString):
4712- return inString
4713-
4714-class EncodedString(Argument):
4715-
4716- def __init__(self, encoding):
4717- self.encoding = encoding
4718-
4719- def toString(self, inObject):
4720- return inObject.encode(self.encoding)
4721-
4722- def fromString(self, inString):
4723- return inString.decode(self.encoding)
4724-
4725-# Temporary backwards compatibility for Exponent
4726-
4727-Body = String
4728-
4729-class Unicode(String):
4730- def toString(self, inObject):
4731- # assert isinstance(inObject, unicode)
4732- return String.toString(self, inObject.encode('utf-8'))
4733-
4734- def fromString(self, inString):
4735- # assert isinstance(inString, str)
4736- return String.fromString(self, inString).decode('utf-8')
4737-
4738-class Path(Unicode):
4739- def fromString(self, inString):
4740- return filepath.FilePath(Unicode.fromString(self, inString))
4741-
4742- def toString(self, inObject):
4743- return Unicode.toString(self, inObject.path)
4744-
4745-
4746-class Float(Argument):
4747- fromString = float
4748- toString = str
4749-
4750-class Base64Binary(Argument):
4751- def toString(self, inObject):
4752- return inObject.encode('base64').replace('\n', '')
4753- def fromString(self, inString):
4754- return inString.decode('base64')
4755-
4756-class Time(Argument):
4757- def toString(self, inObject):
4758- return inObject.asISO8601TimeAndDate()
4759- def fromString(self, inString):
4760- return extime.Time.fromISO8601TimeAndDate(inString)
4761-
4762-class ExtraArg:
4763- def fromTransport(self, inTransport):
4764- raise NotImplementedError()
4765-
4766-class Peer(ExtraArg):
4767- def fromTransport(self, inTransport):
4768- return inTransport.getQ2QPeer()
4769-
4770-class PeerDomain(ExtraArg):
4771- def fromTransport(self, inTransport):
4772- return inTransport.getQ2QPeer().domain
4773-
4774-class PeerUser(ExtraArg):
4775- def fromTransport(self, inTransport):
4776- return inTransport.getQ2QPeer().resource
4777-
4778-class Host(ExtraArg):
4779- def fromTransport(self, inTransport):
4780- return inTransport.getQ2QHost()
4781-
4782-class HostDomain(ExtraArg):
4783- def fromTransport(self, inTransport):
4784- return inTransport.getQ2QHost().domain
4785-
4786-class HostUser(ExtraArg):
4787- def fromTransport(self, inTransport):
4788- return inTransport.getQ2QHost().resource
4789-
4790-
4791-
4792-class Boolean(Argument):
4793- def fromString(self, inString):
4794- if inString == 'True':
4795- return True
4796- elif inString == 'False':
4797- return False
4798- else:
4799- raise RuntimeError("Bad boolean value: %r" % (inString,))
4800-
4801- def toString(self, inObject):
4802- if inObject:
4803- return 'True'
4804- else:
4805- return 'False'
4806-
4807-class Command:
4808- class __metaclass__(type):
4809- def __new__(cls, name, bases, attrs):
4810- re = attrs['reverseErrors'] = {}
4811- er = attrs['allErrors'] = {}
4812- for v, k in attrs.get('errors',{}).iteritems():
4813- re[k] = v
4814- er[v] = k
4815- for v, k in attrs.get('fatalErrors',{}).iteritems():
4816- re[k] = v
4817- er[v] = k
4818- return type.__new__(cls, name, bases, attrs)
4819-
4820- arguments = []
4821- response = []
4822- extra = []
4823- namespaces = [None] # This is set to [None] on purpose: None means
4824- # "no namespace", not "empty list". "empty
4825- # list" will make your command invalid in _all_
4826- # namespaces, effectively uncallable.
4827- errors = {}
4828- fatalErrors = {}
4829-
4830- commandType = Box
4831- responseType = Box
4832-
4833- def commandName():
4834- def get(self):
4835- return self.__class__.__name__
4836- raise NotImplementedError("Missing command name")
4837- return get,
4838- commandName = property(*commandName())
4839-
4840- def __init__(self, **kw):
4841- self.structured = kw
4842- givenArgs = [normalizeKey(k) for k in kw.keys()]
4843- forgotten = []
4844- for name, arg in self.arguments:
4845- if normalizeKey(name) not in givenArgs and not arg.optional:
4846- forgotten.append(normalizeKey(name))
4847-# for v in kw.itervalues():
4848-# if v is None:
4849-# from pprint import pformat
4850-# raise RuntimeError("ARGH: %s" % pformat(kw))
4851- if forgotten:
4852- if len(forgotten) == 1:
4853- plural = 'an argument'
4854- else:
4855- plural = 'some arguments'
4856- raise RuntimeError("You forgot %s to %r: %s" % (
4857- plural, self.commandName, ', '.join(forgotten)))
4858- forgotten = []
4859-
4860- def makeResponse(cls, objects, proto):
4861- try:
4862- return objectsToStrings(objects, cls.response, cls.responseType(), proto)
4863- except:
4864- log.msg("Exception in %r.makeResponse" % (cls,))
4865- raise
4866- makeResponse = classmethod(makeResponse)
4867-
4868- def do(self, proto, namespace=None, requiresAnswer=True):
4869- if namespace is not None:
4870- cmd = namespace + ":" + self.commandName
4871- else:
4872- cmd = self.commandName
4873- def _massageError(error):
4874- error.trap(RemoteJuiceError)
4875- rje = error.value
4876- return Failure(self.reverseErrors.get(rje.errorCode, UnhandledRemoteJuiceError)(rje.description))
4877-
4878- d = proto.sendBoxCommand(
4879- cmd, objectsToStrings(self.structured, self.arguments, self.commandType(),
4880- proto),
4881- requiresAnswer)
4882-
4883- if requiresAnswer:
4884- d.addCallback(stringsToObjects, self.response, proto)
4885- d.addCallback(self.addExtra, proto.transport)
4886- d.addErrback(_massageError)
4887-
4888- return d
4889-
4890- def addExtra(self, d, transport):
4891- for name, extraArg in self.extra:
4892- d[name] = extraArg.fromTransport(transport)
4893- return d
4894-
4895-
4896-class ProtocolSwitchCommand(Command):
4897- """Use this command to switch from something Juice-derived to a different
4898- protocol mid-connection. This can be useful to use juice as the
4899- connection-startup negotiation phase. Since TLS is a different layer
4900- entirely, you can use Juice to negotiate the security parameters of your
4901- connection, then switch to a different protocol, and the connection will
4902- remain secured.
4903- """
4904-
4905- def __init__(self, __protoToSwitchToFactory, **kw):
4906- self.protoToSwitchToFactory = __protoToSwitchToFactory
4907- super(ProtocolSwitchCommand, self).__init__(**kw)
4908-
4909- def makeResponse(cls, innerProto, proto):
4910- return _SwitchBox(innerProto)
4911-
4912- makeResponse = classmethod(makeResponse)
4913-
4914- def do(self, proto, namespace=None):
4915- d = super(ProtocolSwitchCommand, self).do(proto)
4916- proto._lock()
4917- def switchNow(ign):
4918- innerProto = self.protoToSwitchToFactory.buildProtocol(proto.transport.getPeer())
4919- proto._switchTo(innerProto, self.protoToSwitchToFactory)
4920- return ign
4921- def die(ign):
4922- proto.transport.loseConnection()
4923- return ign
4924- def handle(ign):
4925- self.protoToSwitchToFactory.clientConnectionFailed(None, Failure(CONNECTION_LOST))
4926- return ign
4927- return d.addCallbacks(switchNow, handle).addErrback(die)
4928-
4929-class Negotiate(Command):
4930- commandName = 'Negotiate'
4931-
4932- arguments = [('versions', ListOf(Integer()))]
4933- response = [('version', Integer())]
4934-
4935- responseType = NegotiateBox
4936-
4937-
4938-class Juice(LineReceiver, JuiceParserBase):
4939- """
4940- JUICE (JUice Is Concurrent Events) is a simple connection-oriented
4941- request/response protocol. Packets, or "boxes", are collections of
4942- RFC2822-inspired headers, plus a body. Note that this is NOT a literal
4943- interpretation of any existing RFC, 822, 2822 or otherwise, but a simpler
4944- version that does not do line continuations, does not specify any
4945- particular format for header values, dispatches semantic meanings of most
4946- headers on the -Command header rather than giving them global meaning, and
4947- allows multiple sets of headers (messages, or JuiceBoxes) on a connection.
4948-
4949- All headers whose names begin with a dash ('-') are reserved for use by the
4950- protocol. All others are for application use - their meaning depends on
4951- the value of the "-Command" header.
4952- """
4953-
4954- protocolName = 'juice-base'
4955-
4956- hostCertificate = None
4957-
4958- MAX_LENGTH = 1024 * 1024
4959-
4960- isServer = property(lambda self: self._issueGreeting,
4961- doc="""
4962- True if this is a juice server, e.g. it is going to
4963- issue or has issued a server greeting upon
4964- connection.
4965- """)
4966-
4967- isClient = property(lambda self: not self._issueGreeting,
4968- doc="""
4969- True if this is a juice server, e.g. it is not going to
4970- issue or did not issue a server greeting upon
4971- connection.
4972- """)
4973-
4974- def __init__(self, issueGreeting):
4975- """
4976- @param issueGreeting: whether to issue a greeting when connected. This
4977- should be set on server-side Juice protocols.
4978- """
4979- JuiceParserBase.__init__(self)
4980- self._issueGreeting = issueGreeting
4981-
4982- def __repr__(self):
4983- return '<%s %s/%s at 0x%x>' % (self.__class__.__name__, self.isClient and 'client' or 'server', self.innerProtocol, id(self))
4984-
4985- __locked = False
4986-
4987- def _lock(self):
4988- """ Lock this Juice instance so that no further Juice traffic may be sent.
4989- This is used when sending a request to switch underlying protocols.
4990- You probably want to subclass ProtocolSwitchCommand rather than calling
4991- this directly.
4992- """
4993- self.__locked = True
4994-
4995- innerProtocol = None
4996-
4997- def _switchTo(self, newProto, clientFactory=None):
4998- """ Switch this Juice instance to a new protocol. You need to do this
4999- 'simultaneously' on both ends of a connection; the easiest way to do
5000- this is to use a subclass of ProtocolSwitchCommand.
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches