=== modified file 'doc/core/development/policy/test-standard.xhtml'
--- old/doc/core/development/policy/test-standard.xhtml	2011-11-13 18:49:49 +0000
+++ new/doc/core/development/policy/test-standard.xhtml	2012-03-13 08:43:48 +0000
@@ -292,7 +292,7 @@
 old code has decent coverage. Passing the <code>--coverage</code> option to
 to Trial will generate the coverage information in a file called 
 <code>coverage</code> which can be found in the <code>_trial_temp</code>
-folder. This option requires Python 2.3.3 or newer.</p>
+folder.</p>
 
 <h2>Associating Test Cases With Source Files</h2>
 

=== modified file 'twisted/conch/test/test_cftp.py'
--- old/twisted/conch/test/test_cftp.py	2011-07-14 18:05:14 +0000
+++ new/twisted/conch/test/test_cftp.py	2012-03-13 06:34:18 +0000
@@ -14,16 +14,9 @@
 
 _reason = None
 if Crypto and pyasn1:
-    try:
-        from twisted.conch import unix
-        from twisted.conch.scripts import cftp
-        from twisted.conch.test.test_filetransfer import FileTransferForTestAvatar
-    except ImportError, e:
-        # Python 2.3 compatibility fix
-        sys.modules.pop("twisted.conch.unix", None)
-        unix = None
-        _reason = str(e)
-        del e
+    from twisted.conch import unix
+    from twisted.conch.scripts import cftp
+    from twisted.conch.test.test_filetransfer import FileTransferForTestAvatar
 else:
     unix = None
 

=== modified file 'twisted/lore/lint.py'
--- old/twisted/lore/lint.py	2011-02-14 04:45:15 +0000
+++ new/twisted/lore/lint.py	2012-03-13 06:49:42 +0000
@@ -13,9 +13,6 @@
 from twisted.python import reflect
 
 
-# parser.suite in Python 2.3 raises SyntaxError, <2.3 raises parser.ParserError
-parserErrors = (SyntaxError, parser.ParserError)
-
 class TagChecker:
 
     def check(self, dom, filename):
@@ -131,11 +128,11 @@
                     text = '\n'.join(lines) + '\n'
                     try:
                         parser.suite(text)
-                    except parserErrors, e:
+                    except SyntaxError:
                         # Pretend the "..." idiom is syntactically valid
                         text = text.replace("...","'...'")
                         parser.suite(text)
-                except parserErrors, e:
+                except SyntaxError, e:
                     self._reportError(filename, node,
                                       'invalid python code:' + str(e))
 

=== modified file 'twisted/python/_release.py'
--- old/twisted/python/_release.py	2012-02-08 19:50:23 +0000
+++ new/twisted/python/_release.py	2012-03-13 06:51:00 +0000
@@ -424,8 +424,7 @@
     """
     Generate API documentation from source files using
     U{pydoctor<http://codespeak.net/~mwh/pydoctor/>}.  This requires
-    pydoctor to be installed and usable (which means you won't be able to
-    use it with Python 2.3).
+    pydoctor to be installed and usable.
     """
     def build(self, projectName, projectURL, sourceURL, packagePath,
               outputPath):

