Commit 47481643 authored by Lennart Regebro's avatar Lennart Regebro

Python 3 support

parent 550c0c6e
......@@ -5,6 +5,7 @@ CHANGES
3.6.2 (unreleased)
------------------
- Python 3 support.
3.6.1 (2010-07-06)
------------------
......
......@@ -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 @@ setup(name='zope.proxy',
'setuptools'],
include_package_data = True,
zip_safe = False,
**extra
)
......@@ -38,6 +38,41 @@ static PyObject *
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 @@ WrapperType_Lookup(PyTypeObject *type, PyObject *name)
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 @@ wrap_getattro(PyObject *self, PyObject *name)
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 @@ wrap_getattro(PyObject *self, PyObject *name)
}
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 @@ wrap_getattro(PyObject *self, PyObject *name)
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 @@ wrap_getattro(PyObject *self, PyObject *name)
descriptor,
self,
(PyObject *)self->ob_type);
} else {
}
else
{
Py_INCREF(descriptor);
res = descriptor;
}
goto finally;
}
}
......@@ -269,12 +315,14 @@ wrap_setattro(PyObject *self, PyObject *name, PyObject *value)
{
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 @@ wrap_setattro(PyObject *self, PyObject *name, PyObject *value)
}
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 @@ wrap_setattro(PyObject *self, PyObject *name, PyObject *value)
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 @@ wrap_repr(PyObject *wrapper)
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)
......@@ -352,10 +407,6 @@ wrap_call(PyObject *self, PyObject *args, PyObject *kw)
return PyObject_CallObject(Proxy_GET_OBJECT(self), args);
}
/*
* Number methods
*/
/*
* Number methods.
*/
......@@ -372,6 +423,7 @@ call_int(PyObject *self)
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)
{
......@@ -384,18 +436,6 @@ call_long(PyObject *self)
return nb->nb_long(self);
}
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)
{
......@@ -420,6 +460,20 @@ call_hex(PyObject *self)
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)
{
......@@ -508,7 +562,9 @@ check2i(ProxyObject *self, PyObject *other,
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(and, PyNumber_And)
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 @@ wrap_coerce(PyObject **p_self, PyObject **p_other)
*p_other = right;
return 0;
}
#endif
UNOP(neg, PyNumber_Negative)
UNOP(pos, PyNumber_Positive)
......@@ -588,15 +646,19 @@ UNOP(abs, PyNumber_Absolute)
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_as_number = {
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_as_number = {
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 @@ wrap_methods[] = {
* 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 @@ ProxyType = {
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 @@ ProxyType = {
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 @@ wrapper_queryInnerProxy(PyObject *unused, PyObject *args)
return result;
}
/* Module initialization */
static char
module___doc__[] =
"Association between an object, a context object, and a dictionary.\n\
......@@ -1114,14 +1205,14 @@ module_functions[] = {
{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 @@ init_zope_proxy_proxy(void)
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 @@ init_zope_proxy_proxy(void)
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);
}
......@@ -120,6 +120,10 @@ class ProxyTestCase(unittest.TestCase):
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 @@ class ProxyTestCase(unittest.TestCase):
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 @@ class ProxyTestCase(unittest.TestCase):
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 @@ class ProxyTestCase(unittest.TestCase):
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 @@ class ProxyTestCase(unittest.TestCase):
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 @@ class ProxyTestCase(unittest.TestCase):
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])
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment