[Checkins] SVN: zope.proxy/trunk/ Python 3 support

Lennart Regebro regebro at gmail.com
Mon Mar 14 11:24:44 EDT 2011


Log message for revision 120909:
  Python 3 support

Changed:
  U   zope.proxy/trunk/CHANGES.txt
  U   zope.proxy/trunk/setup.py
  U   zope.proxy/trunk/src/zope/proxy/_zope_proxy_proxy.c
  U   zope.proxy/trunk/src/zope/proxy/tests/test_proxy.py

-=-
Modified: zope.proxy/trunk/CHANGES.txt
===================================================================
--- zope.proxy/trunk/CHANGES.txt	2011-03-14 09:15:22 UTC (rev 120908)
+++ zope.proxy/trunk/CHANGES.txt	2011-03-14 15:24:44 UTC (rev 120909)
@@ -5,6 +5,7 @@
 3.6.2 (unreleased)
 ------------------
 
+- Python 3 support.
 
 3.6.1 (2010-07-06)
 ------------------

Modified: zope.proxy/trunk/setup.py
===================================================================
--- zope.proxy/trunk/setup.py	2011-03-14 09:15:22 UTC (rev 120908)
+++ zope.proxy/trunk/setup.py	2011-03-14 15:24:44 UTC (rev 120909)
@@ -18,9 +18,18 @@
 ##############################################################################
 """Setup for zope.proxy package
 """
-import os
+import os, sys
 from setuptools import setup, Extension
 
+if sys.version_info >= (3,):
+    extra = dict(use_2to3 = True,
+                 convert_2to3_doctests = [
+                     'src/zope/i18nmessageid/messages.txt',
+                     ],
+                 )
+else:
+    extra = {}
+
 def read(*rnames):
     return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
 
@@ -61,4 +70,5 @@
           'setuptools'],
       include_package_data = True,
       zip_safe = False,
+      **extra
       )

Modified: zope.proxy/trunk/src/zope/proxy/_zope_proxy_proxy.c
===================================================================
--- zope.proxy/trunk/src/zope/proxy/_zope_proxy_proxy.c	2011-03-14 09:15:22 UTC (rev 120908)
+++ zope.proxy/trunk/src/zope/proxy/_zope_proxy_proxy.c	2011-03-14 15:24:44 UTC (rev 120909)
@@ -38,6 +38,41 @@
 empty_tuple = NULL;
 
 
