From: Mark Wooding Date: Tue, 22 Oct 2019 18:12:28 +0000 (+0100) Subject: Port to Python 3. X-Git-Url: https://git.distorted.org.uk/~mdw/pyke/commitdiff_plain/c80de12d8d0827e0553fed2e4d392cb9bf3a378f Port to Python 3. Most of this is `#ifdef ...' ... `#endif' noise, with a few tweaks thrown in. Some notes on specific parts of the port. * buffer.c: The Python 3 buffer protocol is completely different. Read buffers work differently, but aren't problematic; write buffers can now be held open for an extended period, so we need the locking machinery that was added recently. * catacomb.c: Module initialization has changed, but isn't a great deal more difficult. * catacomb/__init__.py, catacomb/pwsafe.py, pock, pwsafe: There are a number of places which need language-version switches, but none of them is especially interesting. This diff is noisier than it should be because I couldn't adjust the indentation in advance. * mp.c: With the abolition of a separate fixnum type, `mp_frompylong' needed to express the fast path from a fixnum in a different way. * pwsafe: The hacking to alter the error-handling behaviour associated with the `stdout' stream is deeply unpleasant. Sorry. * pyke/pyke.h: Most of the porting work happens here, with alternative definitions for the various macros introduced earlier. * .gitignore: Ignore Python 3 `__pycache__/' turds. Python 3 leaves its pre-tokenized files in `__pycache__/' directories, which somehow manage to be much more objectionable than the loose Python 2 `*.pyc' files. Ignore these. * debian/: Add the necessary things to build a Python 3 extension package. --- diff --git a/mapping.c b/mapping.c index 3ad1ac7..ad02d25 100644 --- a/mapping.c +++ b/mapping.c @@ -257,6 +257,354 @@ static const PyTypeObject itemiter_pytype_skel = { 0 /* @tp_is_gc@ */ }; +/*----- Mapping views -----------------------------------------------------*/ + +#ifdef PY3 + +static PyTypeObject *keyview_pytype, *itemview_pytype, *valview_pytype; + +typedef struct view_pyobj { + PyObject_HEAD + PyObject *map; +} view_pyobj; +#define VIEW_MAP(o) (((view_pyobj *)(o))->map) + +static PyObject *gmap_mkview(PyObject *me, PyTypeObject *ty) +{ + view_pyobj *v = PyObject_NEW(view_pyobj, ty); + v->map = me; Py_INCREF(me); + return ((PyObject *)v); +} + +static void view_pydealloc(PyObject *me) + { Py_DECREF(VIEW_MAP(me)); FREEOBJ(me); } + +#define BINOP(op, update) \ + static PyObject *view_py##op(PyObject *me, PyObject *you) \ + { \ + PyObject *set = 0; \ + PyObject *rc = 0; \ + \ + set = PySet_New(me); if (!set) goto end; \ + if (!PyObject_CallMethod(set, #update, "(O)", you)) goto end; \ + rc = set; set = 0; \ + end: \ + Py_XDECREF(set); \ + return (rc); \ + } +BINOP(and, intersection_update) +BINOP(or, update) +BINOP(xor, symmetric_difference_update) +#undef BINOP + +static int all_contained_p(PyObject *x, PyObject *y) +{ + PyObject *i = 0, *e; + int b, rc = -1; + + i = PyObject_GetIter(x); if (!i) goto end; + for (;;) { + e = PyIter_Next(i); if (!e) break; + b = PySequence_Contains(y, e); Py_DECREF(e); if (b < 0) goto end; + if (!b) { rc = 0; goto end; } + } + if (PyErr_Occurred()) goto end; + rc = 1; +end: + Py_XDECREF(i); + return (rc); +} + +static Py_ssize_t view_pysize(PyObject *me) + { return (PyMapping_Size(VIEW_MAP(me))); } + +static PyObject *view_pyrichcompare(PyObject *me, PyObject *you, int op) +{ + PyObject *map = ITER_MAP(me); + Py_ssize_t mysz, yoursz; + int b; + + mysz = PyMapping_Size(map); if (mysz < 0) return (0); + yoursz = PyObject_Size(you); + if (yoursz < 0) { PyErr_Clear(); RETURN_NOTIMPL; } + + switch (op) { + case Py_EQ: + if (mysz != yoursz) RETURN_FALSE; + b = all_contained_p(you, me); + break; + case Py_NE: + if (mysz != yoursz) RETURN_TRUE; + b = all_contained_p(you, me); + break; + case Py_LT: + if (mysz >= yoursz) RETURN_FALSE; + b = all_contained_p(me, you); + break; + case Py_LE: + if (mysz > yoursz) RETURN_FALSE; + b = all_contained_p(me, you); + break; + case Py_GE: + if (mysz < yoursz) RETURN_FALSE; + b = all_contained_p(you, me); + break; + case Py_GT: + if (mysz <= yoursz) RETURN_FALSE; + b = all_contained_p(you, me); + break; + default: + abort(); + } + if (b < 0) return (0); + return (getbool(b)); +} + +static PyObject *keyview_pyiter(PyObject *me) + { return (gmap_mkiter(VIEW_MAP(me), keyiter_pytype)); } + +static int keyview_pyhaskey(PyObject *me, PyObject *k) +{ + PyObject *map = VIEW_MAP(me); + const struct gmap_ops *gmops = GMAP_OPS(map); + return (gmops->lookup(map, k, 0) ? 1 : PyErr_Occurred() ? -1 : 0); +} + +static int itemview_pyhaskey(PyObject *me, PyObject *it) +{ + PyObject *map = VIEW_MAP(me); + const struct gmap_ops *gmops = GMAP_OPS(map); + void *e; + int b; + PyObject *v; + + if (!PyTuple_Check(it) || PyTuple_GET_SIZE(it) != 2) return (0); + e = gmops->lookup(map, PyTuple_GET_ITEM(it, 0), 0); + if (!e) return (PyErr_Occurred() ? -1 : 0); + v = gmops->entry_value(map, e); if (!v) return (-1); + b = PyObject_RichCompareBool(v, PyTuple_GET_ITEM(it, 1), Py_EQ); + Py_DECREF(v); return (b); +} + +static PyObject *valview_pyiter(PyObject *me) + { return (gmap_mkiter(VIEW_MAP(me), valiter_pytype)); } + +static PyObject *itemview_pyiter(PyObject *me) + { return (gmap_mkiter(VIEW_MAP(me), itemiter_pytype)); } + +static const PyNumberMethods view_pynumber = { + 0, /* @nb_add@ */ + 0, /* @nb_subtract@ */ + 0, /* @nb_multiply@ */ +#ifdef PY2 + 0, /* @nb_divide@ */ +#endif + 0, /* @nb_remainder@ */ + 0, /* @nb_divmod@ */ + 0, /* @nb_power@ */ + 0, /* @nb_negative@ */ + 0, /* @nb_positive@ */ + 0, /* @nb_absolute@ */ + 0, /* @nb_nonzero@ */ + 0, /* @nb_invert@ */ + 0, /* @nb_lshift@ */ + 0, /* @nb_rshift@ */ + view_pyand, /* @nb_and@ */ + view_pyxor, /* @nb_xor@ */ + view_pyor, /* @nb_or@ */ + 0, /* @nb_coerce@ */ + 0, /* @nb_int@ */ + 0, /* @nb_long@ */ + 0, /* @nb_float@ */ + 0, /* @nb_oct@ */ + 0, /* @nb_hex@ */ +}; + +static const PySequenceMethods keyview_pysequence = { + view_pysize, /* @sq_length@ */ + 0, /* @sq_concat@ */ + 0, /* @sq_repeat@ */ + 0, /* @sq_item@ */ + 0, /* @sq_slice@ */ + 0, /* @sq_ass_item@ */ + 0, /* @sq_ass_slice@ */ + keyview_pyhaskey, /* @sq_contains@ */ + 0, /* @sq_inplace_concat@ */ + 0, /* @sq_inplace_repeat@ */ +}; + +static const PyTypeObject keyview_pytype_skel = { + PyVarObject_HEAD_INIT(0, 0) /* Header */ + "_KeyView", /* @tp_name@ */ + sizeof(view_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + view_pydealloc, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + 0, /* @tp_compare@ */ + 0, /* @tp_repr@ */ + PYNUMBER(view), /* @tp_as_number@ */ + PYSEQUENCE(keyview), /* @tp_as_sequence@ */ + 0, /* @tp_as_mapping@ */ + 0, /* @tp_hash@ */ + 0, /* @tp_call@ */ + 0, /* @tp_str@ */ + 0, /* @tp_getattro@ */ + 0, /* @tp_setattro@ */ + 0, /* @tp_as_buffer@ */ + Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ + Py_TPFLAGS_BASETYPE, + + /* @tp_doc@ */ + "View of a mapping's keys.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + view_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + keyview_pyiter, /* @tp_iter@ */ + 0, /* @tp_iternext@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + 0, /* @tp_getset@ */ + 0, /* @tp_base@ */ + 0, /* @tp_dict@ */ + 0, /* @tp_descr_get@ */ + 0, /* @tp_descr_set@ */ + 0, /* @tp_dictoffset@ */ + 0, /* @tp_init@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + abstract_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static const PySequenceMethods valview_pysequence = { + view_pysize, /* @sq_length@ */ + 0, /* @sq_concat@ */ + 0, /* @sq_repeat@ */ + 0, /* @sq_item@ */ + 0, /* @sq_slice@ */ + 0, /* @sq_ass_item@ */ + 0, /* @sq_ass_slice@ */ + 0, /* @sq_contains@ */ + 0, /* @sq_inplace_concat@ */ + 0, /* @sq_inplace_repeat@ */ +}; + +static const PyTypeObject valview_pytype_skel = { + PyVarObject_HEAD_INIT(0, 0) /* Header */ + "_ValueView", /* @tp_name@ */ + sizeof(view_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + view_pydealloc, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + 0, /* @tp_compare@ */ + 0, /* @tp_repr@ */ + PYNUMBER(view), /* @tp_as_number@ */ + PYSEQUENCE(valview), /* @tp_as_sequence@ */ + 0, /* @tp_as_mapping@ */ + 0, /* @tp_hash@ */ + 0, /* @tp_call@ */ + 0, /* @tp_str@ */ + 0, /* @tp_getattro@ */ + 0, /* @tp_setattro@ */ + 0, /* @tp_as_buffer@ */ + Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ + Py_TPFLAGS_BASETYPE, + + /* @tp_doc@ */ + "View of a mapping's values.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + valview_pyiter, /* @tp_iter@ */ + 0, /* @tp_iternext@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + 0, /* @tp_getset@ */ + 0, /* @tp_base@ */ + 0, /* @tp_dict@ */ + 0, /* @tp_descr_get@ */ + 0, /* @tp_descr_set@ */ + 0, /* @tp_dictoffset@ */ + 0, /* @tp_init@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + abstract_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +static const PySequenceMethods itemview_pysequence = { + view_pysize, /* @sq_length@ */ + 0, /* @sq_concat@ */ + 0, /* @sq_repeat@ */ + 0, /* @sq_item@ */ + 0, /* @sq_slice@ */ + 0, /* @sq_ass_item@ */ + 0, /* @sq_ass_slice@ */ + itemview_pyhaskey, /* @sq_contains@ */ + 0, /* @sq_inplace_concat@ */ + 0, /* @sq_inplace_repeat@ */ +}; + +static const PyTypeObject itemview_pytype_skel = { + PyVarObject_HEAD_INIT(0, 0) /* Header */ + "_ItemView", /* @tp_name@ */ + sizeof(view_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + view_pydealloc, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + 0, /* @tp_compare@ */ + 0, /* @tp_repr@ */ + PYNUMBER(view), /* @tp_as_number@ */ + PYSEQUENCE(itemview), /* @tp_as_sequence@ */ + 0, /* @tp_as_mapping@ */ + 0, /* @tp_hash@ */ + 0, /* @tp_call@ */ + 0, /* @tp_str@ */ + 0, /* @tp_getattro@ */ + 0, /* @tp_setattro@ */ + 0, /* @tp_as_buffer@ */ + Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ + Py_TPFLAGS_BASETYPE, + + /* @tp_doc@ */ + "View of a mapping's key/value items.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + view_pyrichcompare, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + itemview_pyiter, /* @tp_iter@ */ + 0, /* @tp_iternext@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + 0, /* @tp_getset@ */ + 0, /* @tp_base@ */ + 0, /* @tp_dict@ */ + 0, /* @tp_descr_get@ */ + 0, /* @tp_descr_set@ */ + 0, /* @tp_dictoffset@ */ + 0, /* @tp_init@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + abstract_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + +#endif + /*----- Other mapping protocol support ------------------------------------*/ Py_ssize_t gmap_pysize(PyObject *me) @@ -319,6 +667,19 @@ const PySequenceMethods gmap_pysequence = { 0 /* @sq_inplace_repeat@ */ }; +#ifdef PY3 + +PyObject *gmapmeth_keys(PyObject *me) + { return (gmap_mkview(me, keyview_pytype)); } + +PyObject *gmapmeth_values(PyObject *me) + { return (gmap_mkview(me, valview_pytype)); } + +PyObject *gmapmeth_items(PyObject *me) + { return (gmap_mkview(me, itemview_pytype)); } + +#else + PyObject *gmapmeth_has_key(PyObject *me, PyObject *arg) { PyObject *k; @@ -408,6 +769,8 @@ PyObject *gmapmeth_itervalues(PyObject *me) PyObject *gmapmeth_iteritems(PyObject *me) { return (gmap_mkiter(me, itemiter_pytype)); } +#endif + PyObject *gmap_pyiter(PyObject *me) { return gmap_mkiter(me, keyiter_pytype); } @@ -493,8 +856,12 @@ static int update_core(PyObject *me, PyObject *map) unsigned foundp; int rc = -1; - v = PyObject_CallMethod(map, "iteritems", 0); + v = PyObject_CallMethod(map, PY23("iteritems", "items"), 0); +#ifdef PY3 + if (v) { i = PyObject_GetIter(v); Py_DECREF(v); v = 0; } +#else i = v; v = 0; +#endif if (i) { for (;;) { @@ -576,6 +943,11 @@ void pyke_gmap_pyinit(void) INITTYPE(keyiter, root); INITTYPE(itemiter, root); INITTYPE(valiter, root); +#ifdef PY3 + INITTYPE(keyview, root); + INITTYPE(valview, root); + INITTYPE(itemview, root); +#endif } void pyke_gmap_pyinsert(PyObject *mod) @@ -583,6 +955,11 @@ void pyke_gmap_pyinsert(PyObject *mod) INSERT("_KeyIter", keyiter_pytype); INSERT("_ValueIter", valiter_pytype); INSERT("_ItemIter", itemiter_pytype); +#ifdef PY3 + INSERT("_KeyView", keyview_pytype); + INSERT("_ValueView", valview_pytype); + INSERT("_ItemView", itemview_pytype); +#endif } /*----- That's all, folks -------------------------------------------------*/ diff --git a/pyke-mLib.c b/pyke-mLib.c index fd6f895..30f6735 100644 --- a/pyke-mLib.c +++ b/pyke-mLib.c @@ -52,8 +52,10 @@ PyObject *getk64(kludge64 u) Py_DECREF(i); i = t; if ((j = PyLong_FromUnsignedLong(LO64(u))) == 0) goto end; if ((t = PyNumber_InPlaceOr(i, j)) == 0) goto end; +# ifdef PY2 Py_DECREF(i); i = t; if ((t = PyNumber_Int(i)) == 0) goto end; +# endif rc = t; end: Py_XDECREF(i); diff --git a/pyke.c b/pyke.c index 03f1c1f..330f15c 100644 --- a/pyke.c +++ b/pyke.c @@ -50,11 +50,13 @@ int convulong(PyObject *o, void *pp) PyObject *t; if (!o) VALERR("can't delete"); +#ifdef PY2 if (PyInt_Check(o)) { long i = PyInt_AS_LONG(o); if (i < 0) VALERR("must be nonnegative"); *p = i; } else +#endif { if ((t = PyNumber_Long(o)) == 0) goto end; *p = PyLong_AsUnsignedLong(t); @@ -110,6 +112,7 @@ int convbin(PyObject *o, void *pp) r->sz = BIN_LEN(o); return (1); } +#ifdef PY2 if (PyUnicode_Check(o)) { o = _PyUnicode_AsDefaultEncodedString(o, 0); if (!o) return (0); @@ -117,6 +120,7 @@ int convbin(PyObject *o, void *pp) r->sz = PyString_GET_SIZE(o); return (1); } +#endif return (PyObject_AsReadBuffer(o, &r->p, &r->sz) ? 0 : 1); } @@ -274,6 +278,9 @@ void *newtype(PyTypeObject *metaty, if (ty->ht_name) ty->ht_type.tp_name = TEXT_STR(ty->ht_name); ty->ht_slots = 0; +#ifdef PY3 + ty->ht_qualname = 0; +#endif (void)PyObject_INIT(&ty->ht_type, metaty); Py_INCREF(metaty); return (ty); @@ -281,6 +288,10 @@ void *newtype(PyTypeObject *metaty, void typeready(PyTypeObject *ty) { +#ifdef PY3 + PyHeapTypeObject *hty = (PyHeapTypeObject *)ty; + hty->ht_qualname = hty->ht_name; +#endif PyType_Ready(ty); PyDict_SetItemString(ty->tp_dict, "__module__", modname); } @@ -312,7 +323,8 @@ PyObject *mkexc(PyObject *mod, PyObject *base, while (mm->ml_name) { if ((func = PyCFunction_NewEx((/*unconst*/ PyMethodDef *)mm, 0, mod)) == 0 || - (meth = PyMethod_New(func, 0, exc)) == 0 || + (meth = PY23(PyMethod_New(func, 0, exc), + PyInstanceMethod_New(func))) == 0 || PyDict_SetItemString(dict, mm->ml_name, meth)) goto fail; Py_DECREF(func); func = 0; diff --git a/pyke.h b/pyke.h index 06c30c2..a60fd76 100644 --- a/pyke.h +++ b/pyke.h @@ -64,6 +64,15 @@ PRIVATE_SYMBOLS; /*----- Python version compatibility hacks --------------------------------*/ +/* Explicit version switching. */ +#if PY_VERSION_HEX >= 0x03000000 +# define PY3 1 +# define PY23(two, three) three +#else +# define PY2 1 +# define PY23(two, three) two +#endif + /* The handy `Py_TYPE' and `Py_SIZE' macros turned up in 2.6. Define them if * they're not already here. */ @@ -82,6 +91,18 @@ PRIVATE_SYMBOLS; # define PyVarObject_HEAD_INIT(super, sz) PyObject_HEAD_INIT(super) sz, #endif +/* Python 3 doesn't have `int', only `long', even though it's called `int' at + * the Python level. Provide some obvious macros to fill in the gaps. + */ +#ifdef PY3 +# define PyInt_Check PyLong_Check +# define PyInt_FromLong PyLong_FromLong +# define PyInt_AS_LONG PyLong_AS_LONG +# define PyInt_AsLong PyLong_AsLong +# define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask +# define PyNumber_Int PyNumber_Long +#endif + /* Python 3.2 changed the type of hash values, so paper over this annoying * difference. */ @@ -89,6 +110,13 @@ PRIVATE_SYMBOLS; typedef long Py_hash_t; #endif +/* Python 3 always has the `CHECKTYPES' behaviour, and doesn't define the + * flag. + */ +#ifdef PY3 +# define Py_TPFLAGS_CHECKTYPES 0 +#endif + /* Plain octet strings. Python 2 calls these `str', while Python 3 calls * them `bytes'. We call them `bin' here, and define the following. * @@ -115,6 +143,25 @@ PRIVATE_SYMBOLS; # * `YN' is a format character for `PyArg_ParseTuple...' for retrieving an * octet string and length from any sort-of vaguely binary-ish object. */ +#ifdef PY3 +# define BINOBJ PyBytesObject +# define BIN_TYPE PyBytes_Type +# define BIN_CHECK(obj) PyBytes_Check(obj) +# define BIN_PTR(obj) PyBytes_AS_STRING(obj) +# define BIN_LEN(obj) PyBytes_GET_SIZE(obj) +# define BIN_FROMSTR(str) PyBytes_FromString(str) +# define BIN_FROMSTRLEN(str, len) PyBytes_FromStringAndSize(str, len) +# define BIN_FORMAT PyBytes_FromFormat +# define BIN_VFORMAT PyBytes_FromFormatV +# define BIN_PREPAREWRITE(obj, ptr, sz) do { \ + (obj) = PyBytes_FromStringAndSize(0, (sz)); \ + (ptr) = PyBytes_AS_STRING(obj); \ + } while (0) +# define BIN_DONEWRITE(obj, sz) do Py_SIZE(obj) = (sz); while (0) +# define BIN_SETLEN(obj, len) do Py_SIZE(obj) = (len); while (0) +# define Y "y" +# define YN "y#" +#else # define BINOBJ PyStringObject # define BIN_TYPE PyString_Type # define BIN_CHECK(obj) PyString_Check(obj) @@ -132,6 +179,7 @@ PRIVATE_SYMBOLS; # define BIN_SETLEN(obj, len) do Py_SIZE(obj) = (len); while (0) # define Y "s" # define YN "s#" +#endif /* Text strings. Both Python 2 and Python 3 call these `str', but they're * very different because a Python 3 `str' is Unicode inside. When dealing @@ -161,6 +209,52 @@ PRIVATE_SYMBOLS; * * (Use `s' and `s#' in `PyArg_ParseTuple...'.) */ +#ifdef PY3 +# define TEXTOBJ PyUnicodeObject +# define TEXT_TYPE PyUnicode_Type +# define TEXT_CHECK(obj) PyUnicode_Check(obj) +# if PY_VERSION_HEX >= 0x03030000 +# define TEXT_PTR(obj) PyUnicode_AsUTF8(obj) +# define TEXT_STR(obj) PyUnicode_AsUTF8(obj) +# define TEXT_PTRLEN(obj, ptr, len) do { \ + Py_ssize_t len_; \ + (ptr) = PyUnicode_AsUTF8AndSize((obj), &len_); \ + (len) = len_; \ + } while (0) +# define TEXT_PREPAREWRITE(obj, ptr, sz) do { \ + (obj) = PyUnicode_New((sz), 127); \ + (ptr) = PyUnicode_DATA(obj); \ + } while (0) +# define TEXT_DONEWRITE(obj, len) do { \ + size_t len_ = (len); \ + assert(PyUnicode_IS_COMPACT_ASCII(obj)); \ + ((char *)PyUnicode_DATA(obj))[len_] = 0; \ + ((PyASCIIObject *)(obj))->length = len_; \ + } while (0) +# else +# define TEXT_PTR(obj) _PyUnicode_AsString(obj) +# define TEXT_STR(obj) _PyUnicode_AsString(obj) +# define TEXT_PTRLEN(obj, ptr, len) do { \ + Py_ssize_t len_; \ + (ptr) = _PyUnicode_AsStringAndSize((obj), &len_); \ + (len) = len_; \ + } while (0) +# define TEXT_PREPAREWRITE(obj, ptr, sz) do { \ + (obj) = PyBytes_FromStringAndSize(0, (sz)); \ + (ptr) = PyBytes_AS_STRING(obj); \ + } while (0) +# define TEXT_DONEWRITE(obj, len) do { \ + PyObject *new_; \ + Py_SIZE(obj) = (len); \ + new_ = PyUnicode_FromEncodedObject(obj, 0, 0); \ + assert(new_); Py_DECREF(obj); (obj) = new_; \ + } while (0) +# endif +# define TEXT_FORMAT PyUnicode_FromFormat +# define TEXT_VFORMAT PyUnicode_FromFormatV +# define TEXT_FROMSTR(str) PyUnicode_FromString(str) +# define TEXT_FROMSTRLEN(str, len) PyUnicode_FromStringAndSize(str, len) +#else # define TEXTOBJ PyStringObject # define TEXT_TYPE PyString_Type # define TEXT_CHECK(obj) PyString_Check(obj) @@ -179,6 +273,7 @@ PRIVATE_SYMBOLS; # define TEXT_DONEWRITE(obj, sz) do { Py_SIZE(obj) = (sz); } while (0) # define TEXT_FROMSTR(str) PyString_FromString(str) # define TEXT_FROMSTRLEN(str, len) PyString_FromStringAndSize(str, len) +#endif /*----- Utilities for returning values and exceptions ---------------------*/ @@ -556,6 +651,13 @@ typedef struct gmap_pyobj { #define GMAP_NAMETHDECL(func, doc) \ extern PyObject *gmapmeth_##func(PyObject *); +#ifdef PY3 +# define GMAP_DOROMETHODS(METH, KWMETH, NAMETH) \ + NAMETH(keys, "D.keys() -> LIST") \ + NAMETH(values, "D.values() -> LIST") \ + NAMETH(items, "D.items() -> LIST") \ + KWMETH(get, "D.get(KEY, [default = None]) -> VALUE") +#else # define GMAP_DOROMETHODS(METH, KWMETH, NAMETH) \ METH (has_key, "D.has_key(KEY) -> BOOL") \ NAMETH(keys, "D.keys() -> LIST") \ @@ -565,6 +667,7 @@ typedef struct gmap_pyobj { NAMETH(itervalues, "D.itervalues() -> ITER") \ NAMETH(iteritems, "D.iteritems() -> ITER") \ KWMETH(get, "D.get(KEY, [default = None]) -> VALUE") +#endif #define GMAP_DOMETHODS(METH, KWMETH, NAMETH) \ GMAP_DOROMETHODS(METH, KWMETH, NAMETH) \