=== modified file 'twisted/python/reflect.py'
--- old/twisted/python/reflect.py	2012-03-07 19:55:14 +0000
+++ new/twisted/python/reflect.py	2012-03-13 06:54:32 +0000
@@ -438,21 +438,15 @@
     @raise _NoModuleFound: if no module was found.
     """
     try:
-        try:
-            return __import__(importName)
-        except ImportError:
-            excType, excValue, excTraceback = sys.exc_info()
-            while excTraceback:
-                execName = excTraceback.tb_frame.f_globals["__name__"]
-                if (execName is None or # python 2.4+, post-cleanup
-                    execName == importName): # python 2.3, no cleanup
-                    raise excType, excValue, excTraceback
-                excTraceback = excTraceback.tb_next
-            raise _NoModuleFound()
-    except:
-        # Necessary for cleaning up modules in 2.3.
-        sys.modules.pop(importName, None)
-        raise
+        return __import__(importName)
+    except ImportError:
+        excType, excValue, excTraceback = sys.exc_info()
+        while excTraceback:
+            execName = excTraceback.tb_frame.f_globals["__name__"]
+            if execName is None:
+                raise excType, excValue, excTraceback
+            excTraceback = excTraceback.tb_next
+        raise _NoModuleFound()
 
 
 

=== modified file 'twisted/python/syslog.py'
--- old/twisted/python/syslog.py	2011-02-14 04:45:15 +0000
+++ new/twisted/python/syslog.py	2012-03-13 07:02:32 +0000
@@ -12,7 +12,7 @@
 
 from twisted.python import log
 
-# These defaults come from the Python 2.3 syslog docs.
+# These defaults come from the Python syslog docs.
 DEFAULT_OPTIONS = 0
 DEFAULT_FACILITY = syslog.LOG_USER
 

=== modified file 'twisted/python/util.py'
--- old/twisted/python/util.py	2012-03-10 15:16:07 +0000
+++ new/twisted/python/util.py	2012-03-13 08:07:32 +0000
@@ -791,10 +791,8 @@
     representation makes sense.
 
     This is mostly necessary in Python 2.4 which implements L{id} to sometimes
-    return a negative value.  Python 2.3 shares this behavior, but also
-    implements hex and the %x format specifier to represent negative values as
-    though they were positive ones, obscuring the behavior of L{id}.  Python
-    2.5's implementation of L{id} always returns positive values.
+    return a negative value.  Python 2.5's implementation of L{id} always
+    returns positive values.
     """
     rval = _idFunction(obj)
     if rval < 0:
@@ -807,9 +805,8 @@
     Overwrite C{g}'s name and docstring with values from C{f}.  Update
     C{g}'s instance dictionary with C{f}'s.
 
-    To use this function safely you must use the return value. In Python 2.3,
-    L{mergeFunctionMetadata} will create a new function. In later versions of
-    Python, C{g} will be mutated and returned.
+    To use this function safely you must use the return value. C{g} will
+    be mutated and returned.
 
     @return: A function that has C{g}'s behavior and metadata merged from
         C{f}.
@@ -817,13 +814,7 @@
     try:
         g.__name__ = f.__name__
     except TypeError:
-        try:
-            merged = types.FunctionType(
-                g.func_code, g.func_globals,
-                f.__name__, inspect.getargspec(g)[-1],
-                g.func_closure)
-        except TypeError:
-            pass
+        pass
     else:
         merged = g
     try:

=== modified file 'twisted/spread/jelly.py'
--- old/twisted/spread/jelly.py	2011-06-24 00:57:16 +0000
+++ new/twisted/spread/jelly.py	2012-03-13 08:06:44 +0000
@@ -54,8 +54,7 @@
 The C{set} builtin and the C{sets.Set} class are serialized to the same
 thing, and unserialized to C{set} if available, else to C{sets.Set}. It means
 that there's a possibility of type switching in the serialization process. The
-solution is to always use C{set} if possible, and only use C{sets.Set} under
-Python 2.3; this can be accomplished by using L{twisted.python.compat.set}.
+solution is to always use C{set} if possible; this can be accomplished by using L{twisted.python.compat.set}.
 
 The same rule applies for C{frozenset} and C{sets.ImmutableSet}.
 

=== modified file 'twisted/test/test_reflect.py'
--- old/twisted/test/test_reflect.py	2012-03-07 19:55:14 +0000
+++ new/twisted/test/test_reflect.py	2012-03-13 08:08:45 +0000
@@ -217,8 +217,6 @@
         self.assertRaises(
             ZeroDivisionError,
             reflect.namedAny, "twisted.test.reflect_helper_ZDE")
-        # Make sure that this behavior is *consistent* for 2.3, where there is
-        # no post-failed-import cleanup
         self.assertRaises(
             ZeroDivisionError,
             reflect.namedAny, "twisted.test.reflect_helper_ZDE")
