Repository: twisted/twisted

Issue:
Remove attribute lookups from twisted.web
#12171 has performance issues, probably because of many attribute lookups on `twisted.web.http`, e.g. in `twisted.web.server`. We should remove those, and just do "from twisted.web.http import X".

Discussion:

Patch:
diff --git a/src/twisted/web/newsfragments/12200.misc b/src/twisted/web/newsfragments/12200.misc
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/twisted/web/server.py b/src/twisted/web/server.py
index 6b823ff1db..cd0c51e329 100644
--- a/src/twisted/web/server.py
+++ b/src/twisted/web/server.py
@@ -34,7 +34,14 @@ from twisted.python.compat import nativeString, networkString
 from twisted.spread.pb import Copyable, ViewPoint
 from twisted.web import http, iweb, resource, util
 from twisted.web.error import UnsupportedMethod
-from twisted.web.http import unquote
+from twisted.web.http import (
+    NO_CONTENT,
+    NOT_MODIFIED,
+    HTTPFactory,
+    Request as BaseRequest,
+    datetimeToString,
+    unquote,
+)
 
 NOT_DONE_YET = 1
 
@@ -66,7 +73,7 @@ def _addressToTuple(addr):
 
 
 @implementer(iweb.IRequest)
-class Request(Copyable, http.Request, components.Componentized):
+class Request(Copyable, BaseRequest, components.Componentized):
     """
     An HTTP request.
 
@@ -92,7 +99,7 @@ class Request(Copyable, http.Request, components.Componentized):
     _log = Logger()
 
     def __init__(self, *args, **kw):
-        http.Request.__init__(self, *args, **kw)
+        BaseRequest.__init__(self, *args, **kw)
         components.Componentized.__init__(self)
 
     def getStateToCopyFor(self, issuer):
@@ -167,7 +174,7 @@ class Request(Copyable, http.Request, components.Componentized):
         try:
             getContentFile = self.channel.site.getContentFile
         except AttributeError:
-            http.Request.gotLength(self, length)
+            BaseRequest.gotLength(self, length)
         else:
             self.content = getContentFile(length)
 
@@ -186,7 +193,7 @@ class Request(Copyable, http.Request, components.Componentized):
 
         # set various default headers
         self.setHeader(b"server", version)
-        self.setHeader(b"date", http.datetimeToString())
+        self.setHeader(b"date", datetimeToString())
 
         # Resource Identification
         self.prepath = []
@@ -220,7 +227,7 @@ class Request(Copyable, http.Request, components.Componentized):
             # NOT_MODIFIED and NO_CONTENT responses. We also omit it if there
             # is a Content-Length header set to 0, as empty bodies don't need
             # a content-type.
-            needsCT = self.code not in (http.NOT_MODIFIED, http.NO_CONTENT)
+            needsCT = self.code not in (NOT_MODIFIED, NO_CONTENT)
             contentType = self.responseHeaders.getRawHeaders(b"content-type")
             contentLength = self.responseHeaders.getRawHeaders(b"content-length")
             contentLengthZero = contentLength and (contentLength[0] == b"0")
@@ -243,17 +250,17 @@ class Request(Copyable, http.Request, components.Componentized):
         if not self._inFakeHead:
             if self._encoder:
                 data = self._encoder.encode(data)
-            http.Request.write(self, data)
+            BaseRequest.write(self, data)
 
     def finish(self):
         """
-        Override C{http.Request.finish} for possible encoding.
+        Override C{BaseRequest.finish} for possible encoding.
         """
         if self._encoder:
             data = self._encoder.finish()
             if data:
-                http.Request.write(self, data)
-        return http.Request.finish(self)
+                BaseRequest.write(self, data)
+        return BaseRequest.finish(self)
 
     def render(self, resrc):
         """
@@ -748,7 +755,7 @@ version = networkString(f"TwistedWeb/{copyright.version}")
 
 
 @implementer(interfaces.IProtocolNegotiationFactory)
-class Site(http.HTTPFactory):
+class Site(HTTPFactory):
     """
     A web site: manage log, sessions, and resources.
 


Test patch:
diff --git a/src/twisted/web/test/test_wsgi.py b/src/twisted/web/test/test_wsgi.py
index ccb736c6bf..09e2b912e8 100644
--- a/src/twisted/web/test/test_wsgi.py
+++ b/src/twisted/web/test/test_wsgi.py
@@ -23,7 +23,7 @@ from twisted.python.failure import Failure
 from twisted.python.threadable import getThreadID
 from twisted.python.threadpool import ThreadPool
 from twisted.trial.unittest import TestCase
-from twisted.web import http
+from twisted.web import server
 from twisted.web.resource import IResource, Resource
 from twisted.web.server import Request, Site, version
 from twisted.web.test.test_web import DummyChannel
@@ -1275,7 +1275,7 @@ class StartResponseTests(WSGITestsMixin, TestCase):
         included in the response.
         """
         # Make the Date header value deterministic
-        self.patch(http, "datetimeToString", lambda: "Tuesday")
+        self.patch(server, "datetimeToString", lambda: "Tuesday")
 
         channel = DummyChannel()