r47051 - clean up the implementation

hawkowl-TA+aISz0psMTMxyoc4vAAJOcrHinNvQL0E9HWUfgJXw@public.gmane.org Tue, 22 Mar 2016 06:05:34 -0600 (MDT)
Newsgroups gmane.comp.python.twisted.commits
Message-ID <[email protected]>
Author: hawkowl
Date: Tue Mar 22 06:05:26 2016
New Revision: 47051

Modified:
   branches/oldstyle-decorator-8244/twisted/python/_oldstyle.py
   branches/oldstyle-decorator-8244/twisted/test/test_nooldstyle.py

Log:
clean up the implementation

Modified: branches/oldstyle-decorator-8244/twisted/python/_oldstyle.py
==============================================================================
--- branches/oldstyle-decorator-8244/twisted/python/_oldstyle.py	(original)
+++ branches/oldstyle-decorator-8244/twisted/python/_oldstyle.py	Tue Mar 22 06:05:26 2016
@@ -1,25 +1,79 @@
+# -*- test-case-name: twisted.test.test_nooldstyle -*-
 # Copyright (c) Twisted Matrix Laboratories.
 # See LICENSE for details.
 
+"""
+Utilities to assist in the "flag day" new-style object transition.
+"""
+
 from __future__ import absolute_import, division
 
 import os
-
-from functools import wraps
+import types
 
 from twisted.python.compat import _PY3
 
-if _PY3 or int(os.environ.get('TWISTED_NEWSTYLE', 0)) == 0:
+
+def _ensureOldClass(cls):
+    """
+    Ensure that C{cls} is an old-style class.
+
+    @param cls: The class to check.
+
+    @return: C{None} if it is an old-style class.
+    @raises: L{ValueError} if it is a new-style class.
+    """
+    if not type(cls) is types.ClassType:
+        from twisted.python.reflect import fullyQualifiedName
+
+        raise ValueError(
+            ("twisted.python._oldstyle._oldStyle is being used to decorate a "
+             "new-style class ({cls}). This should only be used to "
+             "decorate old-style classes.").format(
+                 cls=fullyQualifiedName(cls)))
+
+
+if _PY3:
 
     def _oldStyle(cls):
+        """
+        No such thing as an old style class on Python 3.
+
+        @param cls: The class to wrap (or in this case, not wrap).
+        @return: C{cls}, unchanged
+        """
+        return cls
+
+elif int(os.environ.get('TWISTED_NEWSTYLE', 0)) == 0:
+
+    def _oldStyle(cls):
+        """
+        We don't want to override anything, but throw an exception if a
+        new-style class is decorated.
+
+        @param cls: The class to wrap (or in this case, not wrap).
+        @type cls: L{types.ClassType}
+
+        @return: C{cls}, unchanged
+        @raises: L{ValueError} if C{cls} is a new-style class.
+        """
+        _ensureOldClass(cls)
         return cls
 
 else:
 
     def _oldStyle(cls):
+        """
+        A decorator which converts old-style classes to new-style classes.
+
+        @param cls: An old-style class to convert to new-style.
+        @type cls: L{types.ClassType}
+        @return: A new-style subclass of C{cls}.
+        """
+        _ensureOldClass(cls)
 
         class OverwrittenClass(cls, object):
-            pass
+            __doc__ = cls.__doc__
 
         OverwrittenClass.__name__ = cls.__name__
         OverwrittenClass.__module__ = cls.__module__

Modified: branches/oldstyle-decorator-8244/twisted/test/test_nooldstyle.py
==============================================================================
--- branches/oldstyle-decorator-8244/twisted/test/test_nooldstyle.py	(original)
+++ branches/oldstyle-decorator-8244/twisted/test/test_nooldstyle.py	Tue Mar 22 06:05:26 2016
@@ -1,6 +1,10 @@
 # Copyright (c) Twisted Matrix Laboratories.
 # See LICENSE for details.
 
+"""
+Tests for L{twisted.python._oldstyle._oldStyle}.
+"""
+
 from __future__ import absolute_import, division
 
 import os
@@ -11,21 +15,100 @@
 from twisted.python.modules import getModule
 from twisted.python.compat import _PY3
 from twisted.trial import unittest
+from twisted.python._oldstyle import _oldStyle
 
-skip = None
+_skip = None
 
 if _PY3:
-    skip = "Not relevant on Python 3."
-elif not int(os.environ.get('TWISTED_NEWSTYLE', 0)) == 1:
-    skip = "Not running with TWISTED_NEWSTYLE=1"
+    _skip = "Not relevant on Python 3."
+elif int(os.environ.get('TWISTED_NEWSTYLE', 0)) == 0:
+    _skip = "Not running with TWISTED_NEWSTYLE=1"
+
+
+
+class SomeOldStyleClass:
+    """
+    I am a docstring!
+    """
+
+
+
+class SomeNewStyleClass(object):
+    """
+    Some new style class!
+    """
+
+
+
+class OldStyleDecoratorTests(unittest.TestCase):
+    """
+    Tests for L{_oldStyle}.
+    """
+
+    def test_makesNewStyle(self):
+        """
+        L{_oldStyle} wraps an old-style class and returns a new-style class
+        that descends from the old-style one.
+        """
+        self.assertEqual(type(SomeOldStyleClass), types.ClassType)
+        updatedClass = _oldStyle(SomeOldStyleClass)
+        self.assertEqual(type(updatedClass), type)
+        self.assertIn(SomeOldStyleClass, updatedClass.__bases__)
+
+
+    def test_carriesAttributes(self):
+        """
+        The class returned by L{_oldStyle} has the same C{__name__},
+        C{__module__}, and docstring (C{__doc__}) attributes as the original.
+        """
+        updatedClass = _oldStyle(SomeOldStyleClass)
+
+        self.assertEqual(updatedClass.__name__, SomeOldStyleClass.__name__)
+        self.assertEqual(updatedClass.__doc__, SomeOldStyleClass.__doc__)
+        self.assertEqual(updatedClass.__module__, SomeOldStyleClass.__module__)
+
+
+    def test_onlyOldStyleMayBeDecorated(self):
+        """
+        Using L{_oldStyle} on a new-style class on Python 2 will raise an
+        exception.
+        """
+
+        with self.assertRaises(ValueError) as e:
+            _oldStyle(SomeNewStyleClass)
+
+        self.assertEqual(
+            e.exception.args[0],
+            ("twisted.python._oldstyle._oldStyle is being used to decorate a "
+             "new-style class (twisted.test.test_nooldstyle.SomeNewStyleClass)"
+             ". This should only be used to decorate old-style classes."))
+
+
+    def test_noOpByDefault(self):
+        """
+        On Python 3 or on Py2 when C{TWISTED_NEWSTYLE} is not set, L{_oldStyle}
+        is a no-op.
+        """
+        updatedClass = _oldStyle(SomeOldStyleClass)
+        self.assertIs(updatedClass, SomeOldStyleClass)
+
+    if _PY3:
+        test_onlyOldStyleMayBeDecorated.skip = "Only relevant on Py2."
+
+    if _skip:
+        test_makesNewStyle.skip = _skip
+        test_carriesAttributes.skip = _skip
+    else:
+        test_noOpByDefault.skip = ("Only relevant when not running under "
+                                   "TWISTED_NEWSTYLE=1")
 
 
-if not skip:
+if not _skip:
 
     class NewStyleOnly(object):
         """
-        A testclass that takes a module and tests if the classes defined in it
-        are old-style.
+        A base testclass that takes a module and tests if the classes defined
+        in it are old-style.
 
         CAVEATS: This is maybe slightly dumb, and only looks in non-test
         modules (because some test modules have side effects). It also doesn't
@@ -34,6 +117,9 @@
         module = None
 
         def test_newStyleClassesOnly(self):
+            """
+            Test that C{self.module} has no old-style classes in it.
+            """
             try:
                 module = namedAny(self.module)
             except ImportError:
@@ -51,12 +137,15 @@
         if ".test." in x.name:
             continue
 
-        class test(NewStyleOnly, unittest.TestCase):
+        class Test(NewStyleOnly, unittest.TestCase):
+            """
+            See L{NewStyleOnly}.
+            """
             module = x.name
 
         acceptableName = x.name.replace(".", "_")
-        test.__name__ = acceptableName
-        locals().update({acceptableName: test})
+        Test.__name__ = acceptableName
+        locals().update({acceptableName: Test})
 
 else:
 
@@ -65,4 +154,7 @@
         Just skip, it doesn't make sense right now.
         """
         def test_newStyleClassesOnly(self):
+            """
+            No-op.
+            """
             raise unittest.SkipTest(skip)