@@ -413,20 +411,6 @@
         self.assertEqual(['[0]', '[1][0]'], reflect.objgrep(d, a, reflect.isSame, maxDepth=2))
         self.assertEqual(['[0]', '[1][0]', '[1][1][0]'], reflect.objgrep(d, a, reflect.isSame, maxDepth=3))
 
-    def test_deque(self):
-        """
-        Test references search through a deque object. Only for Python > 2.3.
-        """
-        o = object()
-        D = deque()
-        D.append(None)
-        D.append(o)
-
-        self.assertIn("[1]", reflect.objgrep(D, o, reflect.isSame))
-
-    if deque is None:
-        test_deque.skip = "Deque not available"
-
 
 class GetClass(unittest.TestCase):
     def testOld(self):

=== modified file 'twisted/trial/runner.py'
--- old/twisted/trial/runner.py	2011-10-02 13:12:39 +0000
+++ new/twisted/trial/runner.py	2012-03-13 08:12:04 +0000
@@ -189,7 +189,7 @@
     DEPRECATED in Twisted 8.0.
 
     This class decorates the pyunit.TestCase class, mainly to work around the
-    differences between unittest in Python 2.3, 2.4, and 2.5. These
+    differences between unittest in Python 2.4, and 2.5. These
     differences are::
 
         - The way doctest unittests describe themselves
@@ -239,22 +239,6 @@
 
 
 
-class DocTestCase(PyUnitTestCase):
-    """
-    DEPRECATED in Twisted 8.0.
-    """
-
-    def id(self):
-        """
-        In Python 2.4, doctests have correct id() behaviour. In Python 2.3,
-        id() returns 'runit'.
-
-        Here we override id() so that at least it will always contain the
-        fully qualified Python name of the doctest.
-        """
-        return self._test.shortDescription()
-
-
 class TrialSuite(TestSuite):
     """
     Suite to wrap around every single test in a C{trial} run. Used internally

=== modified file 'twisted/trial/test/test_doctest.py'
--- old/twisted/trial/test/test_doctest.py	2011-06-05 20:52:48 +0000
+++ new/twisted/trial/test/test_doctest.py	2012-03-13 08:22:38 +0000
@@ -14,19 +14,6 @@
     Tests for Twisted's doctest support.
     """
 
-    def test_id(self):
-        """
-        Check that the id() of the doctests' case object contains the FQPN of
-        the actual tests. We need this because id() has weird behaviour w/
-        doctest in Python 2.3.
-        """
-        loader = runner.TestLoader()
-        suite = loader.loadDoctests(mockdoctest)
-        idPrefix = 'twisted.trial.test.mockdoctest.Counter'
-        for test in suite._tests:
-            self.assertIn(idPrefix, itrial.ITestCase(test).id())
-
-
     def test_basicTrialIntegration(self):
         """
         L{loadDoctests} loads all of the doctests in the given module.
@@ -43,8 +30,7 @@
         result = reporter.TestResult()
         suite.run(result)
         self.assertEqual(5, result.successes)
-        # doctest reports failures as errors in 2.3
-        self.assertEqual(2, len(result.errors) + len(result.failures))
+        self.assertEqual(2, len(result.failures))
 
 
     def test_expectedResults(self, count=1):

=== modified file 'twisted/trial/test/test_loader.py'
--- old/twisted/trial/test/test_loader.py	2011-07-14 18:05:14 +0000
+++ new/twisted/trial/test/test_loader.py	2012-03-13 08:19:39 +0000
@@ -510,15 +510,6 @@
 
 
 class PackageOrderingTest(packages.SysPathManglingTest):
-    if sys.version_info < (2, 4):
-        skip = (
-            "Python 2.3 import semantics make this behavior incorrect on that "
-            "version of Python as well as difficult to test.  The second "
-            "import of a package which raised an exception the first time it "
-            "was imported will succeed on Python 2.3, whereas it will fail on "
-            "later versions of Python.  Trial does not account for this, so "
-            "this test fails with inconsistencies between the expected and "
-            "the received loader errors.")
 
     def setUp(self):
         self.loader = runner.TestLoader()

=== modified file 'twisted/trial/unittest.py'
--- old/twisted/trial/unittest.py	2011-10-17 18:26:52 +0000
+++ new/twisted/trial/unittest.py	2012-03-13 08:25:43 +0000
@@ -1401,20 +1401,6 @@
         return self.run(result)
 
 
-    def run(self, result):
-        """
-        Call C{run} on every member of the suite.
-        """
-        # we implement this because Python 2.3 unittest defines this code
-        # in __call__, whereas 2.4 defines the code in run.
-        for test in self._tests:
-            if result.shouldStop:
-                break
-            test(result)
-        return result
-
-
-
 class TestDecorator(components.proxyForInterface(itrial.ITestCase,
                                                  "_originalTest")):
     """