+// Compatibility with Python 2
+#if PY_MAJOR_VERSION < 3
+  #define IS_STRING PyString_Check
+
+  #define MAKE_STRING(name) PyString_AS_STRING(name)
+
+  #define MOD_ERROR_VAL
+
+  #define MOD_SUCCESS_VAL(val)
+
+  #define MOD_INIT(name) void init##name(void)
+
+  #define MOD_DEF(ob, name, doc, methods) \
+          ob = Py_InitModule3(name, methods, doc);
+
+#else
+
+#define IS_STRING PyUnicode_Check
+
+  #define MAKE_STRING(name) PyBytes_AS_STRING( \
+          PyUnicode_AsUTF8String(name))
+
+  #define MOD_ERROR_VAL NULL
+
+  #define MOD_SUCCESS_VAL(val) val
+
+  #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
+
+  #define MOD_DEF(ob, name, doc, methods) \
+	  static struct PyModuleDef moduledef = { \
+	    PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \
+	  ob = PyModule_Create(&moduledef);
+#endif
+
+
 /*
  *   Slot methods.
  */
@@ -173,12 +208,16 @@
         base = PyTuple_GET_ITEM(mro, i);
 
         if (((PyTypeObject *)base) != &ProxyType) {
+#if PY_MAJOR_VERSION < 3
             if (PyClass_Check(base))
                 dict = ((PyClassObject *)base)->cl_dict;
-            else {
+            else 
+#endif
+	    {
                 assert(PyType_Check(base));
                 dict = ((PyTypeObject *)base)->tp_dict;
             }
+	    
             assert(dict && PyDict_Check(dict));
             res = PyDict_GetItem(dict, name);
             if (res != NULL)
@@ -195,13 +234,13 @@
     PyObject *wrapped;
     PyObject *descriptor;
     PyObject *res = NULL;
-    char *name_as_string;
+    const char *name_as_string;
     int maybe_special_name;
 
-#ifdef Py_USING_UNICODE
+#if PY_MAJOR_VERSION < 3 && defined(Py_USING_UNICODE)
     /* The Unicode to string conversion is done here because the
-       existing tp_getattro slots expect a string object as name
-       and we wouldn't want to break those. */
+       existing tp_setattro slots expect a string object as name
+       (except under Python 3) and we wouldn't want to break those. */
     if (PyUnicode_Check(name)) {
         name = PyUnicode_AsEncodedString(name, NULL, NULL);
         if (name == NULL)
@@ -209,14 +248,16 @@
     }
     else
 #endif
-    if (!PyString_Check(name)){
+
+    if (!IS_STRING(name)){
         PyErr_SetString(PyExc_TypeError, "attribute name must be string");
         return NULL;
     }
     else
         Py_INCREF(name);
+	
+    name_as_string = MAKE_STRING(name);
 
-    name_as_string = PyString_AS_STRING(name);
     wrapped = Proxy_GET_OBJECT(self);
     if (wrapped == NULL) {
         PyErr_Format(PyExc_RuntimeError,
@@ -232,9 +273,11 @@
         descriptor = WrapperType_Lookup(self->ob_type, name);
 
         if (descriptor != NULL) {
-            if (PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS)
-                && descriptor->ob_type->tp_descr_get != NULL) {
-
+            if (descriptor->ob_type->tp_descr_get != NULL 
+#if PY_MAJOR_VERSION < 3 // Always true in Python 3
+		&& PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS)
+#endif	    
+	    ){
               if (descriptor->ob_type->tp_descr_set == NULL)
                 {
                   res = PyObject_GetAttr(wrapped, name);
@@ -250,10 +293,13 @@
                         descriptor,
                         self,
                         (PyObject *)self->ob_type);
-            } else {
+            }
+	    else 
+	    {
                 Py_INCREF(descriptor);
                 res = descriptor;
             }
+
             goto finally;
         }
     }
@@ -269,12 +315,14 @@
 {
     PyObject *wrapped;
     PyObject *descriptor;
+    const char *name_as_string;
     int res = -1;
 
-#ifdef Py_USING_UNICODE
+#if PY_MAJOR_VERSION < 3 && defined(Py_USING_UNICODE)
     /* The Unicode to string conversion is done here because the
        existing tp_setattro slots expect a string object as name
-       and we wouldn't want to break those. */
+       (except under Python 3) and we wouldn't want to break those. */
+
     if (PyUnicode_Check(name)) {
         name = PyUnicode_AsEncodedString(name, NULL, NULL);
         if (name == NULL)
@@ -282,7 +330,8 @@
     }
     else
 #endif
-    if (!PyString_Check(name)){
+
+    if (!IS_STRING(name)){
         PyErr_SetString(PyExc_TypeError, "attribute name must be string");
         return -1;
     }
@@ -290,19 +339,24 @@
         Py_INCREF(name);
 
     descriptor = WrapperType_Lookup(self->ob_type, name);
+    
     if (descriptor != NULL
+#if PY_MAJOR_VERSION < 3 // This is always true in Python 3 (I think)
         && PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS) 
+#endif
         && descriptor->ob_type->tp_descr_set != NULL) 
       {
         res = descriptor->ob_type->tp_descr_set(descriptor, self, value);
         goto finally;
       }
 
+    name_as_string = MAKE_STRING(name);
+
     wrapped = Proxy_GET_OBJECT(self);
     if (wrapped == NULL) {
         PyErr_Format(PyExc_RuntimeError,
             "object is NULL; requested to set attribute '%s'",
-            PyString_AS_STRING(name));
+            name_as_string);
         goto finally;
     }
     res = PyObject_SetAttr(wrapped, name, value);
@@ -329,12 +383,13 @@
     return PyObject_Repr(Proxy_GET_OBJECT(wrapper));
 }
 
-
+#if PY_MAJOR_VERSION < 3
 static int
 wrap_compare(PyObject *wrapper, PyObject *v)
 {
     return PyObject_Compare(Proxy_GET_OBJECT(wrapper), v);
 }
+#endif
 
 static long
 wrap_hash(PyObject *self)
@@ -353,10 +408,6 @@
 }
 
 /*
- *   Number methods
- */
-
-/*
  * Number methods.
  */
 
@@ -372,6 +423,7 @@
     return nb->nb_int(self);
 }
 
+#if PY_MAJOR_VERSION < 3 // Python 3 has no long, oct or hex methods.
 static PyObject *
 call_long(PyObject *self)
 {
@@ -385,18 +437,6 @@
 }
 
 static PyObject *
-call_float(PyObject *self)
-{
-    PyNumberMethods *nb = self->ob_type->tp_as_number;
-    if (nb == NULL || nb->nb_float== NULL) {
-        PyErr_SetString(PyExc_TypeError,
-                        "object can't be converted to float");
-        return NULL;
-    }
-    return nb->nb_float(self);
-}
-
-static PyObject *
 call_oct(PyObject *self)
 {
     PyNumberMethods *nb = self->ob_type->tp_as_number;
@@ -420,7 +460,21 @@
     return nb->nb_hex(self);
 }
 
+#endif
+
 static PyObject *
+call_float(PyObject *self)
+{
+    PyNumberMethods *nb = self->ob_type->tp_as_number;
+    if (nb == NULL || nb->nb_float== NULL) {
+        PyErr_SetString(PyExc_TypeError,
+                        "object can't be converted to float");
+        return NULL;
+    }
+    return nb->nb_float(self);
+}
+
+static PyObject *
 call_ipow(PyObject *self, PyObject *other)
 {
     /* PyNumber_InPlacePower has three args.  How silly. :-) */
@@ -508,7 +562,9 @@
 BINOP(add, PyNumber_Add)
 BINOP(sub, PyNumber_Subtract)
 BINOP(mul, PyNumber_Multiply)
+#if PY_MAJOR_VERSION < 3 // Python 3 doesn't support the old integer division
 BINOP(div, PyNumber_Divide)
+#endif
 BINOP(mod, PyNumber_Remainder)
 BINOP(divmod, PyNumber_Divmod)
 
@@ -543,6 +599,7 @@
 BINOP(xor, PyNumber_Xor)
 BINOP(or, PyNumber_Or)
 
+#if PY_MAJOR_VERSION < 3 // Coercion is gone in Python 3
 static int
 wrap_coerce(PyObject **p_self, PyObject **p_other)
 {
@@ -581,6 +638,7 @@
     *p_other = right;
     return 0;
 }
+#endif
 
 UNOP(neg, PyNumber_Negative)
 UNOP(pos, PyNumber_Positive)
@@ -588,15 +646,19 @@
 UNOP(invert, PyNumber_Invert)
 
 UNOP(int, call_int)
-UNOP(long, call_long)
 UNOP(float, call_float)
+#if PY_MAJOR_VERSION < 3 // Python 3 has no long, oct or hex methods
+UNOP(long, call_long) 
 UNOP(oct, call_oct)
 UNOP(hex, call_hex)
+#endif
 
 INPLACE(add, PyNumber_InPlaceAdd)
 INPLACE(sub, PyNumber_InPlaceSubtract)
 INPLACE(mul, PyNumber_InPlaceMultiply)
+#if PY_MAJOR_VERSION < 3 // The old integer division operator is gone in Python 3
 INPLACE(div, PyNumber_InPlaceDivide)
+#endif
 INPLACE(mod, PyNumber_InPlaceRemainder)
 INPLACE(pow, call_ipow)
 INPLACE(lshift, PyNumber_InPlaceLshift)
@@ -715,7 +777,9 @@
     wrap_add,				/* nb_add */
     wrap_sub,				/* nb_subtract */
     wrap_mul,				/* nb_multiply */
+#if PY_MAJOR_VERSION < 3
     wrap_div,				/* nb_divide */
+#endif
     wrap_mod,				/* nb_remainder */
     wrap_divmod,			/* nb_divmod */
     wrap_pow,				/* nb_power */
@@ -729,19 +793,29 @@
     wrap_and,				/* nb_and */
     wrap_xor,				/* nb_xor */
     wrap_or,				/* nb_or */
+#if PY_MAJOR_VERSION < 3
     wrap_coerce,			/* nb_coerce */
+#endif
     wrap_int,				/* nb_int */
+#if PY_MAJOR_VERSION < 3
     wrap_long,				/* nb_long */
+#else
+    0,                                  /* The slot formerly known as nb_long */
+#endif
     wrap_float,				/* nb_float */
+#if PY_MAJOR_VERSION < 3
     wrap_oct,				/* nb_oct */
     wrap_hex,				/* nb_hex */
+#endif
 
     /* Added in release 2.0 */
     /* These require the Py_TPFLAGS_HAVE_INPLACEOPS flag */
     wrap_iadd,				/* nb_inplace_add */
     wrap_isub,				/* nb_inplace_subtract */
     wrap_imul,				/* nb_inplace_multiply */
+#if PY_MAJOR_VERSION < 3
     wrap_idiv,				/* nb_inplace_divide */
+#endif
     wrap_imod,				/* nb_inplace_remainder */
     (ternaryfunc)wrap_ipow,		/* nb_inplace_power */
     wrap_ilshift,			/* nb_inplace_lshift */
@@ -793,10 +867,16 @@
  * be associated with the wrapper itself.
  */
 
-statichere PyTypeObject
+// Python < 2.6	support:
+#ifndef PyVarObject_HEAD_INIT
+    #define PyVarObject_HEAD_INIT(type, size) \
+	PyObject_HEAD_INIT(type) size,
+#endif
+
+
+static PyTypeObject
 ProxyType = {
-    PyObject_HEAD_INIT(NULL)  /* PyObject_HEAD_INIT(&PyType_Type) */  
-    0,
+    PyVarObject_HEAD_INIT(NULL, 0)
     "zope.proxy.ProxyBase",
     sizeof(ProxyObject),
     0,
@@ -804,7 +884,11 @@
     wrap_print,				/* tp_print */
     0,					/* tp_getattr */
     0,					/* tp_setattr */
+#if PY_MAJOR_VERSION < 3
     wrap_compare,			/* tp_compare */
+#else
+    0,			                /* tp_reserved */
+#endif    
     wrap_repr,				/* tp_repr */
     &wrap_as_number,			/* tp_as_number */
     &wrap_as_sequence,			/* tp_as_sequence */
@@ -815,8 +899,13 @@
     wrap_getattro,			/* tp_getattro */
     wrap_setattro,			/* tp_setattro */
     0,					/* tp_as_buffer */
+#if PY_MAJOR_VERSION < 3
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC
-        | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_BASETYPE, /* tp_flags */
+        | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_BASETYPE, /* tp_flags */ 
+#else // Py_TPFLAGS_CHECKTYPES is always true in Python 3 and removed.
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC
+        | Py_TPFLAGS_BASETYPE, /* tp_flags */
+#endif
     0,					/* tp_doc */
     wrap_traverse,			/* tp_traverse */
     wrap_clear,				/* tp_clear */
@@ -1090,6 +1179,8 @@
   return result;
 }
 
+/* Module initialization */
+
 static char
 module___doc__[] =
 "Association between an object, a context object, and a dictionary.\n\
@@ -1114,14 +1205,14 @@
     {NULL}
 };
 
-void
-init_zope_proxy_proxy(void)
+MOD_INIT(_zope_proxy_proxy)
 {
-    PyObject *m = Py_InitModule3("_zope_proxy_proxy", 
-                                 module_functions, module___doc__);
+    PyObject *m;
+    
+    MOD_DEF(m, "_zope_proxy_proxy", module___doc__, module_functions)
 
     if (m == NULL)
-        return;
+        return MOD_ERROR_VAL;
 
     if (empty_tuple == NULL)
         empty_tuple = PyTuple_New(0);
@@ -1129,7 +1220,7 @@
     ProxyType.tp_free = _PyObject_GC_Del;
 
     if (PyType_Ready(&ProxyType) < 0)
-        return;
+        return MOD_ERROR_VAL;
 
     Py_INCREF(&ProxyType);
     PyModule_AddObject(m, "ProxyBase", (PyObject *)&ProxyType);
@@ -1137,8 +1228,12 @@
     if (api_object == NULL) {
         api_object = PyCObject_FromVoidPtr(&wrapper_capi, NULL);
         if (api_object == NULL)
-            return;
+        return MOD_ERROR_VAL;
     }
     Py_INCREF(api_object);
     PyModule_AddObject(m, "_CAPI", api_object);
+    
+    return MOD_SUCCESS_VAL(m);
+    
 }
+

Modified: zope.proxy/trunk/src/zope/proxy/tests/test_proxy.py
===================================================================
--- zope.proxy/trunk/src/zope/proxy/tests/test_proxy.py	2011-03-14 09:15:22 UTC (rev 120908)
+++ zope.proxy/trunk/src/zope/proxy/tests/test_proxy.py	2011-03-14 15:24:44 UTC (rev 120909)
@@ -120,6 +120,10 @@
         self.assert_(w.__class__ is o.__class__)
 
     def test_pickle_prevention(self):
+        # Proxies of old-style classes can't be pickled.
+        if sys.version > '3':
+            # No old-style classes in Python 3.
+            return
         w = self.new_proxy(Thing())
         self.assertRaises(pickle.PicklingError,
                           pickle.dumps, w)
@@ -162,8 +166,9 @@
         self.assert_(w2 <= o2)
 
     def test_proxy_callable(self):
-        w = self.new_proxy({}.get)
-        self.assert_(callable(w))
+        if sys.version < '3': # Gone in Python 3:
+            w = self.new_proxy({}.get)
+            self.assert_(callable(w))
 
     def test_proxy_item_protocol(self):
         w = self.new_proxy({})
@@ -220,8 +225,10 @@
 
     unops = [
         "-x", "+x", "abs(x)", "~x",
-        "int(x)", "long(x)", "float(x)",
+        "int(x)", "float(x)",
         ]
+    if sys.version < '3': # long is gone in Python 3
+        unops.append("long(x)") 
 
     def test_unops(self):
         P = self.new_proxy
@@ -236,7 +243,10 @@
     def test_odd_unops(self):
         # unops that don't return a proxy
         P = self.new_proxy
-        for func in hex, oct, lambda x: not x:
+        funcs = (lambda x: not x,)
+        if sys.version < '3':
+            funcs += (oct, hex)
+        for func in funcs:
             self.assertEqual(func(P(100)), func(100))
 
     binops = [
@@ -276,6 +286,9 @@
         self.assertEqual(pa, 4)
 
     def test_coerce(self):
+        if sys.version > '3':
+            # No coercion in Python 3
+            return
         P = self.new_proxy
 
         # Before 2.3, coerce() of two proxies returns them unchanged
@@ -339,7 +352,11 @@
         self.failUnless(b is y)
 
     def test_getslice(self):
-        # Lists have special slicing bahvior.
+        # These tests are moot under Python 3 as __slice__ isn't supported.
+        if sys.version > '3':
+            return
+        
+        # Lists have special slicing behavior.
         pList = self.new_proxy([1, 2])
         self.assertEqual(pList[-1:], [2])
         self.assertEqual(pList[-2:], [1, 2])



More information about the checkins mailing list