From e3c30b6c98fa74f7d052e8292fc77ba7f829c8a3 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 24 May 2017 12:28:31 -0700 Subject: [PATCH 1/8] Switch to stock sip instead of our own build as wx.siplib. Add dependency on stock sip so pip will install it. Update sip to version 4.19.2. --- bin/build-sip-msw | 4 +--- bin/build-sip-posix | 5 +---- setup.py | 1 + sip/siplib/sip.h | 2 +- sip/siplib/siplib.c | 4 ++-- src/__init__.py | 10 ++++++++++ wscript | 35 ++++++++++++++++++----------------- 7 files changed, 34 insertions(+), 27 deletions(-) diff --git a/bin/build-sip-msw b/bin/build-sip-msw index 7700bfe3a..40b309f3c 100644 --- a/bin/build-sip-msw +++ b/bin/build-sip-msw @@ -22,9 +22,7 @@ MYBINDIR=$(dirname $(readlink -f $0)) cd /c/projects/sip/sip SIPVER=$($PYTHON -c "import sys,configure; sys.stdout.write(configure.sip_version_str);") -$PYTHON configure.py \ - --sip-module wx.siplib \ - $* +$PYTHON configure.py $* cd sipgen nmake clean all diff --git a/bin/build-sip-posix b/bin/build-sip-posix index f6b030489..d186096d5 100755 --- a/bin/build-sip-posix +++ b/bin/build-sip-posix @@ -36,15 +36,12 @@ if [ "$PLATFORM" = "darwin" ]; then --sdk=$SDK \ --arch=i386 \ --universal \ - --sip-module wx.siplib \ $* make -C sipgen clean all else - $PYTHON configure.py \ - --sip-module wx.siplib \ - $* + $PYTHON configure.py $* make -C sipgen clean all fi diff --git a/setup.py b/setup.py index cda4511fc..2dee4a1f1 100644 --- a/setup.py +++ b/setup.py @@ -85,6 +85,7 @@ """ DEPENDENCIES = [ 'six', + 'sip==4.19.2' ] isWindows = sys.platform.startswith('win') diff --git a/sip/siplib/sip.h b/sip/siplib/sip.h index 4b2e363df..9ffdba573 100644 --- a/sip/siplib/sip.h +++ b/sip/siplib/sip.h @@ -250,7 +250,7 @@ extern "C" { /* The name of the sip module. */ -#define SIP_MODULE_NAME "wx.siplib" +#define SIP_MODULE_NAME "sip" /* diff --git a/sip/siplib/siplib.c b/sip/siplib/siplib.c index cb7e950bc..fc009bd4f 100644 --- a/sip/siplib/siplib.c +++ b/sip/siplib/siplib.c @@ -952,13 +952,13 @@ static int importExceptions(sipExportedModuleDef *client, * The Python module initialisation function. */ #if PY_MAJOR_VERSION >= 3 -#define SIP_MODULE_ENTRY PyInit_siplib +#define SIP_MODULE_ENTRY PyInit_sip #define SIP_MODULE_TYPE PyObject * #define SIP_MODULE_DISCARD(m) Py_DECREF(m) #define SIP_FATAL(s) return NULL #define SIP_MODULE_RETURN(m) return (m) #else -#define SIP_MODULE_ENTRY initsiplib +#define SIP_MODULE_ENTRY initsip #define SIP_MODULE_TYPE void #define SIP_MODULE_DISCARD(m) #define SIP_FATAL(s) Py_FatalError(s) diff --git a/src/__init__.py b/src/__init__.py index a07c0d008..ee6d2d081 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -11,6 +11,15 @@ import wx.__version__ __version__ = wx.__version__.VERSION_STRING +# Ensure the sip module has been installed. It should be there because of the +# dependency set in setup.py, but just in case it isn't this will result in a +# nice ImportError instead of a crash when wx.core tries to use it. +import sip + +# Ensure it's a real module and not a namespace module due to just happening +# to have a 'sip' folder on the sys.path (such as in our source tree...) +assert hasattr(sip, '__file__'), "It appears that the sip module is not installed." + # Import all items from the core wxPython module so they appear in the wx # package namespace. @@ -20,3 +29,4 @@ # Clean up the package namespace del core del wx +del sip \ No newline at end of file diff --git a/wscript b/wscript index c888e05a3..41bcb2e41 100644 --- a/wscript +++ b/wscript @@ -500,23 +500,24 @@ def build(bld): # Create the build tasks for each of our extension modules. - addRelwithdebugFlags(bld, 'siplib') - siplib = bld( - features = 'c cxx cshlib cxxshlib pyext', - target = makeTargetName(bld, 'siplib'), - source = ['sip/siplib/apiversions.c', - 'sip/siplib/array.c', - 'sip/siplib/bool.cpp', - 'sip/siplib/descriptors.c', - 'sip/siplib/objmap.c', - 'sip/siplib/qtlib.c', - 'sip/siplib/siplib.c', - 'sip/siplib/threads.c', - 'sip/siplib/voidptr.c', - ], - uselib = 'siplib WX WXPY', - ) - makeExtCopyRule(bld, 'siplib') + + # addRelwithdebugFlags(bld, 'siplib') + # siplib = bld( + # features = 'c cxx cshlib cxxshlib pyext', + # target = makeTargetName(bld, 'siplib'), + # source = ['sip/siplib/apiversions.c', + # 'sip/siplib/array.c', + # 'sip/siplib/bool.cpp', + # 'sip/siplib/descriptors.c', + # 'sip/siplib/objmap.c', + # 'sip/siplib/qtlib.c', + # 'sip/siplib/siplib.c', + # 'sip/siplib/threads.c', + # 'sip/siplib/voidptr.c', + # ], + # uselib = 'siplib WX WXPY', + # ) + # makeExtCopyRule(bld, 'siplib') # Add build rules for each of our ETG generated extension modules makeETGRule(bld, 'etg/_core.py', '_core', 'WX') From 65bc7af2d4ee39441e03822245e82b41b72b6f41 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 24 May 2017 17:35:37 -0700 Subject: [PATCH 2/8] Move wxpy_api to a separate module --- {src => wxpy_api}/wxpy_api.h | 0 {src => wxpy_api}/wxpy_api.sip | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {src => wxpy_api}/wxpy_api.h (100%) rename {src => wxpy_api}/wxpy_api.sip (100%) diff --git a/src/wxpy_api.h b/wxpy_api/wxpy_api.h similarity index 100% rename from src/wxpy_api.h rename to wxpy_api/wxpy_api.h diff --git a/src/wxpy_api.sip b/wxpy_api/wxpy_api.sip similarity index 100% rename from src/wxpy_api.sip rename to wxpy_api/wxpy_api.sip From ab75a8c9c1424bde7d1556742edeb66398701775 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 24 May 2017 20:39:50 -0700 Subject: [PATCH 3/8] Add --py_limited_api CLI build option --- build.py | 7 +++++++ buildtools/config.py | 14 ++++++++++++++ wscript | 12 +++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/build.py b/build.py index c0aaaf62d..24ceb2239 100755 --- a/build.py +++ b/build.py @@ -391,7 +391,11 @@ def makeOptionParser(): ("debug", (False, "Build wxPython with debug symbols")), ("relwithdebug", (False, "Turn on the generation of debug info for release builds on MSW.")), ("release", (False, "Turn off some development options for a release build.")), + + ("py_limited_api", (False, "Set flags to use the Python Limited API.")), + ("keep_hash_lines",(False, "Don't remove the '#line N' lines from the SIP generated code")), + ("gtk3", (False, "On Linux build for gtk3 (default gtk2)")), ("osx_cocoa", (True, "Build the OSX Cocoa port on Mac (default)")), ("osx_carbon", (False, "Build the OSX Carbon port on Mac (unsupported)")), @@ -1398,6 +1402,9 @@ def cmd_build_py(options, args): if options.gtk3: build_options.append('--gtk3') + if options.py_limited_api: + build_options.append('--py_limited_api') + build_options.append('--python="%s"' % PYTHON) build_options.append('--out=%s' % wafBuildDir) # this needs to be the last option diff --git a/buildtools/config.py b/buildtools/config.py index ef854786e..9da46c814 100644 --- a/buildtools/config.py +++ b/buildtools/config.py @@ -558,6 +558,20 @@ def adjustLFLAGS(self, lflags, libdirs, libs): return newLFLAGS + def set_limited_api(self, use_limited_api): + """ + Add a define for Py_LIMITED_API if the option is enabled + """ + name = 'Py_LIMITED_API' + version = '0x03040000' + if use_limited_api: + # not sure yet where it needs to go, so put it everywhere ;-) + self.defines.append( (name, version) ) + self.wafDefines.append('{}={}'.format(name, version)) + # and return it too + return '-D{}={}'.format(name, version) + + # We'll use a factory function so we can use the Configuration class as a singleton _config = None diff --git a/wscript b/wscript index 41bcb2e41..393936a4c 100644 --- a/wscript +++ b/wscript @@ -37,6 +37,8 @@ def options(opt): help='Turn on debug compile options.') opt.add_option('--python', dest='python', default='', action='store', help='Full path to the Python executable to use.') + opt.add_option('--py_limited_api', dest='py_limited_api', default='', action='store', + help='Turn on Py_LIMITED_API for multi-python version builds.') opt.add_option('--wx_config', dest='wx_config', default='wx-config', action='store', help='Full path to the wx-config script to be used for this build.') opt.add_option('--no_magic', dest='no_magic', action='store_true', default=False, @@ -100,6 +102,7 @@ def configure(conf): # Windows/MSVC specific stuff cfg.finishSetup(debug=conf.env.debug) + cfg.set_limited_api(conf.options.py_limited_api) conf.env.INCLUDES_WX = cfg.includes conf.env.DEFINES_WX = cfg.wafDefines @@ -180,6 +183,10 @@ def configure(conf): conf.env.wx_config = conf.options.wx_config cfg.finishSetup(conf.env.wx_config, conf.env.debug) + pla_flag = cfg.set_limited_api(conf.options.py_limited_api) + conf.env.CFLAGS_WXPY.append(pla_flag) + conf.env.CXXFLAGS_WXPY.append(pla_flag) + # Check wx-config exists and fetch some values from it rpath = ' --no-rpath' if not conf.options.no_magic else '' conf.check_cfg(path=conf.options.wx_config, package='', @@ -187,7 +194,7 @@ def configure(conf): uselib_store='WX', mandatory=True) # Run it again with different libs options to get different - # sets of flags stored to use with varous extension modules below. + # sets of flags stored to use with various extension modules below. conf.check_cfg(path=conf.options.wx_config, package='', args='--cxxflags --libs adv,core,net' + rpath, uselib_store='WXADV', mandatory=True) @@ -472,6 +479,9 @@ def build(bld): cfg.finishSetup(bld.env.wx_config) + # Is this needed here or is the flag remembered from the configure step? + # cfg.set_limited_api(conf.options.py_limited_api) + # Copy the license files from wxWidgets updateLicenseFiles(cfg) From e150f9a2246b8c7e44935ba771f780d616edec69 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 24 May 2017 20:42:41 -0700 Subject: [PATCH 4/8] Continue the moving of wxpy_api to a separate module, adding stuff to it, and using the stuff from the rest of Phoenix. Lots more still to go... --- buildtools/config.py | 2 +- etg/_core.py | 4 +-- etg/accel.py | 8 +++--- etg/bitmap.py | 2 +- etg/colour.py | 8 +++--- etg/config.py | 6 ++--- etg/dataobj.py | 3 ++- etgtools/tweaker_tools.py | 32 ++++++++++++------------ patch.diff | 43 +++++++++++++++++++++++++++++++++ requirements.txt | 1 + src/arrays.sip | 4 +-- src/core_ex.cpp | 7 +++--- src/dc_ex.cpp | 51 +++++++++++++++++++++++++++++++-------- wscript | 2 +- wxpy_api/wxpy_api.h | 20 +-------------- 15 files changed, 126 insertions(+), 67 deletions(-) create mode 100644 patch.diff diff --git a/buildtools/config.py b/buildtools/config.py index 9da46c814..6ffcd0797 100644 --- a/buildtools/config.py +++ b/buildtools/config.py @@ -111,6 +111,7 @@ def __init__(self, noWxConfig=False): self.includes = [phoenixDir() + '/sip/siplib', # to get our version of sip.h phoenixDir() + '/src', # for any hand-written headers + phoenixDir() + '/wxpy_api', # for our internal API module ] self.DOXY_XML_DIR = os.path.join(self.WXDIR, 'docs/doxygen/out/xml') @@ -541,7 +542,6 @@ def adjustCFLAGS(self, cflags, defines, includes): return newCFLAGS - def adjustLFLAGS(self, lflags, libdirs, libs): """ Extract the -L and -l flags from lflags and put them in libdirs and diff --git a/etg/_core.py b/etg/_core.py index df888d61e..c84f8e74d 100644 --- a/etg/_core.py +++ b/etg/_core.py @@ -39,7 +39,6 @@ 'defs', 'debug', 'object', - 'wxpy_api', 'arrayholder', 'string', 'filename', @@ -68,7 +67,8 @@ 'position', 'colour', - 'stream', 'filesys', + 'stream', + 'filesys', # GDI and graphics 'image', diff --git a/etg/accel.py b/etg/accel.py index c28f62727..e753b9dd6 100644 --- a/etg/accel.py +++ b/etg/accel.py @@ -69,7 +69,7 @@ def run(): } int idx; for (idx=0; idx( @@ -77,9 +77,9 @@ def run(): tmpEntries[idx] = *entryPtr; } else if (PySequence_Check(obj) && PySequence_Size(obj) == 3) { - PyObject* o1 = PySequence_ITEM(obj, 0); - PyObject* o2 = PySequence_ITEM(obj, 1); - PyObject* o3 = PySequence_ITEM(obj, 2); + PyObject* o1 = PySequence_GetItem(obj, 0); + PyObject* o2 = PySequence_GetItem(obj, 1); + PyObject* o3 = PySequence_GetItem(obj, 2); tmpEntries[idx].Set(wxPyInt_AsLong(o1), wxPyInt_AsLong(o2), wxPyInt_AsLong(o3)); Py_DECREF(o1); Py_DECREF(o2); diff --git a/etg/bitmap.py b/etg/bitmap.py index 466f263d5..deb146b92 100644 --- a/etg/bitmap.py +++ b/etg/bitmap.py @@ -69,7 +69,7 @@ def run(): cArray = new char*[count]; for(int x=0; x( sipConvertToType(pyItem, sipType_{ItemClass}, NULL, 0, &state, sipIsErr)); @@ -1145,7 +1145,7 @@ def ObjArrayHelperTemplate(objType, sipType, errmsg): else {{ len = PySequence_Length(source); for (idx=0; idx( diff --git a/patch.diff b/patch.diff new file mode 100644 index 000000000..ac40b7dbd --- /dev/null +++ b/patch.diff @@ -0,0 +1,43 @@ +diff --git a/src/wxpy_api.h b/src/wxpy_api.h +index 9e9edfb..1f6504b 100644 +--- a/src/wxpy_api.h ++++ b/src/wxpy_api.h +@@ -101,12 +101,23 @@ inline void wxPyEndAllowThreads(PyThreadState* saved) { + // Make a memory view object from a C buffer and size. + inline PyObject* wxPyMakeBuffer(void* ptr, Py_ssize_t len, bool readOnly=false) { + // GIL should already be held +- Py_buffer view; +- int flags = PyBUF_FORMAT|PyBUF_ND; +- if (!readOnly) +- flags |= PyBUF_WRITABLE; +- PyBuffer_FillInfo(&view, NULL, ptr, len, readOnly ? 1:0, flags); +- return PyMemoryView_FromBuffer(&view); ++// Py_buffer view; ++// int flags = PyBUF_FORMAT|PyBUF_ND; ++// if (!readOnly) ++// flags |= PyBUF_WRITABLE; ++// PyBuffer_FillInfo(&view, NULL, ptr, len, readOnly ? 1:0, flags); ++// return PyMemoryView_FromBuffer(&view); ++ ++ // Create a sip.array of bytes, and then convert to a memoryview which is ++ // basically the same thing but is a documented built-in Python type ++ // TODO: Consider just returning the array object instead. ++ int flags = 0; ++ if (readOnly) ++ flags |= SIP_READ_ONLY; ++ PyObject* array = sipConvertToArray(ptr, "B", len, flags); ++ PyObject* mem = PyMemoryView_FromObject(array); ++ Py_DECREF(array); ++ return mem; + } + + +@@ -162,7 +173,7 @@ inline bool wxPyNumberSequenceCheck(PyObject* obj, int reqLength=-1) { + // If it's not one of those, then check for an array. + // It's probably not a good idea to do it this way, but this allows us + // to check if the object is a numpy array without requiring that +- // numpy be imported even for those applications tha are not using it. ++ // numpy be imported even for those applications that are not using it. + if (strcmp(obj->ob_type->tp_name, "numpy.ndarray") != 0) + return false; + } diff --git a/requirements.txt b/requirements.txt index 3fbc0a1ca..74409dcae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,4 @@ pytest pytest-xdist pytest-timeout numpy +sip==4.19.2 diff --git a/src/arrays.sip b/src/arrays.sip index 795b75666..235eb5686 100644 --- a/src/arrays.sip +++ b/src/arrays.sip @@ -64,7 +64,7 @@ } PyErr_Clear(); wxString string; - size_t len = PyUnicode_GET_SIZE(item); + size_t len = PyUnicode_GetSize(item); if (len) { wxPyUnicode_AsWideChar(item, wxStringBuffer(string, len), len); } @@ -224,7 +224,7 @@ wxArrayInt testArrayIntTypemap(const wxArrayInt& arr); Py_DECREF(item); return 0; } - array->Add(PyFloat_AS_DOUBLE(number)); + array->Add(PyFloat_AsDouble(number)); Py_DECREF(item); Py_DECREF(number); } diff --git a/src/core_ex.cpp b/src/core_ex.cpp index 8797056fa..ddbe1c397 100644 --- a/src/core_ex.cpp +++ b/src/core_ex.cpp @@ -112,9 +112,10 @@ void wxPyCoreModuleInject(PyObject* moduleDict) // Create an exception object to use when the app object hasn't been created yet - wxPyNoAppError = PyErr_NewException("wx._core.PyNoAppError", - PyExc_RuntimeError, NULL); - PyDict_SetItemString(moduleDict, "PyNoAppError", wxPyNoAppError); + // TODO + //wxPyNoAppError = PyErr_NewException("wx._core.PyNoAppError", + // PyExc_RuntimeError, NULL); + //PyDict_SetItemString(moduleDict, "PyNoAppError", wxPyNoAppError); #ifdef __WXGTK__ #define wxPort "__WXGTK__" diff --git a/src/dc_ex.cpp b/src/dc_ex.cpp index 3345e2390..653ecd460 100644 --- a/src/dc_ex.cpp +++ b/src/dc_ex.cpp @@ -31,10 +31,15 @@ PyObject* wxPyDrawXXXList(wxDC& dc, wxPyDrawListOp_t doDraw, PyObject* pyCoords, PyObject* pyPens, PyObject* pyBrushes) { wxPyBlock_t blocked = wxPyBeginBlockThreads(); - +#ifndef Py_LIMITED_API bool isFastSeq = PyList_Check(pyCoords) || PyTuple_Check(pyCoords); bool isFastPens = PyList_Check(pyPens) || PyTuple_Check(pyPens); bool isFastBrushes = PyList_Check(pyBrushes) || PyTuple_Check(pyBrushes); +#else + bool isFastSeq = false; + bool isFastPens = false; + bool isFastBrushes = false; +#endif int numObjs = 0; int numPens = 0; int numBrushes = 0; @@ -61,7 +66,9 @@ PyObject* wxPyDrawXXXList(wxDC& dc, wxPyDrawListOp_t doDraw, // Use a new pen? if (i < numPens) { if (isFastPens) { +#ifndef Py_LIMITED_API obj = PySequence_Fast_GET_ITEM(pyPens, i); +#endif } else { obj = PySequence_GetItem(pyPens, i); @@ -79,7 +86,9 @@ PyObject* wxPyDrawXXXList(wxDC& dc, wxPyDrawListOp_t doDraw, // Use a new brush? if (i < numBrushes) { if (isFastBrushes) { +#ifndef Py_LIMITED_API obj = PySequence_Fast_GET_ITEM(pyBrushes, i); +#endif } else { obj = PySequence_GetItem(pyBrushes, i); @@ -97,7 +106,9 @@ PyObject* wxPyDrawXXXList(wxDC& dc, wxPyDrawListOp_t doDraw, // Get the Coordinates if (isFastSeq) { +#ifndef Py_LIMITED_API coords = PySequence_Fast_GET_ITEM(pyCoords, i); +#endif } else { coords = PySequence_GetItem(pyCoords, i); @@ -219,10 +230,17 @@ PyObject* wxPyDrawTextList(wxDC& dc, PyObject* textList, PyObject* pyPoints, PyO { wxPyBlock_t blocked = wxPyBeginBlockThreads(); +#ifndef Py_LIMITED_API bool isFastSeq = PyList_Check(pyPoints) || PyTuple_Check(pyPoints); bool isFastText = PyList_Check(textList) || PyTuple_Check(textList); bool isFastForeground = PyList_Check(foregroundList) || PyTuple_Check(foregroundList); bool isFastBackground = PyList_Check(backgroundList) || PyTuple_Check(backgroundList); +#else + bool isFastSeq = false; + bool isFastText = false; + bool isFastForeground = false; + bool isFastBackground = false; +#endif int numText = 0; int numPoints = 0; int numForeground = 0; @@ -255,7 +273,9 @@ PyObject* wxPyDrawTextList(wxDC& dc, PyObject* textList, PyObject* pyPoints, PyO // Use a new string ? if (i < numText) { if ( isFastText ) { +#ifndef Py_LIMITED_API obj = PySequence_Fast_GET_ITEM(textList, i); +#endif } else { obj = PySequence_GetItem(textList, i); @@ -272,7 +292,9 @@ PyObject* wxPyDrawTextList(wxDC& dc, PyObject* textList, PyObject* pyPoints, PyO if (i < numForeground) { // Use a new foreground ? if ( isFastForeground ) { +#ifndef Py_LIMITED_API obj = PySequence_Fast_GET_ITEM(foregroundList, i); +#endif } else { obj = PySequence_GetItem(foregroundList, i); @@ -290,7 +312,9 @@ PyObject* wxPyDrawTextList(wxDC& dc, PyObject* textList, PyObject* pyPoints, PyO if (i < numBackground) { // Use a new background ? if ( isFastBackground ) { +#ifndef Py_LIMITED_API obj = PySequence_Fast_GET_ITEM(backgroundList, i); +#endif } else { obj = PySequence_GetItem(backgroundList, i); @@ -307,7 +331,9 @@ PyObject* wxPyDrawTextList(wxDC& dc, PyObject* textList, PyObject* pyPoints, PyO // Get the point coordinates if (isFastSeq) { +#ifndef Py_LIMITED_API obj = PySequence_Fast_GET_ITEM(pyPoints, i); +#endif } else { obj = PySequence_GetItem(pyPoints, i); @@ -364,9 +390,9 @@ bool wxPointFromObjects(PyObject* o1, PyObject* o2, wxPoint* point) { // get the x value if (wxPyInt_Check(o1)) - point->x = (int)wxPyInt_AS_LONG(o1); + point->x = (int)wxPyInt_AsLong(o1); else if (PyFloat_Check(o1)) - point->x = (int)PyFloat_AS_DOUBLE(o1); + point->x = (int)PyFloat_AsDouble(o1); else if (PyNumber_Check(o1)) point->x = (int)wxPyInt_AsLong(o1); else @@ -374,9 +400,9 @@ bool wxPointFromObjects(PyObject* o1, PyObject* o2, wxPoint* point) // get the y value if (wxPyInt_Check(o2)) - point->y = (int)wxPyInt_AS_LONG(o2); + point->y = (int)wxPyInt_AsLong(o2); else if (PyFloat_Check(o2)) - point->y = (int)PyFloat_AS_DOUBLE(o2); + point->y = (int)PyFloat_AsDouble(o2); else if (PyNumber_Check(o2)) point->y = (int)wxPyInt_AsLong(o2); else @@ -391,8 +417,11 @@ wxPoint* wxPoint_LIST_helper(PyObject* source, int *count) int idx; wxPoint* temp; PyObject *o, *o1, *o2; +#ifndef Py_LIMITED_API bool isFast = PyList_Check(source) || PyTuple_Check(source); - +#else + bool isFast = false; +#endif if (!PySequence_Check(source)) { goto error0; } @@ -411,7 +440,9 @@ wxPoint* wxPoint_LIST_helper(PyObject* source, int *count) for (idx=0; idx<*count; idx++) { // Get an item: try fast way first. if (isFast) { +#ifndef Py_LIMITED_API o = PySequence_Fast_GET_ITEM(source, idx); +#endif } else { o = PySequence_GetItem(source, idx); @@ -421,10 +452,10 @@ wxPoint* wxPoint_LIST_helper(PyObject* source, int *count) } // Convert o to wxPoint. - if ((PyTuple_Check(o) && PyTuple_GET_SIZE(o) == 2) || - (PyList_Check(o) && PyList_GET_SIZE(o) == 2)) { - o1 = PySequence_Fast_GET_ITEM(o, 0); - o2 = PySequence_Fast_GET_ITEM(o, 1); + if ((PyTuple_Check(o) && PyTuple_Size(o) == 2) || + (PyList_Check(o) && PyList_Size(o) == 2)) { + o1 = PySequence_GetItem(o, 0); + o2 = PySequence_GetItem(o, 1); if (!wxPointFromObjects(o1, o2, &temp[idx])) { goto error2; } diff --git a/wscript b/wscript index 393936a4c..60a702877 100644 --- a/wscript +++ b/wscript @@ -96,7 +96,7 @@ def configure(conf): conf.env.msvc_relwithdebug = conf.options.msvc_relwithdebug # Ensure that the headers in siplib and Phoenix's src dir can be found - conf.env.INCLUDES_WXPY = ['sip/siplib', 'src'] + conf.env.INCLUDES_WXPY = ['sip/siplib', 'src', 'wxpy_api'] if isWindows: # Windows/MSVC specific stuff diff --git a/wxpy_api/wxpy_api.h b/wxpy_api/wxpy_api.h index babad3844..0621b2507 100644 --- a/wxpy_api/wxpy_api.h +++ b/wxpy_api/wxpy_api.h @@ -3,7 +3,7 @@ // Purpose: Some utility functions and such that can be used in other // snippets of C++ code to help reduce complexity, etc. They // are all either macros, inline functions, or functions that -// are exported from the core extension module. +// are exported from the wxpy_api extension module. // // Author: Robin Dunn // @@ -270,24 +270,6 @@ inline PyObject* wxPyMethod_Self(PyObject* method) { return wxPyGetAPIPtr()->p_wxPyMethod_Self(method); } -inline void wxPyReinitializeModules() - { return wxPyGetAPIPtr()->p_wxPyReinitializeModules(); } - - - -inline int wxPyDateTime_Check(PyObject *obj) - { return wxPyGetAPIPtr()->p_wxPyDateTime_Check(obj); } - -inline int wxPyDate_Check(PyObject *obj) - { return wxPyGetAPIPtr()->p_wxPyDate_Check(obj); } - -inline wxDateTime* wxPyDateTime_ToWxDateTime(PyObject *obj) - { return wxPyGetAPIPtr()->p_wxPyDateTime_ToWxDateTime(obj); } - -inline wxDateTime* wxPyDate_ToWxDateTime(PyObject *obj) - { return wxPyGetAPIPtr()->p_wxPyDate_ToWxDateTime(obj); } - - //-------------------------------------------------------------------------- // Convenience helper for RAII-style thread blocking From 6c7ab21180cd6048636b921f29d6b67174bb3af4 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 25 May 2017 09:23:07 -0700 Subject: [PATCH 5/8] Remove accidentally committed patch file --- patch.diff | 43 ------------------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 patch.diff diff --git a/patch.diff b/patch.diff deleted file mode 100644 index ac40b7dbd..000000000 --- a/patch.diff +++ /dev/null @@ -1,43 +0,0 @@ -diff --git a/src/wxpy_api.h b/src/wxpy_api.h -index 9e9edfb..1f6504b 100644 ---- a/src/wxpy_api.h -+++ b/src/wxpy_api.h -@@ -101,12 +101,23 @@ inline void wxPyEndAllowThreads(PyThreadState* saved) { - // Make a memory view object from a C buffer and size. - inline PyObject* wxPyMakeBuffer(void* ptr, Py_ssize_t len, bool readOnly=false) { - // GIL should already be held -- Py_buffer view; -- int flags = PyBUF_FORMAT|PyBUF_ND; -- if (!readOnly) -- flags |= PyBUF_WRITABLE; -- PyBuffer_FillInfo(&view, NULL, ptr, len, readOnly ? 1:0, flags); -- return PyMemoryView_FromBuffer(&view); -+// Py_buffer view; -+// int flags = PyBUF_FORMAT|PyBUF_ND; -+// if (!readOnly) -+// flags |= PyBUF_WRITABLE; -+// PyBuffer_FillInfo(&view, NULL, ptr, len, readOnly ? 1:0, flags); -+// return PyMemoryView_FromBuffer(&view); -+ -+ // Create a sip.array of bytes, and then convert to a memoryview which is -+ // basically the same thing but is a documented built-in Python type -+ // TODO: Consider just returning the array object instead. -+ int flags = 0; -+ if (readOnly) -+ flags |= SIP_READ_ONLY; -+ PyObject* array = sipConvertToArray(ptr, "B", len, flags); -+ PyObject* mem = PyMemoryView_FromObject(array); -+ Py_DECREF(array); -+ return mem; - } - - -@@ -162,7 +173,7 @@ inline bool wxPyNumberSequenceCheck(PyObject* obj, int reqLength=-1) { - // If it's not one of those, then check for an array. - // It's probably not a good idea to do it this way, but this allows us - // to check if the object is a numpy array without requiring that -- // numpy be imported even for those applications tha are not using it. -+ // numpy be imported even for those applications that are not using it. - if (strcmp(obj->ob_type->tp_name, "numpy.ndarray") != 0) - return false; - } From 2a037ecb79555a3956b6c9af2eaef82005bacd25 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 25 May 2017 10:10:41 -0700 Subject: [PATCH 6/8] Add some notes for tracking questions and issues along the way --- py_limited_api.txt | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 py_limited_api.txt diff --git a/py_limited_api.txt b/py_limited_api.txt new file mode 100644 index 000000000..aea42b300 --- /dev/null +++ b/py_limited_api.txt @@ -0,0 +1,70 @@ + + +------------------------------------------------- +Temporary notes, do not commit in the final merge +------------------------------------------------- + + +New bdist_wheel docs including the --py-limited-api option: +https://bitbucket.org/HexDecimal/wheel-fork/src/b25d28cc111f8d354e1b8d949a54ffb3c262dfce/docs/index.rst?fileviewer=file-view-default + +PEP 384: https://www.python.org/dev/peps/pep-0384/ + + +* Can I get rid of wx.siplib and just add dependency on SIP from pypi? Yes, of + course, but it would make using interim builds more difficult. + +* Can I eliminate all uses of the restricted APIs (at least the buffer related + things so far) and use equivalent APIs from siplib itself? If not then I'll + probably have to separate out the incompatible stuff into my own API module + like siplib (and could probably include siplib there if desired...) + +* How to set the tags in the wheel filename? If bdist_wheel doesn't do it + already then it looks like we can override a get_tag(self) method in the + customization of the bdist_wheel class we already have... + + For example, from py2exe's setup.py: + + class my_bdist_wheel(bdist_wheel.bdist_wheel): + """We change the bdist_wheel command so that it creates a + wheel-file compatible with CPython, 3.4, 3.5, and 3.6 only + by setting the impl_tag to 'cp33.cp34.cp35.cp36' + """ + def get_tag(self): + impl_tag, abi_tag, plat_tag = super().get_tag() + return "cp33.cp34.cp35.cp36", abi_tag, plat_tag + + +* The bdist_wheel docs say that --py-limited-api is ignored on Windows??? + +* The extension modules will need to have a different name, I think. Currently + waf is controlling that. Do current versions of waf support py-limited-api + or will something new need to be hacked into it? + +* Check into what Extension(py_limited_api=True, ...) does in recent versions + of setuptools. + + + + +Issues discovered so far +------------------------ + + * We WILL need a separate Python version-specific API module, siplib can give + us lots of help, but doesn't cover everything + + * all the PyFoo_GET_ITEM type of "faster" macros will need to be changed to + their function call equivalents, meaning that our optimizations for those + things will be undone. + + * At least most of the new buffer APIs are not available in the limited API + + * It appears that PyDateTime APIs are also not available + + * We access elements of PyMethod and such in a few places, in the limited API + those are opaque types. + + * Ditto for sipSimpleWrapper + + * There are a few things currently in wxpy_api like wxPyNoAppError that are + expected to end up in the _core module. \ No newline at end of file From a5a777ea72d93f862f16f02726f86b22eec58e7c Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Tue, 8 Aug 2017 16:44:30 -0700 Subject: [PATCH 7/8] Fix merge error --- wxpy_api/wxpy_api.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/wxpy_api/wxpy_api.h b/wxpy_api/wxpy_api.h index 0621b2507..550b28a27 100644 --- a/wxpy_api/wxpy_api.h +++ b/wxpy_api/wxpy_api.h @@ -270,6 +270,24 @@ inline PyObject* wxPyMethod_Self(PyObject* method) { return wxPyGetAPIPtr()->p_wxPyMethod_Self(method); } +inline void wxPyReinitializeModules() + { return wxPyGetAPIPtr()->p_wxPyReinitializeModules(); } + + +// Datetime +inline int wxPyDateTime_Check(PyObject *obj) + { return wxPyGetAPIPtr()->p_wxPyDateTime_Check(obj); } + +inline int wxPyDate_Check(PyObject *obj) + { return wxPyGetAPIPtr()->p_wxPyDate_Check(obj); } + +inline wxDateTime* wxPyDateTime_ToWxDateTime(PyObject *obj) + { return wxPyGetAPIPtr()->p_wxPyDateTime_ToWxDateTime(obj); } + +inline wxDateTime* wxPyDate_ToWxDateTime(PyObject *obj) + { return wxPyGetAPIPtr()->p_wxPyDate_ToWxDateTime(obj); } + + //-------------------------------------------------------------------------- // Convenience helper for RAII-style thread blocking From 3e2f35d67c863f04b774c8892c4ec9d76910158d Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Tue, 8 Aug 2017 16:46:55 -0700 Subject: [PATCH 8/8] finish switching to non-macro versions of Python APIs, and adding wxPy APIs for things not supported by Py_LIMITED_API --- etg/cmndata.py | 2 +- etg/dataview.py | 4 ++-- etg/event.py | 2 +- etg/palette.py | 6 +++--- etg/stream.py | 2 +- etg/treelist.py | 2 +- src/event_ex.cpp | 2 +- src/stream_input.cpp | 8 ++++---- src/stream_output.cpp | 10 +++++----- src/string.sip | 4 ++-- src/wxpybuffer.sip | 4 ++-- wxpy_api/wxpy_api.h | 12 +++++++++++- wxpy_api/wxpy_api.sip | 13 ++++++++++--- 13 files changed, 44 insertions(+), 27 deletions(-) diff --git a/etg/cmndata.py b/etg/cmndata.py index 4131a1fbe..e41776016 100644 --- a/etg/cmndata.py +++ b/etg/cmndata.py @@ -83,7 +83,7 @@ def run(): return; } - self->SetPrivData(PyBytes_AS_STRING(data), PyBytes_GET_SIZE(data)); + self->SetPrivData(PyBytes_AsString(data), PyBytes_Size(data)); """) c.addAutoProperties() diff --git a/etg/dataview.py b/etg/dataview.py index 0ef7b34c4..2c9f69250 100644 --- a/etg/dataview.py +++ b/etg/dataview.py @@ -417,8 +417,8 @@ def _fixupBoolGetters(method, sig): col_obj = Py_None; Py_INCREF(Py_None); } - PyTuple_SET_ITEM(value, 0, item_obj); - PyTuple_SET_ITEM(value, 1, col_obj); + PyTuple_SetItem(value, 0, item_obj); + PyTuple_SetItem(value, 1, col_obj); // PyTuple steals a reference, so we don't need to decref the items here return value; """) diff --git a/etg/event.py b/etg/event.py index 0dcce309c..7be786609 100644 --- a/etg/event.py +++ b/etg/event.py @@ -513,7 +513,7 @@ def run(): } for (int i=0; iWrite(PyBytes_AS_STRING(data), PyBytes_GET_SIZE(data)); + self->Write(PyBytes_AsString(data), PyBytes_Size(data)); RETURN_NONE(); """) diff --git a/etg/treelist.py b/etg/treelist.py index 7fb30971f..a4b7e2134 100644 --- a/etg/treelist.py +++ b/etg/treelist.py @@ -96,7 +96,7 @@ def run(): for (size_t i=0; i sizeof(long)) // wxFileOffset is a 64-bit value... - PyTuple_SET_ITEM(arglist, 0, PyLong_FromLongLong(off)); + PyTuple_SetItem(arglist, 0, PyLong_FromLongLong(off)); else - PyTuple_SET_ITEM(arglist, 0, wxPyInt_FromLong(off)); + PyTuple_SetItem(arglist, 0, wxPyInt_FromLong(off)); - PyTuple_SET_ITEM(arglist, 1, wxPyInt_FromLong(mode)); + PyTuple_SetItem(arglist, 1, wxPyInt_FromLong(mode)); PyObject* result = PyEval_CallObject(m_seek, arglist); diff --git a/src/stream_output.cpp b/src/stream_output.cpp index c135e2091..8e6850020 100644 --- a/src/stream_output.cpp +++ b/src/stream_output.cpp @@ -5,7 +5,7 @@ static PyObject* wxPyGetMethod(PyObject* py, char* name) if (!PyObject_HasAttrString(py, name)) return NULL; PyObject* o = PyObject_GetAttrString(py, name); - if (!PyMethod_Check(o) && !PyCFunction_Check(o)) { + if (!wxPyMethod_Check(o) && !PyCFunction_Check(o)) { Py_DECREF(o); return NULL; } @@ -90,7 +90,7 @@ class wxPyOutputStream : public wxOutputStream wxPyThreadBlocker blocker; PyObject* arglist = PyTuple_New(1); - PyTuple_SET_ITEM(arglist, 0, PyBytes_FromStringAndSize((char*)buffer, bufsize)); + PyTuple_SetItem(arglist, 0, PyBytes_FromStringAndSize((char*)buffer, bufsize)); PyObject* result = PyEval_CallObject(m_write, arglist); Py_DECREF(arglist); @@ -109,11 +109,11 @@ class wxPyOutputStream : public wxOutputStream if (sizeof(wxFileOffset) > sizeof(long)) // wxFileOffset is a 64-bit value... - PyTuple_SET_ITEM(arglist, 0, PyLong_FromLongLong(off)); + PyTuple_SetItem(arglist, 0, PyLong_FromLongLong(off)); else - PyTuple_SET_ITEM(arglist, 0, wxPyInt_FromLong(off)); + PyTuple_SetItem(arglist, 0, wxPyInt_FromLong(off)); - PyTuple_SET_ITEM(arglist, 1, wxPyInt_FromLong(mode)); + PyTuple_SetItem(arglist, 1, wxPyInt_FromLong(mode)); PyObject* result = PyEval_CallObject(m_seek, arglist); diff --git a/src/string.sip b/src/string.sip index 5bfc393f4..007aa9024 100644 --- a/src/string.sip +++ b/src/string.sip @@ -24,7 +24,7 @@ %ConvertToTypeCode #if wxUSE_UNICODE_WCHAR == 0 - #error wxString converison can only handle WCHAR wxStrings currently + #error wxString conversion can only handle WCHAR wxStrings currently #endif // Code to test a PyObject for compatibility with wxString @@ -45,7 +45,7 @@ } } *sipCppPtr = new wxString(); - size_t len = PyUnicode_GET_SIZE(uni); + size_t len = PyUnicode_GetSize(uni); if (len) { wxPyUnicode_AsWideChar(uni, wxStringBuffer(**sipCppPtr, len), len); } diff --git a/src/wxpybuffer.sip b/src/wxpybuffer.sip index 1e571b572..0e362a356 100644 --- a/src/wxpybuffer.sip +++ b/src/wxpybuffer.sip @@ -23,7 +23,7 @@ %ConvertToTypeCode // Code to test a PyObject for compatibility if (!sipIsErr) { - if (PyObject_CheckBuffer(sipPy) // New buffer interface + if (wxPyObject_CheckBuffer(sipPy) // New buffer interface || PyObject_CheckReadBuffer(sipPy)) // or old buffer interface return TRUE; return FALSE; @@ -58,7 +58,7 @@ %ConvertToTypeCode // Code to test a PyObject for compatibility if (!sipIsErr) { - if (PyObject_CheckBuffer(sipPy)) + if (wxPyObject_CheckBuffer(sipPy)) return TRUE; return FALSE; } diff --git a/wxpy_api/wxpy_api.h b/wxpy_api/wxpy_api.h index 550b28a27..486ea85d1 100644 --- a/wxpy_api/wxpy_api.h +++ b/wxpy_api/wxpy_api.h @@ -166,11 +166,12 @@ struct wxPyAPI { void* (*p_wxPyGetCppPtr)(sipSimpleWrapper* sipPyObj); PyObject* (*p_wxPyMethod_Self)(PyObject* method); void (*p_wxPyReinitializeModules)(); - int (*p_wxPyDateTime_Check)(PyObject *obj); int (*p_wxPyDate_Check)(PyObject *obj); wxDateTime* (*p_wxPyDateTime_ToWxDateTime)(PyObject *obj); wxDateTime* (*p_wxPyDate_ToWxDateTime)(PyObject *obj); + bool (*p_wxPyMethod_Check)(PyObject *obj); + int (*p_wxPyObject_CheckBuffer)(PyObject* obj); // Always add new items here at the end. }; @@ -288,6 +289,15 @@ inline wxDateTime* wxPyDate_ToWxDateTime(PyObject *obj) { return wxPyGetAPIPtr()->p_wxPyDate_ToWxDateTime(obj); } + +inline bool wxPyMethod_Check(PyObject *obj) + { return wxPyGetAPIPtr()->p_wxPyMethod_Check(obj); } + +inline int wxPyObject_CheckBuffer(PyObject *obj) + { return wxPyGetAPIPtr()->p_wxPyObject_CheckBuffer(obj); } + + + //-------------------------------------------------------------------------- // Convenience helper for RAII-style thread blocking diff --git a/wxpy_api/wxpy_api.sip b/wxpy_api/wxpy_api.sip index a3a60e639..b70b4abbb 100644 --- a/wxpy_api/wxpy_api.sip +++ b/wxpy_api/wxpy_api.sip @@ -527,11 +527,13 @@ void* i_wxPyGetCppPtr(sipSimpleWrapper* sipPyObj) { // Call the PyMethod_Self API, which is not available when the Python // limited API is activated. -inline PyObject* i_wxPyMethod_Self(PyObject* method) { +PyObject* i_wxPyMethod_Self(PyObject* method) { return PyMethod_Self(method); } - +bool i_wxPyMethod_Check(PyObject* obj) { + return PyMethod_Check(obj); +} //-------------------------------------------------------------------------- // Cleanup and reinitialize the wxModules. This is needed because sometimes an @@ -578,6 +580,10 @@ wxDateTime* i_wxPyDate_ToWxDateTime(PyObject *obj) { +int i_wxPyObject_CheckBuffer(PyObject* obj) { + return PyObject_CheckBuffer(obj); +} + //-------------------------------------------------------------------------- // An instance of the API structure static wxPyAPI API = { @@ -601,7 +607,8 @@ static wxPyAPI API = { i_wxPyDateTime_Check, i_wxPyDate_Check, i_wxPyDateTime_ToWxDateTime, - i_wxPyDate_ToWxDateTime + i_wxPyDate_ToWxDateTime, + i_wxPyMethod_Check }; %End