@@ -1570,14 +1556,7 @@
 
 
 
-# Support for Python 2.3
-try:
-    iter(pyunit.TestSuite())
-except TypeError:
-    # Python 2.3's TestSuite doesn't support iteration. Let's monkey patch it!
-    def __iter__(self):
-        return iter(self._tests)
-    pyunit.TestSuite.__iter__ = __iter__
+iter(pyunit.TestSuite())
 
 
 

=== modified file 'twisted/web/http_headers.py'
--- old/twisted/web/http_headers.py	2011-05-28 13:00:26 +0000
+++ new/twisted/web/http_headers.py	2012-03-13 08:31:25 +0000
@@ -78,19 +78,6 @@
         return dict(self.items())
 
 
-    # Python 2.3 DictMixin.setdefault is defined so as not to have a default
-    # for the value parameter.  This is necessary to make this setdefault look
-    # like dict.setdefault on Python 2.3. -exarkun
-    def setdefault(self, name, value=None):
-        """
-        Retrieve the last value for the given header name.  If there are no
-        values present for that header, set the value to C{value} and return
-        that instead.  Note that C{None} is the default for C{value} for
-        backwards compatibility, but header values may only be of type C{str}.
-        """
-        return DictMixin.setdefault(self, name, value)
-
-
     # The remaining methods are only for efficiency.  The same behavior
     # should remain even if they are removed.  For details, see
     # <http://docs.python.org/lib/module-UserDict.html>.

=== modified file 'twisted/words/protocols/jabber/sasl.py'
--- old/twisted/words/protocols/jabber/sasl.py	2011-02-14 04:45:15 +0000
+++ new/twisted/words/protocols/jabber/sasl.py	2012-03-13 08:41:30 +0000
@@ -5,25 +5,12 @@
 XMPP-specific SASL profile.
 """
 
+from base64 import b64decode, b64encode
 import re
 from twisted.internet import defer
 from twisted.words.protocols.jabber import sasl_mechanisms, xmlstream
 from twisted.words.xish import domish
 
-# The b64decode and b64encode functions from the base64 module are new in
-# Python 2.4. For Python 2.3 compatibility, the legacy interface is used while
-# working around MIMEisms.
-
-try:
-    from base64 import b64decode, b64encode
-except ImportError:
-    import base64
-
-    def b64encode(s):
-        return "".join(base64.encodestring(s).split("\n"))
-
-    b64decode = base64.decodestring
-
 NS_XMPP_SASL = 'urn:ietf:params:xml:ns:xmpp-sasl'
 
 def get_mechanisms(xs):

=== modified file 'twisted/words/protocols/jabber/xmpp_stringprep.py'
--- old/twisted/words/protocols/jabber/xmpp_stringprep.py	2011-02-14 04:45:15 +0000
+++ new/twisted/words/protocols/jabber/xmpp_stringprep.py	2012-03-13 08:42:19 +0000
@@ -20,9 +20,7 @@
 
     warnings.warn("Accented and non-Western Jabber IDs will not be properly "
                   "case-folded with this version of Python, resulting in "
-                  "incorrect protocol-level behavior.  It is strongly "
-                  "recommended you upgrade to Python 2.3.2 or newer if you "
-                  "intend to use Twisted's Jabber support.")
+                  "incorrect protocol-level behavior.")
 
 else:
     import stringprep

