From 671416472072d9fb9209fd5e0bd0b7e0ed3d1d5a Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sun, 25 Apr 2021 21:39:34 -0700 Subject: [PATCH 1/3] Remove tox-wheel because it doesn't work for me --- tox.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/tox.ini b/tox.ini index 65ed82b385b..ccf807f24e8 100644 --- a/tox.ini +++ b/tox.ini @@ -19,7 +19,6 @@ minversion=3.21.4 requires= virtualenv>=20.4.2 - tox-wheel>=0.6.0 skip_missing_interpreters=True envlist=lint, mypy, apidocs, narrativedocs, newsfragment, @@ -35,7 +34,6 @@ sources = setup.py src/ docs/conch/examples docs/mail/examples docs/names/exampl ; docs/core/examples [testenv] -wheel = True ;; dependencies managed by extras in setup.cfg extras = ; The "nodeps" build still depends on PyHamcrest. From f5b18ad884fc98574910c1fb6f664e9cd0d39d66 Mon Sep 17 00:00:00 2001 From: Tom Most Date: Sun, 25 Apr 2021 22:20:29 -0700 Subject: [PATCH 2/3] Sessions: use the site reactor, not the global one Expose the reactor associated with the `Site` by renaming the `_reactor` attribute to `reactor` in its superclass `HTTPFactory`. Make the `Session` initializer take the reactor from its *site* argument when the explicit *reactor* argument isn't passed. It used to use the global reactor. There were a few tests that called the `Session` constructor with incorrect arguments. It looks like the `uid` used to be an integer, but it's bytes now. Also a few had the *uid* and *site* arguments swapped. I also added some missing documentation. --- src/twisted/web/http.py | 17 ++++----- src/twisted/web/newsfragments/10177.bugfix | 1 + src/twisted/web/server.py | 41 +++++++++++++++++----- src/twisted/web/test/requesthelper.py | 3 +- src/twisted/web/test/test_cgi.py | 3 +- src/twisted/web/test/test_web.py | 32 +++++++++++------ 6 files changed, 68 insertions(+), 29 deletions(-) create mode 100644 src/twisted/web/newsfragments/10177.bugfix diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py index 8923bc1966d..5842c47f72a 100644 --- a/src/twisted/web/http.py +++ b/src/twisted/web/http.py @@ -3099,8 +3099,8 @@ class HTTPFactory(protocol.ServerFactory): support writing to L{twisted.python.log} which, unfortunately, works with native strings. - @ivar _reactor: An L{IReactorTime} provider used to compute logging - timestamps. + @ivar reactor: An L{IReactorTime} provider used to manage connection + timeouts and compute logging timestamps. """ # We need to ignore the mypy error here, because @@ -3130,12 +3130,13 @@ def __init__( the access log. L{combinedLogFormatter} when C{None} is passed. @type logFormatter: L{IAccessLogFormatter} provider - @param reactor: A L{IReactorTime} provider used to manage connection - timeouts and compute logging timestamps. + @param reactor: An L{IReactorTime} provider used to manage connection + timeouts and compute logging timestamps. Defaults to the global + reactor. """ if not reactor: from twisted.internet import reactor - self._reactor = reactor + self.reactor = reactor if logPath is not None: logPath = os.path.abspath(logPath) @@ -3153,8 +3154,8 @@ def _updateLogDateTime(self): """ Update log datetime periodically, so we aren't always recalculating it. """ - self._logDateTime = datetimeToLogString(self._reactor.seconds()) - self._logDateTimeCall = self._reactor.callLater(1, self._updateLogDateTime) + self._logDateTime = datetimeToLogString(self.reactor.seconds()) + self._logDateTimeCall = self.reactor.callLater(1, self._updateLogDateTime) def buildProtocol(self, addr): p = protocol.ServerFactory.buildProtocol(self, addr) @@ -3164,7 +3165,7 @@ def buildProtocol(self, addr): # ideally be resolved by passing the reactor more generally to the # HTTPChannel, but that won't work for the TimeoutMixin until we fix # https://twistedmatrix.com/trac/ticket/8488 - p.callLater = self._reactor.callLater + p.callLater = self.reactor.callLater # timeOut needs to be on the Protocol instance cause # TimeoutMixin expects it there diff --git a/src/twisted/web/newsfragments/10177.bugfix b/src/twisted/web/newsfragments/10177.bugfix new file mode 100644 index 00000000000..fe7df425c72 --- /dev/null +++ b/src/twisted/web/newsfragments/10177.bugfix @@ -0,0 +1 @@ +Calling twisted.web.server.Site now registers its expiration timeout using the reactor associated with its twisted.web.server.Site. Site now a reactor attribute via its superclass, twisted.web.http.HTTPFactory. diff --git a/src/twisted/web/server.py b/src/twisted/web/server.py index dfc68fcf714..2e4c52744b2 100644 --- a/src/twisted/web/server.py +++ b/src/twisted/web/server.py @@ -686,12 +686,23 @@ class Session(components.Componentized): This utility class contains no functionality, but is used to represent a session. + @ivar site: The L{Site} that generated the session. + @type site: L{Site} + @ivar uid: A unique identifier for the session. @type uid: L{bytes} @ivar _reactor: An object providing L{IReactorTime} to use for scheduling expiration. - @ivar sessionTimeout: timeout of a session, in seconds. + + @ivar sessionTimeout: Time after last modification the session will expire, + in seconds. + @type sessionTimeout: L{float} + + @ivar lastModified: Time the C{touch()} method was last called (or time the + session was created). A UNIX timestamp as returned by + L{IReactorTime.seconds()}. + @type lastModified L{float} """ sessionTimeout = 900 @@ -701,11 +712,14 @@ class Session(components.Componentized): def __init__(self, site, uid, reactor=None): """ Initialize a session with a unique ID for that session. + + @param reactor: L{IReactorTime} used to schedule expiration of the + session. If C{None}, the reactor associated with I{site} is used. """ - components.Componentized.__init__(self) + super().__init__() if reactor is None: - from twisted.internet import reactor + reactor = site.reactor self._reactor = reactor self.site = site @@ -743,7 +757,7 @@ def expire(self): def touch(self): """ - Notify session modification. + Mark the session as modified, which resets expiration timer. """ self.lastModified = self._reactor.seconds() if self._expireCall is not None: @@ -758,13 +772,24 @@ class Site(http.HTTPFactory): """ A web site: manage log, sessions, and resources. - @ivar counter: increment value used for generating unique sessions ID. @ivar requestFactory: A factory which is called with (channel) and creates L{Request} instances. Default to L{Request}. + @ivar displayTracebacks: If set, unhandled exceptions raised during rendering are returned to the client as HTML. Default to C{False}. + @ivar sessionFactory: factory for sessions objects. Default to L{Session}. - @ivar sessionCheckTime: Deprecated. See L{Session.sessionTimeout} instead. + + @ivar sessions: Mapping of session IDs to objects returned by + C{sessionFactory}. + @type sessions: L{dict} mapping L{bytes} to L{Session} given the default + C{sessionFactory} + + @ivar counter: The number of sessions that have been generated. + @type counter: L{int} + + @ivar sessionCheckTime: Deprecated and unused. See + L{Session.sessionTimeout} instead. """ counter = 0 @@ -785,7 +810,7 @@ def __init__(self, resource, requestFactory=None, *args, **kwargs): @see: L{twisted.web.http.HTTPFactory.__init__} """ - http.HTTPFactory.__init__(self, *args, **kwargs) + super().__init__(*args, **kwargs) self.sessions = {} self.resource = resource if requestFactory is not None: @@ -832,7 +857,7 @@ def buildProtocol(self, addr): """ Generate a channel attached to this site. """ - channel = http.HTTPFactory.buildProtocol(self, addr) + channel = super().buildProtocol(addr) channel.requestFactory = self.requestFactory channel.site = self return channel diff --git a/src/twisted/web/test/requesthelper.py b/src/twisted/web/test/requesthelper.py index f94f6e5ea39..487733952ed 100644 --- a/src/twisted/web/test/requesthelper.py +++ b/src/twisted/web/test/requesthelper.py @@ -18,6 +18,7 @@ from twisted.internet.defer import Deferred from twisted.internet.address import IPv4Address, IPv6Address from twisted.internet.interfaces import ISSLTransport, IAddress +from twisted.internet.task import Clock from twisted.trial import unittest @@ -233,7 +234,7 @@ def __init__(self, postpath, session=None, client=None): self.postpath = postpath self.prepath = [] self.session = None - self.protoSession = session or Session(0, self) + self.protoSession = session or Session(site=None, uid=b"0", reactor=Clock()) self.args = {} self.requestHeaders = Headers() self.responseHeaders = Headers() diff --git a/src/twisted/web/test/test_cgi.py b/src/twisted/web/test/test_cgi.py index d8af9ca3257..be265e46963 100644 --- a/src/twisted/web/test/test_cgi.py +++ b/src/twisted/web/test/test_cgi.py @@ -376,8 +376,7 @@ def test_urlParameters(self): """ cgiFilename = self.writeCGI(URL_PARAMETER_CGI) portnum = self.startServer(cgiFilename) - url = "http://localhost:%d/cgi?param=1234" % (portnum,) - url = url.encode("ascii") + url = b"http://localhost:%d/cgi?param=1234" % (portnum,) agent = client.Agent(reactor) d = agent.request(b"GET", url) d.addCallback(client.readBody) diff --git a/src/twisted/web/test/test_web.py b/src/twisted/web/test/test_web.py index ca6c064f6c6..5ecd2c19947 100644 --- a/src/twisted/web/test/test_web.py +++ b/src/twisted/web/test/test_web.py @@ -214,17 +214,29 @@ def setUp(self): """ self.clock = Clock() self.uid = b"unique" - self.site = server.Site(resource.Resource()) - self.session = server.Session(self.site, self.uid, self.clock) + self.site = server.Site(resource.Resource(), reactor=self.clock) + self.session = server.Session(self.site, self.uid) self.site.sessions[self.uid] = self.session def test_defaultReactor(self): """ - If not value is passed to L{server.Session.__init__}, the global - reactor is used. + If no value is passed to L{server.Session.__init__}, the reactor + associated with the site is used. """ - session = server.Session(server.Site(resource.Resource()), b"123") - self.assertIdentical(session._reactor, reactor) + site = server.Site(resource.Resource(), reactor=Clock()) + session = server.Session(site, b"123") + self.assertIdentical(session._reactor, site.reactor) + + def test_explicitReactor(self): + """ + L{Session} accepts the reactor to use as a parameter. + """ + site = server.Site(resource.Resource()) + otherReactor = Clock() + + session = server.Session(site, b"123", reactor=otherReactor) + + self.assertIdentical(session._reactor, otherReactor) def test_startCheckingExpiration(self): """ @@ -761,7 +773,7 @@ def test_retrieveExistingSession(self): request = server.Request(d, 1) request.site = site request.sitepath = [] - mySession = server.Session(b"special-id", site) + mySession = server.Session(site, b"special-id") site.sessions[mySession.uid] = mySession request.received_cookies[b"TWISTED_SESSION"] = mySession.uid self.assertIs(request.getSession(), mySession) @@ -1827,11 +1839,11 @@ class ExplicitHTTPFactoryReactor(unittest.TestCase): def test_explicitReactor(self): """ L{http.HTTPFactory.__init__} accepts a reactor argument which is set on - L{http.HTTPFactory._reactor}. + L{http.HTTPFactory.reactor}. """ reactor = "I am a reactor!" factory = http.HTTPFactory(reactor=reactor) - self.assertIs(factory._reactor, reactor) + self.assertIs(factory.reactor, reactor) def test_defaultReactor(self): """ @@ -1841,4 +1853,4 @@ def test_defaultReactor(self): from twisted.internet import reactor factory = http.HTTPFactory() - self.assertIs(factory._reactor, reactor) + self.assertIs(factory.reactor, reactor) From c05455205022f597a1291c89d2036d7c7a8f4e38 Mon Sep 17 00:00:00 2001 From: Tom Most Date: Wed, 12 May 2021 20:16:44 -0700 Subject: [PATCH 3/3] Remove unused import --- src/twisted/web/test/test_web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/twisted/web/test/test_web.py b/src/twisted/web/test/test_web.py index 5ecd2c19947..62993a06b1c 100644 --- a/src/twisted/web/test/test_web.py +++ b/src/twisted/web/test/test_web.py @@ -15,7 +15,7 @@ from twisted.python import reflect, failure from twisted.python.filepath import FilePath from twisted.trial import unittest -from twisted.internet import reactor, interfaces +from twisted.internet import interfaces from twisted.internet.address import IPv4Address, IPv6Address from twisted.internet.task import Clock from twisted.web import server, resource