diff options
author | Chris McDonough <chrism@plope.com> | 2011-12-26 22:02:42 -0500 |
---|---|---|
committer | Chris McDonough <chrism@plope.com> | 2011-12-26 22:02:42 -0500 |
commit | f4590b5a3badbee03751094d5b3b5c6504c2ee12 (patch) | |
tree | 88516439961f51c2cb923fbc43dd2eebd722d388 | |
parent | 62e63d9ce9ac7c28f31523a12860303a43930c98 (diff) | |
download | waitress-f4590b5a3badbee03751094d5b3b5c6504c2ee12.tar.gz |
expose all adjustments to serve()
-rw-r--r-- | TODO.txt | 3 | ||||
-rw-r--r-- | waitress/__init__.py | 50 | ||||
-rw-r--r-- | waitress/adjustments.py | 67 | ||||
-rw-r--r-- | waitress/interfaces.py | 6 | ||||
-rw-r--r-- | waitress/server.py | 49 | ||||
-rw-r--r-- | waitress/task.py | 6 | ||||
-rw-r--r-- | waitress/tests/test_adjustments.py | 67 | ||||
-rw-r--r-- | waitress/tests/test_init.py | 70 | ||||
-rw-r--r-- | waitress/tests/test_server.py | 57 | ||||
-rw-r--r-- | waitress/tests/test_task.py | 50 |
10 files changed, 222 insertions, 203 deletions
@@ -1,5 +1,3 @@ -- Expose more adjustments to serve(). - - 0.0.0.0 / IPv6. - Documentation. @@ -22,3 +20,4 @@ - Warn instead of relog in task.py. +- Get rid of dual-moded-ness. diff --git a/waitress/__init__.py b/waitress/__init__.py index 203e0b6..7f82cf5 100644 --- a/waitress/__init__.py +++ b/waitress/__init__.py @@ -1,53 +1,13 @@ -from waitress.task import ThreadedTaskDispatcher from waitress.server import WSGIHTTPServer -from waitress.adjustments import Adjustments -truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1')) - -def asbool(s): - """ Return the boolean value ``True`` if the case-lowered value of string - input ``s`` is any of ``t``, ``true``, ``y``, ``on``, or ``1``, otherwise - return the boolean value ``False``. If ``s`` is the value ``None``, - return ``False``. If ``s`` is already one of the boolean values ``True`` - or ``False``, return it.""" - if s is None: - return False - if isinstance(s, bool): - return s - s = str(s).strip() - return s.lower() in truthy - -def serve( - app, - host='0.0.0.0', - port=8080, - threads=4, - url_scheme='http', - connection_limit=100, - log_socket_errors=True, - ident=None, - verbose=True, - server=WSGIHTTPServer, # test shim - dispatcher=ThreadedTaskDispatcher, # test shim - ): - port = int(port) - threads = int(threads) - task_dispatcher = dispatcher() - task_dispatcher.set_thread_count(threads) - adj = Adjustments() - adj.url_scheme = url_scheme - adj.connection_limit = int(connection_limit) - adj.log_socket_errors = asbool(log_socket_errors) - server = server(app, host, port, task_dispatcher, ident=ident, adj=adj) - if verbose: # pragma: no cover +def serve(app, _server=WSGIHTTPServer, **kw): + # _server is a test shim + server = _server(app, **kw) + if server.adj.verbose: # pragma: no cover print('serving on http://%s:%s' % (server.ip, server.port)) server.run() -def serve_paste( - app, - global_conf, - **kw - ): +def serve_paste(app, global_conf, **kw): serve(app, **kw) return 0 diff --git a/waitress/adjustments.py b/waitress/adjustments.py index f1ceace..39b193f 100644 --- a/waitress/adjustments.py +++ b/waitress/adjustments.py @@ -14,6 +14,7 @@ """Adjustments are tunable parameters. """ import socket +import sys class Adjustments(object): """This class contains tunable communication parameters. @@ -22,9 +23,24 @@ class Adjustments(object): all sockets, or you can create a new instance of this class, change its attributes, and pass it to the channel constructors. """ + # host + host = '127.0.0.1' + + # port + port = 8080 + + # threads + threads = 4 + # wsgi url scheme url_scheme = 'http' + # verbose + verbose = True + + # ident + ident = 'waitress' + # backlog is the argument to pass to socket.listen(). backlog = 1024 @@ -66,4 +82,53 @@ class Adjustments(object): (socket.SOL_TCP, socket.TCP_NODELAY, 1), ] -default_adj = Adjustments() + def __init__(self, **kw): + for k, v in kw.items(): + if k == 'host': + v = str(v) + if k == 'port': + v = int(v) + if k == 'threads': + v = int(v) + if k == 'url_scheme': + v = str(v) + if k == 'backlog': + v = int(v) + if k == 'recv_bytes': + v = int(v) + if k == 'send_bytes': + v = int(v) + if k == 'outbuf_overflow': + v = int(v) + if k == 'inbuf_overflow': + v = int(v) + if k == 'connection_limit': + v = int(v) + if k == 'cleanup_interval': + v = int(v) + if k == 'channel_timeout': + v = int(v) + if k == 'log_socket_errors': + v = asbool(v) + if k == 'verbose': + v = asbool(v) + setattr(self, k, v) + if (sys.platform[:3] == "win" and + self.host == 'localhost' ): # pragma: no cover + self.host= '' + +truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1')) + +def asbool(s): + """ Return the boolean value ``True`` if the case-lowered value of string + input ``s`` is any of ``t``, ``true``, ``y``, ``on``, or ``1``, otherwise + return the boolean value ``False``. If ``s`` is the value ``None``, + return ``False``. If ``s`` is already one of the boolean values ``True`` + or ``False``, return it.""" + if s is None: + return False + if isinstance(s, bool): + return s + s = str(s).strip() + return s.lower() in truthy + diff --git a/waitress/interfaces.py b/waitress/interfaces.py index 4dd83a4..05a501c 100644 --- a/waitress/interfaces.py +++ b/waitress/interfaces.py @@ -209,12 +209,6 @@ class IServer(Interface): for more information. """) - SERVER_IDENT = Attribute(""" - This string identifies the server. By default - this is 'zope.server.' and should be - overridden. - """) - def getsockname(): """ Return the IP-address, port number pair to which this server's socket is bound""" diff --git a/waitress/server.py b/waitress/server.py index fba49f9..3344054 100644 --- a/waitress/server.py +++ b/waitress/server.py @@ -14,10 +14,10 @@ import asyncore import socket -import sys from waitress.adjustments import Adjustments from waitress.channel import HTTPServerChannel +from waitress.task import ThreadedTaskDispatcher from waitress import trigger class WSGIHTTPServer(asyncore.dispatcher, object): @@ -31,48 +31,35 @@ class WSGIHTTPServer(asyncore.dispatcher, object): """ channel_class = HTTPServerChannel - SERVER_IDENT = 'waitress' socketmod = socket # test shim def __init__(self, application, - ip, - port, - task_dispatcher, - ident=None, - adj=None, map=None, - start=True, # test shim - sock=None # test shim - ): + _start=True, # test shim + _sock=None, # test shim + _dispatcher=None, # test shim + **kw # adjustments + ): self.application = application - - if ident is not None: - self.SERVER_IDENT = ident - - if sys.platform[:3] == "win" and ip == 'localhost': - ip = '' - - self.ip = ip or '127.0.0.1' - - if adj is None: - adj = Adjustments() - self.adj = adj + self.adj = Adjustments(**kw) self.trigger = trigger.trigger(map) - asyncore.dispatcher.__init__(self, sock, map=map) - self.port = port - self.task_dispatcher = task_dispatcher - if sock is None: + if _dispatcher is None: + _dispatcher = ThreadedTaskDispatcher() + _dispatcher.set_thread_count(self.adj.threads) + self.task_dispatcher = _dispatcher + asyncore.dispatcher.__init__(self, _sock, map=map) + if _sock is None: self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() - self.bind((ip, port)) - self.server_name = self.computeServerName(ip) - if start: + self.bind((self.adj.host, self.adj.port)) + self.server_name = self.get_server_name(self.adj.host) + if _start: self.accept_connections() - def computeServerName(self, ip): - """Given an IP, try to determine the server name.""" + def get_server_name(self, ip): + """Given an IP or hostname, try to determine the server name.""" if ip: server_name = str(ip) else: diff --git a/waitress/task.py b/waitress/task.py index a63615a..8a1244d 100644 --- a/waitress/task.py +++ b/waitress/task.py @@ -364,7 +364,7 @@ class HTTPTask(object): # Set the Server and Date field, if not yet specified. This is needed # if the server is used as a proxy. - ident = self.channel.server.SERVER_IDENT + ident = self.channel.server.adj.ident if not server_header: response_headers.append(('Server', ident)) else: @@ -395,9 +395,9 @@ class HTTPTask(object): environ = {} environ['REQUEST_METHOD'] = request_data.command.upper() - environ['SERVER_PORT'] = str(server.port) + environ['SERVER_PORT'] = str(server.adj.port) environ['SERVER_NAME'] = server.server_name - environ['SERVER_SOFTWARE'] = server.SERVER_IDENT + environ['SERVER_SOFTWARE'] = server.adj.ident environ['SERVER_PROTOCOL'] = "HTTP/%s" % self.version environ['SCRIPT_NAME'] = '' environ['PATH_INFO'] = '/' + path diff --git a/waitress/tests/test_adjustments.py b/waitress/tests/test_adjustments.py new file mode 100644 index 0000000..73bb72e --- /dev/null +++ b/waitress/tests/test_adjustments.py @@ -0,0 +1,67 @@ +import unittest + +class Test_asbool(unittest.TestCase): + def _callFUT(self, s): + from waitress.adjustments import asbool + return asbool(s) + + def test_s_is_None(self): + result = self._callFUT(None) + self.assertEqual(result, False) + + def test_s_is_True(self): + result = self._callFUT(True) + self.assertEqual(result, True) + + def test_s_is_False(self): + result = self._callFUT(False) + self.assertEqual(result, False) + + def test_s_is_true(self): + result = self._callFUT('True') + self.assertEqual(result, True) + + def test_s_is_false(self): + result = self._callFUT('False') + self.assertEqual(result, False) + + def test_s_is_yes(self): + result = self._callFUT('yes') + self.assertEqual(result, True) + + def test_s_is_on(self): + result = self._callFUT('on') + self.assertEqual(result, True) + + def test_s_is_1(self): + result = self._callFUT(1) + self.assertEqual(result, True) + +class TestAdjustments(unittest.TestCase): + def _makeOne(self, **kw): + from waitress.adjustments import Adjustments + return Adjustments(**kw) + + def test_it(self): + inst = self._makeOne( + host='host', port='8080', threads='5', + url_scheme='https', backlog='20', recv_bytes='200', + send_bytes='300', outbuf_overflow='400', inbuf_overflow='500', + connection_limit='1000', cleanup_interval='1100', + channel_timeout='1200', log_socket_errors='true', + verbose='false') + self.assertEqual(inst.host, 'host') + self.assertEqual(inst.port, 8080) + self.assertEqual(inst.threads, 5) + self.assertEqual(inst.url_scheme, 'https') + self.assertEqual(inst.backlog, 20) + self.assertEqual(inst.recv_bytes, 200) + self.assertEqual(inst.send_bytes, 300) + self.assertEqual(inst.outbuf_overflow, 400) + self.assertEqual(inst.inbuf_overflow, 500) + self.assertEqual(inst.connection_limit, 1000) + self.assertEqual(inst.cleanup_interval, 1100) + self.assertEqual(inst.channel_timeout, 1200) + self.assertEqual(inst.log_socket_errors, True) + self.assertEqual(inst.verbose, False) + diff --git a/waitress/tests/test_init.py b/waitress/tests/test_init.py index 9427b1c..facc144 100644 --- a/waitress/tests/test_init.py +++ b/waitress/tests/test_init.py @@ -1,42 +1,5 @@ import unittest -class Test_asbool(unittest.TestCase): - def _callFUT(self, s): - from waitress import asbool - return asbool(s) - - def test_s_is_None(self): - result = self._callFUT(None) - self.assertEqual(result, False) - - def test_s_is_True(self): - result = self._callFUT(True) - self.assertEqual(result, True) - - def test_s_is_False(self): - result = self._callFUT(False) - self.assertEqual(result, False) - - def test_s_is_true(self): - result = self._callFUT('True') - self.assertEqual(result, True) - - def test_s_is_false(self): - result = self._callFUT('False') - self.assertEqual(result, False) - - def test_s_is_yes(self): - result = self._callFUT('yes') - self.assertEqual(result, True) - - def test_s_is_on(self): - result = self._callFUT('on') - self.assertEqual(result, True) - - def test_s_is_1(self): - result = self._callFUT(1) - self.assertEqual(result, True) - class Test_serve(unittest.TestCase): def _callFUT(self, app, **kw): from waitress import serve @@ -45,15 +8,10 @@ class Test_serve(unittest.TestCase): def test_it(self): server = DummyServerFactory() app = object() - - result = self._callFUT(app, server=server, - dispatcher=DummyTaskDispatcher, verbose=False) - + result = self._callFUT(app, _server=server) + self.assertEqual(server.app, app) self.assertEqual(result, None) self.assertEqual(server.ran, True) - self.assertEqual(server.host, '0.0.0.0') - self.assertEqual(server.port, 8080) - self.assertEqual(server.task_dispatcher.threads, 4) class Test_serve_paste(unittest.TestCase): def _callFUT(self, app, **kw): @@ -63,29 +21,23 @@ class Test_serve_paste(unittest.TestCase): def test_it(self): server = DummyServerFactory() app = object() - - result = self._callFUT(app, server=server, - dispatcher=DummyTaskDispatcher, verbose=False) - + result = self._callFUT(app, _server=server) + self.assertEqual(server.app, app) self.assertEqual(result, 0) self.assertEqual(server.ran, True) - self.assertEqual(server.host, '0.0.0.0') - self.assertEqual(server.port, 8080) - self.assertEqual(server.task_dispatcher.threads, 4) class DummyServerFactory(object): ran = False - def __call__(self, app, host, port, task_dispatcher, **kw): + def __call__(self, app, **kw): + self.adj = DummyAdj(kw) self.app = app - self.host = host - self.port = port - self.task_dispatcher = task_dispatcher self.kw = kw return self def run(self): self.ran = True -class DummyTaskDispatcher(object): - def set_thread_count(self, num): - self.threads = num - +class DummyAdj(object): + verbose = False + def __init__(self, kw): + self.__dict__.update(kw) + diff --git a/waitress/tests/test_server.py b/waitress/tests/test_server.py index 7d4cf1c..125d80c 100644 --- a/waitress/tests/test_server.py +++ b/waitress/tests/test_server.py @@ -3,52 +3,59 @@ import socket import unittest class TestWSGIHTTPServer(unittest.TestCase): - def _makeOne(self, application, ip, port, task_dispatcher=None, adj=None, - start=True, map=None, sock=None): + def _makeOne(self, application, host='127.0.0.1', port=62122, + _dispatcher=None, adj=None, map=None, _start=True, + _sock=None): from waitress.server import WSGIHTTPServer class TestServer(WSGIHTTPServer): def bind(self, v): pass return TestServer( application, - ip, - port, - task_dispatcher=task_dispatcher, - adj=adj, - start=start, + host=host, + port=port, map=map, - sock=sock) + _dispatcher=_dispatcher, + _start=_start, + _sock=_sock) - def _makeOneWithMap(self, adj=None, start=True, ip='127.0.0.1', port=62122, - app=None): + def _makeOneWithMap(self, adj=None, _start=True, host='127.0.0.1', + port=62122, app=None): sock = DummySock() task_dispatcher = DummyTaskDispatcher() map = {} - return self._makeOne(app, ip, port, task_dispatcher=task_dispatcher, - start=start, map=map, sock=sock) + return self._makeOne( + app, + host=host, + port=port, + map=map, + _sock=sock, + _dispatcher=task_dispatcher, + _start=_start, + ) def test_ctor_start_true(self): - inst = self._makeOneWithMap(start=True) + inst = self._makeOneWithMap(_start=True) self.assertEqual(inst.accepting, True) self.assertEqual(inst.socket.listened, 1024) def test_ctor_start_false(self): - inst = self._makeOneWithMap(start=False) + inst = self._makeOneWithMap(_start=False) self.assertEqual(inst.accepting, False) - def test_computeServerName_empty(self): - inst = self._makeOneWithMap(start=False) - result = inst.computeServerName('') + def test_get_server_name_empty(self): + inst = self._makeOneWithMap(_start=False) + result = inst.get_server_name('') self.assertTrue(result) - def test_computeServerName_with_ip(self): - inst = self._makeOneWithMap(start=False) - result = inst.computeServerName('127.0.0.1') + def test_get_server_name_with_ip(self): + inst = self._makeOneWithMap(_start=False) + result = inst.get_server_name('127.0.0.1') self.assertTrue(result) - def test_computeServerName_with_hostname(self): - inst = self._makeOneWithMap(start=False) - result = inst.computeServerName('fred.flintstone.com') + def test_get_server_name_with_hostname(self): + inst = self._makeOneWithMap(_start=False) + result = inst.get_server_name('fred.flintstone.com') self.assertEqual(result, 'fred.flintstone.com') def test_add_task(self): @@ -165,10 +172,6 @@ class DummyTask(object): self.written = '' def service(self): # pragma: no cover self.serviced = True - def write(self, val): - self.written += val - def get_environment(self): - return {} class DummyAdj: connection_limit = 1 diff --git a/waitress/tests/test_task.py b/waitress/tests/test_task.py index c9a8861..a48d507 100644 --- a/waitress/tests/test_task.py +++ b/waitress/tests/test_task.py @@ -143,7 +143,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(lines[0], b'HTTP/1.0 200 OK') self.assertEqual(lines[1], b'Connection: close') self.assertTrue(lines[2].startswith(b'Date:')) - self.assertEqual(lines[3], b'Server: hithere') + self.assertEqual(lines[3], b'Server: waitress') self.assertEqual(inst.close_on_finish, True) self.assertTrue(('Connection', 'close') in inst.response_headers) @@ -160,7 +160,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(lines[1], b'Connection: Keep-Alive') self.assertEqual(lines[2], b'Content-Length: 10') self.assertTrue(lines[3].startswith(b'Date:')) - self.assertEqual(lines[4], b'Server: hithere') + self.assertEqual(lines[4], b'Server: waitress') self.assertEqual(inst.close_on_finish, False) def test_build_response_header_v11_connection_closed_by_app(self): @@ -174,7 +174,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(lines[0], b'HTTP/1.1 200 OK') self.assertEqual(lines[1], b'Connection: close') self.assertTrue(lines[2].startswith(b'Date:')) - self.assertEqual(lines[3], b'Server: hithere') + self.assertEqual(lines[3], b'Server: waitress') self.assertTrue(('Connection', 'close') in inst.response_headers) self.assertEqual(inst.close_on_finish, True) @@ -189,7 +189,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(lines[0], b'HTTP/1.1 200 OK') self.assertEqual(lines[1], b'Connection: close') self.assertTrue(lines[2].startswith(b'Date:')) - self.assertEqual(lines[3], b'Server: hithere') + self.assertEqual(lines[3], b'Server: waitress') self.assertTrue(('Connection', 'close') in inst.response_headers) self.assertEqual(inst.close_on_finish, True) @@ -204,7 +204,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(lines[0], b'HTTP/1.1 200 OK') self.assertEqual(lines[1], b'Connection: close') self.assertTrue(lines[2].startswith(b'Date:')) - self.assertEqual(lines[3], b'Server: hithere') + self.assertEqual(lines[3], b'Server: waitress') self.assertTrue(('Connection', 'close') in inst.response_headers) self.assertEqual(inst.close_on_finish, True) @@ -219,7 +219,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(lines[0], b'HTTP/1.1 200 OK') self.assertEqual(lines[1], b'Connection: close') self.assertTrue(lines[2].startswith(b'Date:')) - self.assertEqual(lines[3], b'Server: hithere') + self.assertEqual(lines[3], b'Server: waitress') self.assertEqual(lines[4], b'Transfer-Encoding: notchunked') self.assertTrue(('Connection', 'close') in inst.response_headers) self.assertEqual(inst.close_on_finish, True) @@ -234,7 +234,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(len(lines), 4) self.assertEqual(lines[0], b'HTTP/1.1 200 OK') self.assertTrue(lines[1].startswith(b'Date:')) - self.assertEqual(lines[2], b'Server: hithere') + self.assertEqual(lines[2], b'Server: waitress') self.assertEqual(lines[3], b'Transfer-Encoding: chunked') self.assertEqual(inst.close_on_finish, False) @@ -248,7 +248,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(len(lines), 3) self.assertEqual(lines[0], b'HTTP/1.1 304 OK') self.assertTrue(lines[1].startswith(b'Date:')) - self.assertEqual(lines[2], b'Server: hithere') + self.assertEqual(lines[2], b'Server: waitress') self.assertEqual(inst.close_on_finish, False) def test_build_response_header_v11_200_no_content_length(self): @@ -261,7 +261,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(lines[0], b'HTTP/1.1 200 OK') self.assertEqual(lines[1], b'Connection: close') self.assertTrue(lines[2].startswith(b'Date:')) - self.assertEqual(lines[3], b'Server: hithere') + self.assertEqual(lines[3], b'Server: waitress') self.assertEqual(inst.close_on_finish, True) self.assertTrue(('Connection', 'close') in inst.response_headers) @@ -275,7 +275,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(lines[0], b'HTTP/8.1 200 OK') self.assertEqual(lines[1], b'Connection: close') self.assertTrue(lines[2].startswith(b'Date:')) - self.assertEqual(lines[3], b'Server: hithere') + self.assertEqual(lines[3], b'Server: waitress') self.assertEqual(inst.close_on_finish, True) self.assertTrue(('Connection', 'close') in inst.response_headers) @@ -291,7 +291,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(lines[1], b'Connection: close') self.assertTrue(lines[2].startswith(b'Date:')) self.assertEqual(lines[3], b'Server: abc') - self.assertEqual(lines[4], b'Via: hithere') + self.assertEqual(lines[4], b'Via: waitress') def test_build_response_header_date_exists(self): inst = self._makeOne() @@ -304,7 +304,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(lines[0], b'HTTP/1.0 200 OK') self.assertEqual(lines[1], b'Connection: close') self.assertTrue(lines[2].startswith(b'Date:')) - self.assertEqual(lines[3], b'Server: hithere') + self.assertEqual(lines[3], b'Server: waitress') def test_get_environment_already_cached(self): inst = self._makeOne() @@ -354,7 +354,7 @@ class TestHTTPTask(unittest.TestCase): self.assertEqual(environ['REQUEST_METHOD'], 'GET') self.assertEqual(environ['SERVER_PORT'], '80') self.assertEqual(environ['SERVER_NAME'], 'localhost') - self.assertEqual(environ['SERVER_SOFTWARE'], 'hithere') + self.assertEqual(environ['SERVER_SOFTWARE'], 'waitress') self.assertEqual(environ['SERVER_PROTOCOL'], 'HTTP/1.0') self.assertEqual(environ['SCRIPT_NAME'], '') self.assertEqual(environ['PATH_INFO'], '/') @@ -430,24 +430,16 @@ class DummyTask(object): def cancel(self): self.cancelled = True -class DummyServer(object): - SERVER_IDENT = 'hithere' - server_name = 'localhost' - port = 80 - def __init__(self, toraise=None): - self.toraise = toraise - self.executed = [] - def executeRequest(self, task): - self.executed.append(task) - if self.toraise: - raise self.toraise - - def application(self, environ, start_response): - start_response('200 OK', []) - return [b'abc'] - class DummyAdj(object): log_socket_errors = True + ident = 'waitress' + host = '127.0.0.1' + port = 80 + +class DummyServer(object): + server_name = 'localhost' + def __init__(self): + self.adj = DummyAdj() class DummyChannel(object): closed_when_done = False |