X-Git-Url: https://git.distorted.org.uk/~mdw/pyke/blobdiff_plain/7e18a150da6ebba51ca22fbe65f12c023def4b13..c80de12d8d0827e0553fed2e4d392cb9bf3a378f:/pyke.h diff --git a/pyke.h b/pyke.h index 984b5d5..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,171 @@ 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. + * + * * `BINOBJ' is the C type of a `bin' object. + * * `BIN_TYPE' is the Python `type' object for `bin'. + * * `BIN_CHECK(OBJ)' is true if OBJ is a `bin' object. + * * `BIN_PTR(OBJ)' points to the first octet of OBJ, without checking! + * * `BIN_LEN(OBJ)' yields the length of OBJ in octets, without checking! + * * `BIN_FROMSTR(STR)' makes a `bin' object from a null-terminated string. + * * `BIN_FORMAT(FMT, ARGS...)' and `BIN_VFORMAT(FMT, AP)' make a `bin' + * object from a `printf'-like format string and arguments. + * * `BIN_PREPAREWRITE(OBJ, PTR, LEN)' prepares to make a `bin' object: it + * sets PTR to point to a buffer of LEN bytes; call `BIN_DONEWRITE' when + * finished. The variable OBJ will eventually be the resulting object, + * but until `BIN_DONEWRITE' is called, it may in fact be some different + * object. + * * `BIN_DONEWRITE(OBJ, LEN)' completes making a `bin' object: it adjusts + * its length to be LEN, which must not be larger than the LEN given to + * `BIN_PREPAREWRITE', and sets OBJ to point to the finished object. + * * `BIN_SETLEN(OBJ, LEN)' adjusts the length of OBJ downwards to LEN, + * without checking! + # * `Y' is a format character for `PyArg_ParseTuple...' for retrieving a + * null-terminated octet string from a `bin' object. + # * `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) +# define BIN_PTR(obj) PyString_AS_STRING(obj) +# define BIN_LEN(obj) PyString_GET_SIZE(obj) +# define BIN_FROMSTR(str) PyString_FromString(str) +# define BIN_FROMSTRLEN(str, len) PyString_FromStringAndSize(str, len) +# define BIN_FORMAT PyString_FromFormat +# define BIN_VFORMAT PyString_FromFormatV +# define BIN_PREPAREWRITE(obj, ptr, sz) do { \ + (obj) = PyString_FromStringAndSize(0, (sz)); \ + (ptr) = PyString_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 "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 + * with Python 3 text, the data is UTF-8 encoded. We call them `text' here, + * and define the following. + * + * * `TEXTOBJ' is the C type of a `text' object. + * * `TEXT_TYPE' is the Python `type' object for `text'. + * * `TEXT_CHECK(OBJ)' is true if OBJ is a `text' object. + * * `TEXT_STR(OBJ)' points to the first byte of a null-terminated string + * OBJ, or is null. + * * `TEXT_PTR(OBJ)' points to the first byte of OBJ, without checking! + * * `TEXT_LEN(OBJ)' yields the length of OBJ in octets, without checking! + * * `TEXT_FROMSTR(STR)' makes a `text' object from a null-terminated + * string. + * * `TEXT_FORMAT(FMT, ARGS...)' and `TEST_VFORMAT(FMT, AP)' make a `text' + * object from a `printf'-like format string and arguments. + * * `TEXT_PREPAREWRITE(OBJ, PTR, LEN)' prepares to make a `text' object: + * it sets PTR to point to a buffer of LEN bytes; call `TEXT_DONEWRITE' + * when finished. The variable OBJ will eventually be the resulting + * object, but until `TEXT_DONEWRITE' is called, it may in fact be some + * different object. + * * `TEXT_DONEWRITE(OBJ, LEN)' completes making a `text' object: it + * adjusts its length to be LEN, which must not be larger than the LEN + * given to `TEXT_PREPAREWRITE', and sets OBJ to point to the finished + * object. + * + * (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) +# define TEXT_PTR(obj) PyString_AS_STRING(obj) +# define TEXT_STR(obj) PyString_AsString(obj) +# define TEXT_PTRLEN(obj, ptr, len) do { \ + (ptr) = PyString_AS_STRING(obj); \ + (len) = PyString_GET_SIZE(obj); \ + } while (0) +# define TEXT_FORMAT PyString_FromFormat +# define TEXT_VFORMAT PyString_FromFormatV +# define TEXT_PREPAREWRITE(obj, ptr, sz) do { \ + (obj) = PyString_FromStringAndSize(0, (sz)); \ + (ptr) = PyString_AS_STRING(obj); \ + } while (0) +# 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 ---------------------*/ /* Returning values. */ @@ -188,6 +374,11 @@ extern PyObject *getulong(unsigned long); /* any kind of unsigned integer */ extern PyObject *abstract_pynew(PyTypeObject *, PyObject *, PyObject *); /* A `tp_new' function which refuses to make the object. */ +extern PyObject *enrich_compare(int /*op*/, int /*cmp*/); + /* Use a traditional compare-against-zero comparison result CMP to answer a + * modern Python `tp_richcompare' operation OP. + */ + #ifndef CONVERT_CAREFULLY # define CONVERT_CAREFULLY(newty, expty, obj) \ (!sizeof(*(expty *)0 = (obj)) + (/*unconst*/ newty)(obj)) @@ -326,7 +517,7 @@ extern PyTypeObject *inittype(const PyTypeObject */*skel*/, /*----- Populating modules ------------------------------------------------*/ extern PyObject *modname; - /* The overall module name. Set this with `PyString_FromString'. */ + /* The overall module name. Set this with `TEXT_FROMSTR'. */ extern PyObject *home_module; /* The overall module object. */ @@ -460,15 +651,23 @@ typedef struct gmap_pyobj { #define GMAP_NAMETHDECL(func, doc) \ extern PyObject *gmapmeth_##func(PyObject *); -#define GMAP_DOROMETHODS(METH, KWMETH, NAMETH) \ - METH (has_key, "D.has_key(KEY) -> BOOL") \ - NAMETH(keys, "D.keys() -> LIST") \ - NAMETH(values, "D.values() -> LIST") \ - NAMETH(items, "D.items() -> LIST") \ - NAMETH(iterkeys, "D.iterkeys() -> ITER") \ - NAMETH(itervalues, "D.itervalues() -> ITER") \ - NAMETH(iteritems, "D.iteritems() -> ITER") \ - KWMETH(get, "D.get(KEY, [default = None]) -> VALUE") +#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") \ + NAMETH(values, "D.values() -> LIST") \ + NAMETH(items, "D.items() -> LIST") \ + NAMETH(iterkeys, "D.iterkeys() -> ITER") \ + 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) \