def require(packageName, fixName): if (packageName, fixName) in _alreadyInstalled: return if (packageName, fixName) == ("twisted", "filepath_copyTo"): from twisted.python import filepath if filepath.FilePath("a") != filepath.FilePath("a"): from vmc.contrib.epsilon.hotfixes import filepath_copyTo filepath_copyTo.install() elif (packageName, fixName) == ("twisted", "timeoutmixin_calllater"): from twisted.protocols import policies if not hasattr(policies.TimeoutMixin, "callLater"): from vmc.contrib.epsilon.hotfixes import timeoutmixin_calllater timeoutmixin_calllater.install() elif (packageName, fixName) == ("twisted", "delayedcall_seconds"): from twisted.internet import base args = inspect.getargs(base.DelayedCall.__init__.func_code)[0] if "seconds" not in args: from vmc.contrib.epsilon.hotfixes import delayedcall_seconds delayedcall_seconds.install() elif (packageName, fixName) == ("twisted", "deferredgenerator_tfailure"): from twisted.internet import defer result = [] def test(): d = defer.waitForDeferred(defer.succeed(1)) yield d result.append(d.getResult()) defer.deferredGenerator(test)() if result == [1]: from vmc.contrib.epsilon.hotfixes import deferredgenerator_tfailure deferredgenerator_tfailure.install() else: assert result == [None] elif (packageName, fixName) == ("twisted", "proto_helpers_stringtransport"): from twisted.test.proto_helpers import StringTransport st = StringTransport() try: st.write(u"foo") except TypeError, e: pass else: from vmc.contrib.epsilon.hotfixes import proto_helpers_stringtransport proto_helpers_stringtransport.install()
def processLockRequest(resource, request): """ Respond to a LOCK request. (RFC 2518, section 8.10) Relevant notes: """ requestStream = request.stream depth = getDepth(request.headers) #log.error(request.headers.getRawHeaders("X-Litmus")[0]) # generate DAVDocument from request body lockInfo = waitForDeferred(deferredGenerator(parseLockRequest)(requestStream)) yield lockInfo lockInfo = lockInfo.getResult() assertExclusiveLock(lockInfo) assertWriteLock(lockInfo) # we currently only allow lock depths of "0" assertZeroLockDepth(depth) # build the corresponding activelock element # e.g. http://www.webdav.org/specs/rfc2518.html#rfc.section.8.10.8 activeLock = buildActiveLock(lockInfo, depth) # extract the lock token lt = activeLock.childOfType(davxml.LockToken).childOfType(davxml.HRef) # make headers with lock token header lth = http_headers.Headers( rawHeaders = {"Lock-Token": [lt]} ) ld = davxml.LockDiscovery(activeLock) ignored = waitForDeferred(deferredGenerator(resource._setLock)(ld, request)) yield ignored ignored = ignored.getResult() # debug ignored = waitForDeferred(deferredGenerator(resource._getLock)()) yield ignored ignored = ignored.getResult() pp = davxml.PropertyContainer(ld) yield Response( code = responsecode.OK, headers = lth, stream = stream.MemoryStream(pp.toxml()))
def testBuggyGen(self): def _genError(): yield waitForDeferred(getThing()) 1/0 _genError = deferredGenerator(_genError) self.assertRaises(ZeroDivisionError, util.wait, _genError())
def http_PROPPATCH(self, request): """ Wrap the request in an assertion that the lock token provided with the request corresponds to the lock token associated with the resource. """ return deferredGenerator(self.assertNotLocked)(request).addCallback( super(Lockable, self).http_PROPPATCH)
def testDeferredYielding(self): # See the comment _deferGenerator about d.callback(Deferred). def _genDeferred(): yield getThing() _genDeferred = deferredGenerator(_genDeferred) return self.assertFailure(_genDeferred(), TypeError)
def testDeferredYielding(self): # See the comment _deferGenerator about d.callback(Deferred). def _genDeferred(): yield getThing() _genDeferred = deferredGenerator(_genDeferred) self.assertRaises(TypeError, util.wait, _genDeferred())
def testBuggyGen(self): def _genError(): yield waitForDeferred(getThing()) 1/0 _genError = deferredGenerator(_genError) return self.assertFailure(_genError(), ZeroDivisionError)
def testStackUsage2(self): def _loop(): for x in range(5000): yield 1 yield 0 _loop = deferredGenerator(_loop) self.assertEquals(util.wait(_loop()), 0)
def isLocked(self, request): """ A resource is considered mutable in this context, if -- it is not locked -- the request provides the opaquelocktoken corresponding to the lock on this resource """ # get the local lock token llt = waitForDeferred(deferredGenerator(self._lockToken)()) yield llt llt = llt.getResult() # get the remote lock token rlt = getOpaqueLockToken(request) if self.exists(): # a resource that does not exist can not be locked yield False elif llt == None: # this resource has no lock associated with it, ergo not locked yield False elif rlt == llt: # this resource has a lock token associated with it, but the same # lock token has been supplied, ergo not locked (for this request) yield False else: # resource is locked yield True
def http_COPY(self, request): """ Wrap the request in an assertion that the lock token provided with the request corresponds to the lock token associated with the resource. """ def __http_copy(self, request): """Assert that the destination is not locked.""" destination_uri = request.headers.getHeader("destination") # assert that the destination exists if not destination_uri: msg = "No destination header in %s request." % (request.method,) log.error(msg) raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg)) # assert that the destination is not locked dest = waitForDeferred(request.locateResource(destination_uri)) yield dest dest = dest.getResult() ignore = waitForDeferred(deferredGenerator(dest.assertNotLocked)(request)) yield ignore ignore = ignore.getResult() dd = waitForDeferred(super(Lockable, self).http_COPY(request)) yield dd yield dd.getResult() #yield deferredGenerator(super(Lockable, self).http_COPY)(request) return deferredGenerator(__http_copy)(self, request)
class ContainMalformed(pyunit.TestCase): """ This TestCase holds malformed test methods that trial should handle. """ def test_foo(self, blah): pass def test_bar(): pass test_spam = defer.deferredGenerator(test_bar)
def testStackUsage2(self): def _loop(): for x in range(5000): # Test with yielding a random value yield 1 yield 0 _loop = deferredGenerator(_loop) return _loop().addCallback(self.assertEqual, 0)
def http_MOVE(self, request): """ Wrap the request in an assertion that the lock token provided with the request corresponds to the lock token associated with the resource. TODO: assert that the destination file is not locked. """ return deferredGenerator(self.assertNotLocked)(request).addCallback( super(Lockable, self).http_MOVE)
def testMultipleShutdowns(self): def runner(): for k in xrange(10): yield waitForDeferred(self.broker.shutdown()) d = Deferred() reactor.callLater(0.02, d.callback, None) yield waitForDeferred(d) runner = deferredGenerator(runner) return runner()
def testStackUsage(self): def _loop(): for x in range(5000): x = waitForDeferred(defer.succeed(1)) yield x x = x.getResult() yield 0 _loop = deferredGenerator(_loop) self.assertEquals(util.wait(_loop()), 0)
def require(packageName, fixName): if (packageName, fixName) in _alreadyInstalled: return if (packageName, fixName) == ('twisted', 'filepath_copyTo'): from twisted.python import filepath if filepath.FilePath('a') != filepath.FilePath('a'): from epsilon.hotfixes import filepath_copyTo filepath_copyTo.install() elif (packageName, fixName) == ('twisted', 'timeoutmixin_calllater'): from twisted.protocols import policies if not hasattr(policies.TimeoutMixin, 'callLater'): from epsilon.hotfixes import timeoutmixin_calllater timeoutmixin_calllater.install() elif (packageName, fixName) == ('twisted', 'delayedcall_seconds'): from twisted.internet import base args = inspect.getargs(base.DelayedCall.__init__.func_code)[0] if 'seconds' not in args: from epsilon.hotfixes import delayedcall_seconds delayedcall_seconds.install() elif (packageName, fixName) == ('twisted', 'deferredgenerator_tfailure'): from twisted.internet import defer result = [] def test(): d = defer.waitForDeferred(defer.succeed(1)) yield d result.append(d.getResult()) defer.deferredGenerator(test)() if result == [1]: from epsilon.hotfixes import deferredgenerator_tfailure deferredgenerator_tfailure.install() else: assert result == [None] elif (packageName, fixName) == ("twisted", "proto_helpers_stringtransport"): from twisted.test.proto_helpers import StringTransport st = StringTransport() try: st.write(u'foo') except TypeError, e: pass else: from epsilon.hotfixes import proto_helpers_stringtransport proto_helpers_stringtransport.install()
class DeferredTests(unittest.TestCase): touched = False def _cb_fail(self, reason): self.fail(reason) def _cb_error(self, reason): raise RuntimeError(reason) def _cb_skip(self, reason): raise unittest.SkipTest(reason) def _touchClass(self, ignored): self.__class__.touched = True def setUp(self): self.__class__.touched = False def test_pass(self): return defer.succeed('success') def test_passGenerated(self): self._touchClass(None) yield None test_passGenerated = defer.deferredGenerator(test_passGenerated) def test_fail(self): return defer.fail(self.failureException('I fail')) def test_failureInCallback(self): d = defer.succeed('fail') d.addCallback(self._cb_fail) return d def test_errorInCallback(self): d = defer.succeed('error') d.addCallback(self._cb_error) return d def test_skip(self): d = defer.succeed('skip') d.addCallback(self._cb_skip) d.addCallback(self._touchClass) return d def test_thread(self): return threads.deferToThread(lambda: None) def test_expectedFailure(self): d = defer.succeed('todo') d.addCallback(self._cb_error) return d test_expectedFailure.todo = "Expected failure"
def testDeferredYielding(self): """ Ensure that yielding a Deferred directly is trapped as an error. """ # See the comment _deferGenerator about d.callback(Deferred). def _genDeferred(): yield getThing() _genDeferred = deferredGenerator(_genDeferred) return self.assertFailure(_genDeferred(), TypeError)
def _removeLock(self, request): """ Remove the lockDiscovery property from the resource. """ ignore = waitForDeferred(deferredGenerator(self.assertNotLocked)(request)) yield ignore ignore = ignore.getResult() self.removeDeadProperty(davxml.LockDiscovery) yield Response(responsecode.NO_CONTENT)
def _put(self, stream): if not self.isWritableFile(): message = "http_PUT: not authorized to put file: " + self.fp.path log.error(message) raise HTTPError(StatusResponse(responsecode.UNAUTHORIZED, message)) response = waitForDeferred(deferredGenerator(self.__putDelete)()) yield response response = response.getResult() xx = waitForDeferred(deferredGenerator(self.__putFile)(stream)) yield xx xx = xx.getResult() self._registerWithParent() xx = waitForDeferred(deferredGenerator(self._updateMetadata)()) yield xx yield response
def testStackUsage(self): # Make sure we don't blow the stack when yielding immediately # available values def _loop(): for x in range(5000): # Test with yielding a deferred x = waitForDeferred(defer.succeed(1)) yield x x = x.getResult() yield 0 _loop = deferredGenerator(_loop) return _loop().addCallback(self.assertEqual, 0)
def _removeLock(self, request): """ Remove the lockDiscovery property from the resource. """ ignore = waitForDeferred( deferredGenerator(self.assertNotLocked)(request)) yield ignore ignore = ignore.getResult() self.removeDeadProperty(davxml.LockDiscovery) yield Response(responsecode.NO_CONTENT)
def assertNotLocked(self, request): il = waitForDeferred(deferredGenerator(self.isLocked)(request)) yield il il = il.getResult() if il is True: error = "Resource is locked and you don't have the proper token handy." log.error(error) raise HTTPError(StatusResponse(responsecode.LOCKED, error)) # we must forward the request to possible callbacks yield request
class AbstractServerTestMixin: type = None def testBasicWorkingness(self): args = ('-u', util.sibpath(__file__, "simple_client.py"), "basic", str(self.port), self.type) d = waitForDeferred( utils.getProcessOutputAndValue(sys.executable, args=args, env=os.environ)) yield d out, err, code = d.getResult() self.assertEquals(code, 0, "Error output:\n%s" % (err, )) self.assertEquals( out, "HTTP/1.1 402 Payment Required\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" ) testBasicWorkingness = deferredGenerator(testBasicWorkingness) def testLingeringClose(self): args = ('-u', util.sibpath(__file__, "simple_client.py"), "lingeringClose", str(self.port), self.type) d = waitForDeferred( utils.getProcessOutputAndValue(sys.executable, args=args, env=os.environ)) yield d out, err, code = d.getResult() self.assertEquals(code, 0, "Error output:\n%s" % (err, )) self.assertEquals( out, "HTTP/1.1 402 Payment Required\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" ) testLingeringClose = deferredGenerator(testLingeringClose)
def testStackUsage(self): # Make sure we don't blow the stack when yielding immediately # available values def _loop(): for x in range(5000): # Test with yielding a deferred x = waitForDeferred(defer.succeed(1)) yield x x = x.getResult() yield 0 _loop = deferredGenerator(_loop) self.assertEquals(util.wait(_loop()), 0)
def _setLock(self, lockDiscovery, request): """ Lock this resource with the supplied activelock. """ ignore = waitForDeferred(deferredGenerator(self.assertNotLocked)(request)) yield ignore ignore = ignore.getResult() # the lockDiscovery property is protected, it must therefore be # set through writeDeadProperty instead of through writeProperty self.writeDeadProperty(lockDiscovery) yield lockDiscovery
def __proppatch(self, request): # read the body doc = waitForDeferred(deferredGenerator(readRequestBody)(request)) yield doc doc = doc.getResult() # perform basic validation, and extract the clone field to be updated try: cloneField = validateBodyXML(doc) except AssertionError, e: error = "PROPPATCH request body does not validate. Error: " + str( e) log.info(error) raise HTTPError(StatusResponse(responsecode.FORBIDDEN, error))
def _lockToken(self): """ @return the uri of the opaquelocktoken of the lock on this resource, if the latter exists, otherwise None. See: http://webdav.org/specs/rfc2518.html#rfc.section.6.4 """ lockDiscovery = waitForDeferred(deferredGenerator(self._getLock)()) yield lockDiscovery lockDiscovery = lockDiscovery.getResult() if lockDiscovery is None: yield None else: href = str(lockDiscovery.childOfType(davxml.ActiveLock).childOfType(davxml.LockToken).childOfType(davxml.HRef)) yield href
def _setLock(self, lockDiscovery, request): """ Lock this resource with the supplied activelock. """ ignore = waitForDeferred( deferredGenerator(self.assertNotLocked)(request)) yield ignore ignore = ignore.getResult() # the lockDiscovery property is protected, it must therefore be # set through writeDeadProperty instead of through writeProperty self.writeDeadProperty(lockDiscovery) yield lockDiscovery
def initCluster(locator): """This method must be called first""" global cluster for name, value in globals().items(): if isinstance(value, type): if issubclass(value, Model): setattr(value, 'locator', locator) def initShards(): shards = defer.waitForDeferred(cluster.startup()) yield shards shards = shards.getResult() d = defer.deferredGenerator(initShards) return d()
def _lockToken(self): """ @return the uri of the opaquelocktoken of the lock on this resource, if the latter exists, otherwise None. See: http://webdav.org/specs/rfc2518.html#rfc.section.6.4 """ lockDiscovery = waitForDeferred(deferredGenerator(self._getLock)()) yield lockDiscovery lockDiscovery = lockDiscovery.getResult() if lockDiscovery is None: yield None else: href = str( lockDiscovery.childOfType(davxml.ActiveLock).childOfType( davxml.LockToken).childOfType(davxml.HRef)) yield href
def __lockPreconditions(self, request): if not self.exists(): error = "File not found in LOCK request: %s" % ( self.fp.path, ) raise HTTPError(StatusResponse(responsecode.NOT_FOUND, error)) if not self.isWritableFile(): error = "No write permission for file." raise HTTPError(StatusResponse(responsecode.UNAUTHORIZED, error)) ignore = waitForDeferred(deferredGenerator(self.assertNotLocked)(request)) yield ignore ignore = ignore.getResult() # for some reason, the result of preconditions_LOCK is handed as an argument to http_LOCK # (i guess so that the request can be modified during the preconditions call). anyway, # we must yield the request at the end. yield request
class TestURLEncoded(unittest.TestCase): def doTest(self, data, expected_args): for bytes in range(1, 20): s = TestStream(data, maxReturn=bytes) d = waitForDeferred(fileupload.parse_urlencoded(s)) yield d args = d.getResult() self.assertEquals(args, expected_args) doTest = deferredGenerator(doTest) def test_parseValid(self): self.doTest("a=b&c=d&c=e", {'a': ['b'], 'c': ['d', 'e']}) self.doTest("a=b&c=d&c=e", {'a': ['b'], 'c': ['d', 'e']}) self.doTest("a=b+c%20d", {'a': ['b c d']}) def test_parseInvalid(self): self.doTest("a&b=c", {'b': ['c']})
def testHandledTerminalFailure(self): """ Create a Deferred Generator which yields a Deferred which fails and handles the exception which results. Assert that the Deferred Generator does not errback its Deferred. """ class TerminalException(Exception): pass def _genFailure(): x = waitForDeferred(defer.fail(TerminalException("Handled Terminal Failure"))) yield x try: x.getResult() except TerminalException: pass _genFailure = deferredGenerator(_genFailure) return _genFailure().addCallback(self.assertEqual, None)
def __lockPreconditions(self, request): if not self.exists(): error = "File not found in LOCK request: %s" % (self.fp.path, ) raise HTTPError(StatusResponse(responsecode.NOT_FOUND, error)) if not self.isWritableFile(): error = "No write permission for file." raise HTTPError(StatusResponse(responsecode.UNAUTHORIZED, error)) ignore = waitForDeferred( deferredGenerator(self.assertNotLocked)(request)) yield ignore ignore = ignore.getResult() # for some reason, the result of preconditions_LOCK is handed as an argument to http_LOCK # (i guess so that the request can be modified during the preconditions call). anyway, # we must yield the request at the end. yield request
def testHandledTerminalFailure(self): """ Create a Deferred Generator which yields a Deferred which fails and handles the exception which results. Assert that the Deferred Generator does not errback its Deferred. """ class TerminalException(Exception): pass def _genFailure(): x = waitForDeferred( defer.fail(TerminalException("Handled Terminal Failure"))) yield x try: x.getResult() except TerminalException: pass _genFailure = deferredGenerator(_genFailure) return _genFailure().addCallback(self.assertEqual, None)
def getPageFromAny(upstreams, factory=client.HTTPClientFactory, context_factory=None): if not upstreams: raise error.Error(http.INTERNAL_SERVER_ERROR) def subgen(): lastError = None for (identifier, url, args, kwargs) in upstreams: subfactory = client._makeGetterFactory(url, factory, context_factory, *args, **kwargs) wait = defer.waitForDeferred(subfactory.deferred) yield wait try: yield (wait.getResult(), identifier, subfactory) return except ConnectError: lastError = sys.exc_info()[1] raise lastError and lastError or error.Error(http.INTERNAL_SERVER_ERROR) return defer.deferredGenerator(subgen)()
def quotaSize(self, request): """ Get the size of this resource. TODO: Take into account size of dead-properties. Does stat include xattrs size? @return: an L{Deferred} with a C{int} result containing the size of the resource. """ if self.isCollection(): def walktree(top): """ Recursively descend the directory tree rooted at top, calling the callback function for each regular file @param top: L{FilePath} for the directory to walk. """ total = 0 for f in top.listdir(): child = top.child(f) if child.isdir(): # It's a directory, recurse into it result = waitForDeferred(walktree(child)) yield result total += result.getResult() elif child.isfile(): # It's a file, call the callback function total += child.getsize() else: # Unknown file type, print a message pass yield total walktree = deferredGenerator(walktree) return walktree(self.fp) else: return succeed(self.fp.getsize())
def testHandledTerminalAsyncFailure(self): """ Just like testHandledTerminalFailure, only with a Deferred which fires asynchronously with an error. """ class TerminalException(Exception): pass d = defer.Deferred() def _genFailure(): x = waitForDeferred(d) yield x try: x.getResult() except TerminalException: pass _genFailure = deferredGenerator(_genFailure) deferredGeneratorResultDeferred = _genFailure() d.errback(TerminalException("Handled Terminal Failure")) return deferredGeneratorResultDeferred.addCallback( self.assertEqual, None)
class ManholeLoopbackMixin: serverProtocol = manhole.ColoredManhole def wfd(self, d): return defer.waitForDeferred(d) def testSimpleExpression(self): done = self.recvlineClient.expect("done") self._testwrite("1 + 1\n" "done") def finished(ign): self._assertBuffer([">>> 1 + 1", "2", ">>> done"]) return done.addCallback(finished) def testTripleQuoteLineContinuation(self): done = self.recvlineClient.expect("done") self._testwrite("'''\n'''\n" "done") def finished(ign): self._assertBuffer([">>> '''", "... '''", "'\\n'", ">>> done"]) return done.addCallback(finished) def testFunctionDefinition(self): done = self.recvlineClient.expect("done") self._testwrite("def foo(bar):\n" "\tprint bar\n\n" "foo(42)\n" "done") def finished(ign): self._assertBuffer([ ">>> def foo(bar):", "... print bar", "... ", ">>> foo(42)", "42", ">>> done" ]) return done.addCallback(finished) def testClassDefinition(self): done = self.recvlineClient.expect("done") self._testwrite("class Foo:\n" "\tdef bar(self):\n" "\t\tprint 'Hello, world!'\n\n" "Foo().bar()\n" "done") def finished(ign): self._assertBuffer([ ">>> class Foo:", "... def bar(self):", "... print 'Hello, world!'", "... ", ">>> Foo().bar()", "Hello, world!", ">>> done" ]) return done.addCallback(finished) def testException(self): done = self.recvlineClient.expect("done") self._testwrite("raise Exception('foo bar baz')\n" "done") def finished(ign): self._assertBuffer([ ">>> raise Exception('foo bar baz')", "Traceback (most recent call last):", ' File "<console>", line 1, in ' + defaultFunctionName, "Exception: foo bar baz", ">>> done" ]) return done.addCallback(finished) def testControlC(self): done = self.recvlineClient.expect("done") self._testwrite("cancelled line" + manhole.CTRL_C + "done") def finished(ign): self._assertBuffer( [">>> cancelled line", "KeyboardInterrupt", ">>> done"]) return done.addCallback(finished) def test_interruptDuringContinuation(self): """ Sending ^C to Manhole while in a state where more input is required to complete a statement should discard the entire ongoing statement and reset the input prompt to the non-continuation prompt. """ continuing = self.recvlineClient.expect("things") self._testwrite("(\nthings") def gotContinuation(ignored): self._assertBuffer([">>> (", "... things"]) interrupted = self.recvlineClient.expect(">>> ") self._testwrite(manhole.CTRL_C) return interrupted continuing.addCallback(gotContinuation) def gotInterruption(ignored): self._assertBuffer( [">>> (", "... things", "KeyboardInterrupt", ">>> "]) continuing.addCallback(gotInterruption) return continuing def testControlBackslash(self): self._testwrite("cancelled line") partialLine = self.recvlineClient.expect("cancelled line") def gotPartialLine(ign): self._assertBuffer([">>> cancelled line"]) self._testwrite(manhole.CTRL_BACKSLASH) d = self.recvlineClient.onDisconnection return self.assertFailure(d, error.ConnectionDone) def gotClearedLine(ign): self._assertBuffer([""]) return partialLine.addCallback(gotPartialLine).addCallback( gotClearedLine) def testControlD(self): self._testwrite("1 + 1") helloWorld = self.wfd(self.recvlineClient.expect(r"\+ 1")) yield helloWorld helloWorld.getResult() self._assertBuffer([">>> 1 + 1"]) self._testwrite(manhole.CTRL_D + " + 1") cleared = self.wfd(self.recvlineClient.expect(r"\+ 1")) yield cleared cleared.getResult() self._assertBuffer([">>> 1 + 1 + 1"]) self._testwrite("\n") printed = self.wfd(self.recvlineClient.expect("3\n>>> ")) yield printed printed.getResult() self._testwrite(manhole.CTRL_D) d = self.recvlineClient.onDisconnection disconnected = self.wfd(self.assertFailure(d, error.ConnectionDone)) yield disconnected disconnected.getResult() testControlD = defer.deferredGenerator(testControlD) def testControlL(self): """ CTRL+L is generally used as a redraw-screen command in terminal applications. Manhole doesn't currently respect this usage of it, but it should at least do something reasonable in response to this event (rather than, say, eating your face). """ # Start off with a newline so that when we clear the display we can # tell by looking for the missing first empty prompt line. self._testwrite("\n1 + 1") helloWorld = self.wfd(self.recvlineClient.expect(r"\+ 1")) yield helloWorld helloWorld.getResult() self._assertBuffer([">>> ", ">>> 1 + 1"]) self._testwrite(manhole.CTRL_L + " + 1") redrew = self.wfd(self.recvlineClient.expect(r"1 \+ 1 \+ 1")) yield redrew redrew.getResult() self._assertBuffer([">>> 1 + 1 + 1"]) testControlL = defer.deferredGenerator(testControlL) def test_controlA(self): """ CTRL-A can be used as HOME - returning cursor to beginning of current line buffer. """ self._testwrite('rint "hello"' + '\x01' + 'p') d = self.recvlineClient.expect('print "hello"') def cb(ignore): self._assertBuffer(['>>> print "hello"']) return d.addCallback(cb) def test_controlE(self): """ CTRL-E can be used as END - setting cursor to end of current line buffer. """ self._testwrite('rint "hello' + '\x01' + 'p' + '\x05' + '"') d = self.recvlineClient.expect('print "hello"') def cb(ignore): self._assertBuffer(['>>> print "hello"']) return d.addCallback(cb) def testDeferred(self): self._testwrite("from twisted.internet import defer, reactor\n" "d = defer.Deferred()\n" "d\n") deferred = self.wfd(self.recvlineClient.expect("<Deferred #0>")) yield deferred deferred.getResult() self._testwrite("c = reactor.callLater(0.1, d.callback, 'Hi!')\n") delayed = self.wfd(self.recvlineClient.expect(">>> ")) yield delayed delayed.getResult() called = self.wfd( self.recvlineClient.expect("Deferred #0 called back: 'Hi!'\n>>> ")) yield called called.getResult() self._assertBuffer([ ">>> from twisted.internet import defer, reactor", ">>> d = defer.Deferred()", ">>> d", "<Deferred #0>", ">>> c = reactor.callLater(0.1, d.callback, 'Hi!')", "Deferred #0 called back: 'Hi!'", ">>> " ]) testDeferred = defer.deferredGenerator(testDeferred)
tuple will be either C{(SUCCESS, <return value>)} or C{(FAILURE, <Failure>)}. """ results = [] for f in callables: d = defer.maybeDeferred(f) thing = defer.waitForDeferred(d) yield thing try: results.append((defer.SUCCESS, thing.getResult())) except: results.append((defer.FAILURE, Failure())) if stopOnFirstError: break yield results _runSequentially = defer.deferredGenerator(_runSequentially) class _NoTrialMarker(Exception): """ No trial marker file could be found. Raised when trial attempts to remove a trial temporary working directory that does not contain a marker file. """ def _removeSafely(path): """
if not resource.exists(): log.error("File not found: %s" % (resource, )) raise HTTPError(responsecode.NOT_FOUND) # Do quota checks before we start deleting things myquota = waitForDeferred(resource.quota(request)) yield myquota myquota = myquota.getResult() if myquota is not None: old_size = waitForDeferred(resource.quotaSize(request)) yield old_size old_size = old_size.getResult() else: old_size = 0 # Do delete x = waitForDeferred(delete(resource_uri, resource.fp, depth)) yield x result = x.getResult() # Adjust quota if myquota is not None: d = waitForDeferred(resource.quotaSizeAdjust(request, -old_size)) yield d d.getResult() yield result deleteResource = deferredGenerator(deleteResource)
class BufferedStream(object): """A stream which buffers its data to provide operations like readline and readExactly.""" data = "" def __init__(self, stream): self.stream = stream def _readUntil(self, f): """Internal helper function which repeatedly calls f each time after more data has been received, until it returns non-None.""" while True: r = f() if r is not None: yield r return newdata = self.stream.read() if isinstance(newdata, defer.Deferred): newdata = defer.waitForDeferred(newdata) yield newdata newdata = newdata.getResult() if newdata is None: # End Of File newdata = self.data self.data = '' yield newdata return self.data += str(newdata) _readUntil = defer.deferredGenerator(_readUntil) def readExactly(self, size=None): """Read exactly size bytes of data, or, if size is None, read the entire stream into a string.""" if size is not None and size < 0: raise ValueError("readExactly: size cannot be negative: %s", size) def gotdata(): data = self.data if size is not None and len(data) >= size: pre, post = data[:size], data[size:] self.data = post return pre return self._readUntil(gotdata) def readline(self, delimiter='\r\n', size=None): """ Read a line of data from the string, bounded by delimiter. The delimiter is included in the return value. If size is specified, read and return at most that many bytes, even if the delimiter has not yet been reached. If the size limit falls within a delimiter, the rest of the delimiter, and the next line will be returned together. """ if size is not None and size < 0: raise ValueError("readline: size cannot be negative: %s" % (size, )) def gotdata(): data = self.data if size is not None: splitpoint = data.find(delimiter, 0, size) if splitpoint == -1: if len(data) >= size: splitpoint = size else: splitpoint += len(delimiter) else: splitpoint = data.find(delimiter) if splitpoint != -1: splitpoint += len(delimiter) if splitpoint != -1: pre = data[:splitpoint] self.data = data[splitpoint:] return pre return self._readUntil(gotdata) def pushback(self, pushed): """Push data back into the buffer.""" self.data = pushed + self.data def read(self): data = self.data if data: self.data = "" return data return self.stream.read() def _len(self): l = self.stream.length if l is None: return None return l + len(self.data) length = property(_len) def split(self, offset): off = offset - len(self.data) pre, post = self.stream.split(max(0, off)) pre = BufferedStream(pre) post = BufferedStream(post) if off < 0: pre.data = self.data[:-off] post.data = self.data[-off:] else: pre.data = self.data return pre, post
request.submethod = name try: method = getattr(self, method_name) # Also double-check via supported-reports property reports = self.supportedReports() test = lookupElement((namespace, name)) if not test: raise AttributeError() test = davxml.Report(test()) if test not in reports: raise AttributeError() except AttributeError: # # Requested report is not supported. # log.error("Unsupported REPORT %s for resource %s (no method %s)" % (encodeXMLName(namespace, name), self, method_name)) raise HTTPError( ErrorResponse(responsecode.FORBIDDEN, davxml.SupportedReport())) d = waitForDeferred(method(request, doc.root_element)) yield d yield d.getResult() http_REPORT = deferredGenerator(http_REPORT)
class MultipartMimeStream(object): implements(IStream) def __init__(self, stream, boundary): self.stream = BufferedStream(stream) self.boundary = "--" + boundary self.first = True def read(self): """ Return a deferred which will fire with a tuple of: (fieldname, filename, ctype, dataStream) or None when all done. Format errors will be sent to the errback. Returns None when all done. IMPORTANT: you *must* exhaust dataStream returned by this call before calling .read() again! """ if self.first: self.first = False d = self._readFirstBoundary() else: d = self._readBoundaryLine() d.addCallback(self._doReadHeaders) d.addCallback(self._gotHeaders) return d def _readFirstBoundary(self): # print("_readFirstBoundary") line = self.stream.readline(size=1024) if isinstance(line, defer.Deferred): line = defer.waitForDeferred(line) yield line line = line.getResult() if line != self.boundary + '\r\n': raise MimeFormatError("Extra data before first boundary: %r looking for: %r" % (line, self.boundary + '\r\n')) self.boundary = "\r\n" + self.boundary yield True return _readFirstBoundary = defer.deferredGenerator(_readFirstBoundary) def _readBoundaryLine(self): # print("_readBoundaryLine") line = self.stream.readline(size=1024) if isinstance(line, defer.Deferred): line = defer.waitForDeferred(line) yield line line = line.getResult() if line == "--\r\n": # THE END! yield False return elif line != "\r\n": raise MimeFormatError("Unexpected data on same line as boundary: %r" % (line,)) yield True return _readBoundaryLine = defer.deferredGenerator(_readBoundaryLine) def _doReadHeaders(self, morefields): # print("_doReadHeaders", morefields) if not morefields: return None return _readHeaders(self.stream) def _gotHeaders(self, headers): if headers is None: return None bws = _BoundaryWatchingStream(self.stream, self.boundary) self.deferred = bws.deferred ret = list(headers) ret.append(bws) return tuple(ret)
# Always need to have at least one propstat present (required by Prefer header behavior) if len(propstats) == 0: propstats.append(davxml.PropertyStatus( davxml.PropertyContainer(), davxml.Status.fromResponseCode(responsecode.OK) )) xml_resource = davxml.HRef(uri) xml_response = davxml.PropertyStatusResponse(xml_resource, *propstats) xml_responses.append(xml_response) # # Return response # yield MultiStatusResponse(xml_responses) http_PROPFIND = deferredGenerator(http_PROPFIND) ## # Utilities ## def propertyName(name): property_namespace, property_name = name pname = davxml.WebDAVUnknownElement() pname.namespace = property_namespace pname.name = property_name return pname
error = "Request XML body is required." log.error("Error: {err}", err=error) raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, error)) # # Parse request # acl = doc.root_element if not isinstance(acl, davxml.ACL): error = ("Request XML body must be an acl element." % (davxml.PropertyUpdate.sname(),)) log.error("Error: {err}", err=error) raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, error)) # # Do ACL merger # result = waitForDeferred(self.mergeAccessControlList(acl, request)) yield result result = result.getResult() # # Return response # if result is None: yield responsecode.OK else: yield ErrorResponse(responsecode.FORBIDDEN, result) http_ACL = deferredGenerator(http_ACL)
d = waitForDeferred(propertySearch(child, request)) yield d d = d.getResult() if d: # Check size of results is within limit matchcount += 1 if matchcount > max_number_of_matches: raise NumberOfMatchesWithinLimits(max_number_of_matches) d = waitForDeferred(prop_common.responseForHref( request, responses, element.HRef.fromString(uri), child, propertiesForResource, propElement )) yield d d.getResult() except NumberOfMatchesWithinLimits: log.error("Too many matching components in prinicpal-property-search report") raise HTTPError(ErrorResponse( responsecode.FORBIDDEN, element.NumberOfMatchesWithinLimits() )) yield MultiStatusResponse(responses) report_DAV__principal_property_search = deferredGenerator(report_DAV__principal_property_search)
def report_DAV__principal_property_search(self, request, principal_property_search): """ Generate a principal-property-search REPORT. (RFC 3744, section 9.4) """ # Verify root element if not isinstance(principal_property_search, element.PrincipalPropertySearch): raise ValueError("%s expected as root element, not %s." % (element.PrincipalPropertySearch.sname(), principal_property_search.sname())) # Only handle Depth: 0 depth = request.headers.getHeader("depth", "0") if depth != "0": log.error("Error in prinicpal-property-search REPORT, Depth set to %s" % (depth,)) raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,))) # Get a single DAV:prop element from the REPORT request body propertiesForResource = None propElement = None propertySearches = [] applyTo = False for child in principal_property_search.children: if child.qname() == (dav_namespace, "prop"): propertiesForResource = prop_common.propertyListForResource propElement = child elif child.qname() == (dav_namespace, "apply-to-principal-collection-set"): applyTo = True elif child.qname() == (dav_namespace, "property-search"): props = child.childOfType(element.PropertyContainer) props.removeWhitespaceNodes() match = child.childOfType(element.Match) propertySearches.append((props.children, str(match).lower())) def nodeMatch(node, match): """ See if the content of the supplied node matches the supplied text. Try to follow the matching guidance in rfc3744 section 9.4.1. @param prop: the property element to match. @param match: the text to match against. @return: True if the property matches, False otherwise. """ node.removeWhitespaceNodes() for child in node.children: if isinstance(child, PCDATAElement): comp = str(child).lower() if comp.find(match) != -1: return True else: return nodeMatch(child, match) else: return False def propertySearch(resource, request): """ Test the resource to see if it contains properties matching the property-search specification in this report. @param resource: the L{DAVFile} for the resource to test. @param request: the current request. @return: True if the resource has matching properties, False otherwise. """ for props, match in propertySearches: # Test each property for prop in props: try: propvalue = waitForDeferred(resource.readProperty(prop.qname(), request)) yield propvalue propvalue = propvalue.getResult() if propvalue and not nodeMatch(propvalue, match): yield False return except HTTPError: # No property => no match yield False return yield True propertySearch = deferredGenerator(propertySearch) # Run report try: resources = [] responses = [] matchcount = 0 if applyTo: for principalCollection in self.principalCollections(): uri = principalCollection.principalCollectionURL() resource = waitForDeferred(request.locateResource(uri)) yield resource resource = resource.getResult() if resource: resources.append((resource, uri)) else: resources.append((self, request.uri)) # Loop over all collections and principal resources within for resource, ruri in resources: # Do some optimisation of access control calculation by determining any inherited ACLs outside of # the child resource loop and supply those to the checkPrivileges on each child. filteredaces = waitForDeferred(resource.inheritedACEsforChildren(request)) yield filteredaces filteredaces = filteredaces.getResult() children = [] d = waitForDeferred(resource.findChildren("infinity", request, lambda x, y: children.append((x,y)), privileges=(element.Read(),), inherited_aces=filteredaces)) yield d d.getResult() for child, uri in children: if isPrincipalResource(child): d = waitForDeferred(propertySearch(child, request)) yield d d = d.getResult() if d: # Check size of results is within limit matchcount += 1 if matchcount > max_number_of_matches: raise NumberOfMatchesWithinLimits(max_number_of_matches) d = waitForDeferred(prop_common.responseForHref( request, responses, element.HRef.fromString(uri), child, propertiesForResource, propElement )) yield d d.getResult() except NumberOfMatchesWithinLimits: log.error("Too many matching components in prinicpal-property-search report") raise HTTPError(ErrorResponse( responsecode.FORBIDDEN, element.NumberOfMatchesWithinLimits() )) yield MultiStatusResponse(responses)
yield False return except: responses.add(Failure(), property) yield False return else: responses.add(responsecode.OK, property) # Only add undo action for those that succeed because those that fail will not have changed undoActions.append(undo) yield True return do = deferredGenerator(do) if isinstance(setOrRemove, davxml.Set): for property in properties: ok = waitForDeferred(do(self.writeProperty, property)) yield ok ok = ok.getResult() if not ok: gotError = True elif isinstance(setOrRemove, davxml.Remove): for property in properties: ok = waitForDeferred(do(self.removeProperty, property)) yield ok ok = ok.getResult() if not ok: gotError = True
# May need to add a location header addLocation(request, destination_uri) #x = waitForDeferred(copy(self.fp, destination.fp, destination_uri, depth)) x = waitForDeferred(put_common.storeResource(request, source=self, source_uri=request.uri, destination=destination, destination_uri=destination_uri, deletesource=False, depth=depth )) yield x yield x.getResult() http_COPY = deferredGenerator(http_COPY) def http_MOVE(self, request): """ Respond to a MOVE request. (RFC 2518, section 8.9) """ r = waitForDeferred(prepareForCopy(self, request)) yield r r = r.getResult() destination, destination_uri, depth = r # # Check authentication and access controls # parentURL = parentForURL(request.uri)
class liveGraphFragment(athena.LiveFragment): jsClass = u'statusGraphs.PS' kicks = ['renderGraphs', 'renderTables'] docFactory = loaders.xmlfile('statusGraphs.xml', templateDir=Settings.BaseDir + '/templates') loadAve = [] networkStats = {} last = {} def __init__(self, *a, **kw): super(liveGraphFragment, self).__init__(*a, **kw) reactor.callLater(2, self.kickEvents) self.loadAve = [[0.0, 0.0, 0.0] for i in xrange(20)] def render_interfaces(self, ctx, data): ifaces = self.getIFStat().keys() ifaces.sort() return ctx.tag[[[ tags.h3['Network Load:', iface], tags.div[tags.xml( '<canvas id="graph%s" height="128" width="256"/>' % iface)] ] for iface in ifaces]] def render_load(self, ctx, data): return ctx.tag[tags.h3["System Load"], tags.div[tags.xml( '<canvas id="graphload" height="128" width="256"/>')]] def getIFStat(self): ps = open("/proc/net/dev") results = {} for ln in ps: line = ln.strip() if "ppp" in line or "eth" in line: bits = line.split(':')[-1].split() ipin = float(bits[0]) ipout = float(bits[8]) iface = line.split(':')[0] if "eth" in iface: num = iface.strip('eth') iface = "Port " + num if "ppp" in iface: num = iface.strip('ppp') iface = "PPPoE " + num if iface != "lo": results[iface] = (ipin, ipout) ps.close() return results def processCall(self, name, args=[]): return utils.getProcessOutput(name, args, errortoo=1) def systemCalls(self): # EMAIL if os.path.exists('/etc/debian_version'): debianMode = True else: debianMode = False proclist = [ "dhcpd", "squid", "apache2", "openvpn.vpn", "samba", "courier-imapd", "sshd" ] namelist = { "dhcpd": u"DHCP Server", "dhcp3-server": u"DHCP Server", "squid": u"Web Proxy", "squid3": u"Web Proxy", "apache2": u"Web Server", "openvpn.vpn": u"VPN Server", "openvpn": u"VPN Server", "samba": u"File Server", "courier-imapd": u"IMAP Mail Server", "courier-imap": u"IMAP Mail Server", "sshd": u"Secure Shell" } if Settings.Mailer == "exim": mq = utils.getProcessOutput(Settings.BaseDir + '/syscripts/mailQueue.sh', [], errortoo=1) res = wait(mq) yield res mq = res.getResult() mq = unicode(mq.strip('\n')).split() proclist.append('exim') namelist['exim'] = "Mail Server" namelist['exim4'] = "Mail Server" else: mq = utils.getProcessOutput(Settings.BaseDir + '/syscripts/postQueue.sh', [], errortoo=1) res = wait(mq) yield res mq = res.getResult() if not "is empty" in mq: post = mq.strip('\n').split() mq = [unicode(post[4]), u"%s %s" % (post[1], post[2])] else: mq = [u"0", u"0"] proclist.append('postfix') namelist['postfix'] = u"Mail Server" # PROCESS LISTS procstatus = [] for proc in proclist: if debianMode: # Remap package names for Debian if proc == "openvpn.vpn": checkproc = "openvpn" proc = "openvpn" elif proc == "courier-imapd": checkproc = "couriertcpd" proc = "courier-imap" elif proc == "samba": checkproc = "smbd" elif proc == "dhcpd": checkproc = "dhcpd" proc = "dhcp3-server" elif proc == "exim": proc = "exim4" checkproc = "exim" elif proc == "squid": proc = "squid3" checkproc = "squid" else: checkproc = proc stat = self.processCall( Settings.BaseDir + '/syscripts/debInitStat.sh', [checkproc]) else: stat = self.processCall( Settings.BaseDir + '/syscripts/initStat.sh', [proc]) res = wait(stat) yield res stat = res.getResult() if "started" in stat: procstatus.append( (u'/images/green.gif', unicode(namelist[proc]), (u'Running. Click to stop this service', url.root.child('Proc').child(proc).child('stop'), u'Running'))) elif "stop" in stat: procstatus.append( (u'/images/red.gif', unicode(namelist[proc]), (u'Not running. Click to start this service', url.root.child('Proc').child(proc).child('start'), u'Stopped'))) else: procstatus.append( (u'/images/horange.gif', unicode(namelist[proc]), (u'Can\'t tell the status. Click to force a restart', url.root.child('Proc').child(proc).child('restart'), u'Unknown'))) # DISK UTILL d = self.processCall(Settings.BaseDir + '/syscripts/diskUtil.sh') res = wait(d) yield res d = unicode(res.getResult()) filesystem = [] for i in d.split('\n'): f = unicode(i.strip('\n')).split() filesystem.append(f) d = self.processCall(Settings.BaseDir + '/syscripts/raidStat.sh') res = wait(d) yield res d = unicode(res.getResult()) raids = {} if not "No such file" in d: # Don't try if the raid doesn't exist... thisRaid = "" for i in d.split('\n'): l = i.strip().strip('\n') if l: line = l.split() if "md" in line[0]: thisRaid = line[0] raids[thisRaid] = [line[2], line[3], line[4:]] else: raids[thisRaid].append(line[3]) raidstat = [] for i in raids.keys(): thisRaid = [ unicode(i), unicode(raids[i][1]), u"%s %s" % (raids[i][0], raids[i][3]) ] smart = [] for i in raids[i][2]: s = self.processCall( Settings.BaseDir + '/syscripts/smartStat.sh', [i[:3]]) res = wait(s) yield res s = res.getResult() smart.append(u"%s: %s" % (i, s.strip('\n').replace( '=== START OF READ SMART DATA SECTION ===', ''))) thisRaid.append(smart) raidstat.append(thisRaid) # SAMBA SESSIONS s = self.processCall('/usr/bin/net', ['status', 'shares', 'parseable']) res = wait(s) yield res s = res.getResult() shares = {} for i in s.split('\n'): if i: l = unicode(i).split('\\') if not shares.get(l[3], None): shares[l[3]] = [] shares[l[3]].append(l[0]) del s s = self.processCall('/usr/bin/net', ['status', 'sessions', 'parseable']) res = wait(s) yield res s = res.getResult() sessions = [] for i in s.split('\n'): if i: l = i.split('\\') if shares.get(l[1], None): shareopen = [u'%s' % k for k in shares[l[1]]] else: shareopen = [u""] sessions.append([ unicode(l[1]), unicode(l[2]), u"%s (%s)" % (l[3], l[4]), shareopen ]) del s uptime = self.processCall('/usr/bin/uptime') res = wait(uptime) yield res s = res.getResult() uptime = s.strip('\n').split(',') time = unicode(uptime[0].split()[0]) up = unicode(' '.join(uptime[0].split()[2:4])) users = unicode(uptime[2].split()[0]) yield mq, procstatus, filesystem, raidstat, shares, sessions, time, up, users systemCalls = deferredGenerator(systemCalls) def getSystemDetails(self): def returnTup(_): return _ def error(_): print "---------------------------------------------" print "Failure", _ print "---------------------------------------------" return ((0, 0), (), (), (), (), (), 0, 0, 0) return self.systemCalls().addCallbacks(returnTup, error) athena.expose(getSystemDetails) def kickEvents(self): for kick in self.kicks: self.callRemote(kick) def getLoadAve(self): def processDeferredResult(stdout): # Load averages del self.loadAve[0] self.loadAve.append( [float(i) for i in stdout.replace(',', '').split()[-3:]]) zipped = [[], [], []] for cnt, i in enumerate(self.loadAve): k = 0 for j in i: zipped[k].append((cnt, j)) k += 1 # Network stats latest = self.getIFStat() statsNow = {} for iface, bits in latest.items(): # if this is the first run, clear the stats with stuff if not self.networkStats.get(iface, False): self.networkStats[iface] = [(0.0, 0.0) for i in xrange(20)] rate = (0, 0) if self.last.get(iface, False): tel = zip(self.last[iface], bits) rate = [((i[1] - i[0]) * 8) / (2000) or 1.0 for i in tel] if len(self.networkStats[iface]) > 19: del self.networkStats[iface][0] self.networkStats[iface].append(rate) statzip = [[], []] for cnt, i in enumerate(self.networkStats[iface]): k = 0 for j in i: statzip[k].append((cnt, j)) k += 1 statsNow[unicode(iface)] = statzip self.last = latest skeys = statsNow.keys() return zipped, statsNow, skeys return utils.getProcessOutput( '/usr/bin/uptime', [], errortoo=1).addCallback(processDeferredResult) athena.expose(getLoadAve)
# print 'Discovered to be', authority, 'for', r ## else: ## # print 'Doing address lookup for', soFar, 'at', authority ## msg = defer.waitForDeferred(lookupAddress(soFar, authority, p)) ## yield msg ## msg = msg.getResult() ## records = msg.answers + msg.authority + msg.additional ## addresses = [r for r in records if r.type == dns.A] ## if addresses: ## authority = addresses[0].payload.dottedQuad() ## else: ## raise IOError("Resolution error") # print "Yielding authority", authority yield authority discoverAuthority = defer.deferredGenerator(discoverAuthority) def makePlaceholder(deferred, name): def placeholder(*args, **kw): deferred.addCallback(lambda r: getattr(r, name)(*args, **kw)) return deferred return placeholder class DeferredResolver: def __init__(self, resolverDeferred): self.waiting = [] resolverDeferred.addCallback(self.gotRealResolver) def gotRealResolver(self, resolver): w = self.waiting self.__dict__ = resolver.__dict__
headers.append((name, value)) if name == "content-type": ctype = http_headers.parseContentType(http_headers.tokenize((value,), foldCase=False)) elif name == "content-disposition": fieldname, filename = parseContentDispositionFormData(value) if ctype is None: ctype = http_headers.MimeType('application', 'octet-stream') if fieldname is None: raise MimeFormatError('Content-disposition invalid or omitted.') # End of headers, return (field name, content-type, filename) yield fieldname, filename, ctype return _readHeaders = defer.deferredGenerator(_readHeaders) class _BoundaryWatchingStream(object): def __init__(self, stream, boundary): self.stream = stream self.boundary = boundary self.data = '' self.deferred = defer.Deferred() length = None # unknown def read(self): if self.stream is None: if self.deferred is not None: