From c8e25b79e1374f6e5e5046097443d0f395a7c8a8 Mon Sep 17 00:00:00 2001
From: Robin Dunn <robin@alldunn.com>
Date: Tue, 5 Jan 2021 10:56:42 -0800
Subject: [PATCH] Run SIP via its Python interface rather than as a separate
 subprocess

---
 build.py                  |  50 ++--
 buildtools/config.py      |   2 -
 buildtools/wxpysip.py     | 110 +++++++++
 wscript                   |   7 -
 wx/include/wxPython/sip.h | 463 ++++++--------------------------------
 5 files changed, 209 insertions(+), 423 deletions(-)
 create mode 100644 buildtools/wxpysip.py

diff --git a/build.py b/build.py
index 34038cc32..9ffd520b6 100755
--- a/build.py
+++ b/build.py
@@ -45,6 +45,7 @@
                                getVcsRev, runcmd, textfile_open, getSipFiles, \
                                getVisCVersion, getToolsPlatformName, updateLicenseFiles, \
                                TemporaryDirectory
+from buildtools.wxpysip import sip_runner
 
 import buildtools.version as version
 
@@ -87,14 +88,6 @@
 
 # Some tools will be downloaded for the builds. These are the versions and
 # MD5s of the tool binaries currently in use.
-sipCurrentVersion = '4.19.24'
-sipMD5 = {
-    'darwin'   : '2a22cb7a35eb14384b0829593a366c29',
-    'win32'    : '49e0aa36397d7629fea95418452961fb',
-    'linux32'  : 'ea773f6fd92d5f23530730428a86df2f',
-    'linux64'  : 'b44a45191f5f84db10e2ba1c4cecd8ff',
-}
-
 wafCurrentVersion = '2.0.19'
 wafMD5 = 'ac362b60111a59ab2df63513018d5ad8'
 
@@ -638,15 +631,6 @@ def _error_msg(txt):
 
 # The download and MD5 check only needs to happen once per run, cache the sip
 # cmd value here the first time through.
-_sipCmd = None
-def getSipCmd():
-    global _sipCmd
-    if _sipCmd is None:
-        _sipCmd = getTool('sip', sipCurrentVersion, sipMD5, 'SIP', True, True)
-    return _sipCmd
-
-
-# Same thing for WAF
 _wafCmd = None
 def getWafCmd():
     global _wafCmd
@@ -1285,15 +1269,31 @@ def cmd_sip(options, args):
         if not newer_group(sipFiles, sbf) and os.path.exists(pycode):
             continue
 
-        # leave this turned off for now...
-        # typehint = '-y {}'.format(posixjoin(cfg.PKGDIR, base[1:]) + '.pyi')
-        typehint = ''
+        # Leave it turned off for now. TODO: Experiment with this...
+        # pyi_extract = posixjoin(cfg.PKGDIR, base[1:]) + '.pyi'
+        pyi_extract = None
+
+        # SIP extracts are used to pull python snippets and put them into the
+        # module's .py file
+        pycode = 'pycode'+base+':'+pycode
+
+        sip_runner(src_name,
+            abi_version = '12.8',       # siplib abi version
+            warnings = True,            # enable warning messages
+            docstrings = True,          # enable the automatic generation of docstrings
+            release_gil = True,         # always release and reacquire the GIL
+            sip_module = 'wx.siplib',   # the fully qualified name of the sip module
+            sbf_file=sbf,               # File to write the generated file lists to
+            exceptions = False,         # enable support for exceptions
+            tracing = False,            # generate code with tracing enabled
+            sources_dir = tmpdir,       # the name of the code directory
+            extracts = [pycode],        # add <ID:FILE> to the list of extracts to generate
+            pyi_extract=pyi_extract,    # the name of the .pyi stub file
+            include_dirs = [
+                os.path.join(phoenixDir(), 'src'),
+                os.path.join(phoenixDir(), 'sip', 'gen'),
+            ])
 
-        pycode = '-X pycode'+base+':'+pycode
-        sip = getSipCmd()
-        cmd = '%s %s -c %s -b %s %s %s %s'  % \
-            (sip, cfg.SIPOPTS, tmpdir, sbf, pycode, typehint, src_name)
-        runcmd(cmd)
 
         classesNeedingClassInfo = { 'sip_corewxTreeCtrl.cpp' : 'wxTreeCtrl', }
 
diff --git a/buildtools/config.py b/buildtools/config.py
index 99a1a2258..a7c2a0d59 100644
--- a/buildtools/config.py
+++ b/buildtools/config.py
@@ -169,8 +169,6 @@ def finishSetup(self, wx_config=None, debug=None):
                              ('WXUSINGDLL', '1'),
                              ('ISOLATION_AWARE_ENABLED', None),
                              #('NDEBUG',),  # using a 1-tuple makes it do an undef
-                             ('SIP_MODULE_NAME', 'wx.siplib'),
-                             ('SIP_MODULE_BASENAME', 'siplib'),
                              ]
             if int(getVisCVersion()) > 100:
                 self.defines += [ ('wxUSE_RC_MANIFEST', '1'),
diff --git a/buildtools/wxpysip.py b/buildtools/wxpysip.py
new file mode 100644
index 000000000..c58102b84
--- /dev/null
+++ b/buildtools/wxpysip.py
@@ -0,0 +1,110 @@
+#----------------------------------------------------------------------
+# Name:        buildtools.wxpysip
+# Purpose:     Code to help migrate to SIP 5 with as little disruption
+#              as possible.
+#
+# Author:      Robin Dunn
+#
+# Created:     4-Jan-2021
+# Copyright:   (c) 2021 by Total Control Software
+# License:     wxWindows License
+#----------------------------------------------------------------------
+
+# NOTE: This code is mostly copied, adapted, and extended from the
+#       sipbuild.legacy.sip5 module. The main intent is to make it easy to run
+#       sip the same way as the legacy sip5 entry point, but without needing to
+#       run a subprocess, and to also add a little missing sip 4 functionality
+#       that we were depending on with the old SIP.
+import os
+
+from sipbuild.code_generator import (set_globals, parse, generateCode,
+        generateExtracts, generateAPI, generateXML, generateTypeHints)
+from sipbuild.exceptions import handle_exception, UserException
+from sipbuild.module import resolve_abi_version
+from sipbuild.version import SIP_VERSION, SIP_VERSION_STR
+
+
+def sip_runner(
+    specification,              # the name of the specification file [default stdin]
+    sources_dir=None,           # the name of the code output directory [default not generated]
+    include_dirs=[],            # add <DIR> to the list of directories to search when importing or including .sip files
+    warnings=False,             # enable warning messages [default disabled]
+    docstrings=False,           # enable the automatic generation of docstrings [default disabled]
+    release_gil=False,          # always release and reacquire the GIL [default only when specified]
+    sip_module=None,            # the fully qualified name of the sip module
+    api_extract=None,           # the name of the QScintilla API file [default not generated
+    exceptions=False,           # enable support for C++ exceptions [default disabled]
+    tracing=False,              # generate code with tracing enabled [default disabled]
+    extracts=[],                # add <ID:FILE> to the list of extracts to generate
+    pyi_extract=None,           # the name of the .pyi stub file [default not generated]
+    sbf_file=None,              # File to write the generated file lists to [default not generated]
+    abi_version=None,           # the sip ABI version
+    backstops=[],               # add <TAG> to the list of timeline backstops
+    py_debug=False,             # generate code for a debug build of Python
+    warnings_are_errors=False,  # warnings are handled as errors
+    parts=0,                    # split the generated code into <FILES> files [default 1 per class]
+    xml_extract=None,           # file to write sip xml to
+    protected_is_public=False,  # enable the protected/public hack [default disabled]
+    source_suffix=None,         # the suffix to use for C or C++ source files [default \".c\" or \".cpp\"]
+    tags=[],                    # add <TAG> to the list of versions/platforms to generate code for
+    disabled_features=[],       # add <FEATURE> to the list of disabled features
+    ):
+
+    print("Running SIP code generator on: {}".format(specification))
+
+    generated_files = []
+    try:
+        # The code generator requires the name of the sip module.
+        if sources_dir is not None and sip_module is None:
+            raise UserException("the name of the sip module must be given")
+
+        # Check the ABI version.
+        abi_major, abi_minor = resolve_abi_version(abi_version).split('.')
+
+        # Set the globals.
+        set_globals(SIP_VERSION, SIP_VERSION_STR, int(abi_major), int(abi_minor),
+                UserException, include_dirs)
+
+        # Parse the input file.
+        pt, _, _, _, tags, disabled_features = parse(specification,
+                (xml_extract is None), tags, backstops, disabled_features,
+                protected_is_public)
+
+        # Generate the bindings.
+        if sources_dir is not None:
+            generated_files = generateCode(pt, sources_dir, source_suffix,
+                    exceptions, tracing, release_gil, parts, tags,
+                    disabled_features, docstrings, py_debug, sip_module)
+
+        if sbf_file is not None:
+            generateBuildFile(sbf_file, generated_files)
+
+        # Generate any extracts.
+        generateExtracts(pt, extracts)
+
+        # Generate the API file.
+        if api_extract is not None:
+            generateAPI(pt, api_extract)
+
+        # Generate the type hints file.
+        if pyi_extract is not None:
+            generateTypeHints(pt, pyi_extract)
+
+        # Generate the XML file.
+        if xml_extract is not None:
+            generateXML(pt, xml_extract)
+
+    except Exception as e:
+        handle_exception(e)
+
+    return generated_files
+
+
+def generateBuildFile(sbf_file, generated_files):
+    header, sources = generated_files
+    header = os.path.basename(header)
+    sources = [os.path.basename(n) for n in sources]
+    with open(sbf_file, 'w') as f:
+        f.write("sources = {}\n".format(' '.join(sources)))
+        f.write("headers = {}\n".format(header))
+
diff --git a/wscript b/wscript
index 1e143be67..7fc4f7ca8 100644
--- a/wscript
+++ b/wscript
@@ -323,13 +323,6 @@ def configure(conf):
         conf.env.CFLAGS_WXPY.append('-UNDEBUG')
         conf.env.CXXFLAGS_WXPY.append('-UNDEBUG')
 
-        # set the name of our siplib module
-        conf.env.CFLAGS_WXPY.append('-DSIP_MODULE_NAME=wx.siplib')
-        conf.env.CXXFLAGS_WXPY.append('-DSIP_MODULE_NAME=wx.siplib')
-
-        conf.env.CFLAGS_WXPY.append('-DSIP_MODULE_BASENAME=siplib')
-        conf.env.CXXFLAGS_WXPY.append('-DSIP_MODULE_BASENAME=siplib')
-
         # Add basic debug info for all builds
         conf.env.CFLAGS_WXPY.append('-g')
         conf.env.CXXFLAGS_WXPY.append('-g')
diff --git a/wx/include/wxPython/sip.h b/wx/include/wxPython/sip.h
index ad9036373..aa911aee7 100644
--- a/wx/include/wxPython/sip.h
+++ b/wx/include/wxPython/sip.h
@@ -21,22 +21,11 @@
 #define _SIP_H
 
 
-/*
- * This gets round a problem with Qt's moc and Python v2.3.  Strictly speaking
- * it's a Qt problem but later versions of Python include a fix for it so we
- * might as well too.
- */
-#undef slots
-
-
 #include <Python.h>
 
-/*
- * There is a mis-feature somewhere with the Borland compiler.  This works
- * around it.
- */
-#if defined(__BORLANDC__)
-#include <rpc.h>
+/* Sanity check on the Python version. */
+#if PY_VERSION_HEX < 0x03050000
+#error "This version of SIP requires Python v3.5 or later"
 #endif
 
 
@@ -45,238 +34,35 @@ extern "C" {
 #endif
 
 
-/* Sanity check on the Python version. */
-#if PY_VERSION_HEX < 0x02030000
-#error "This version of SIP requires Python v2.3 or later"
-#endif
+/* The patch version of this implementation of the ABI. */
+#define SIP_MODULE_PATCH_VERSION    1
 
 
 /*
- * Define the SIP version number.
+ * The changes to this version of the ABI.
+ *
+ * Preserve any current exception in the wrapper tp_dealloc functions.
  */
-#define SIP_VERSION         0x041318
-#define SIP_VERSION_STR     "4.19.24"
 
 
-/*
- * Define the current API version number.  SIP must handle modules with the
- * same major number and with the same or earlier minor number.  Whenever
- * members are added to non-embedded data structures they must be appended and
- * the minor number incremented.  Whenever data structure members are removed
- * or their offset changed then the major number must be incremented and the
- * minor number set * to 0.
- *
- * History:
- *
- * 12.7 Added sip_api_visit_wrappers() to the public API.
- *      Added sip_api_register_exit_notifier() to the public API.
- *      sip_api_is_owned_by_python() is now part of the public API.
- *
- * 12.6 Added sip_api_long_as_size_t() to the public API.
- *      Added the '=' format character to sip_api_build_result().
- *      Added the '=' format character to sip_api_parse_result_ex().
- *
- * 12.5 Replaced the sipConvertFromSliceObject() macro with
- *      sip_api_convert_from_slice_object() in the public API.
- *
- * 12.4 Added sip_api_instance_destroyed_ex() to the private API.
- *
- * 12.3 Added SIP_TYPE_SCOPED_ENUM to the sipTypeDef flags.
- *      Added sip_api_convert_to_enum() to the public API.
- *      Added sip_api_convert_to_bool() to the public API.
- *      Added sip_api_long_as_char(), sip_api_long_as_signed_char(),
- *      sip_api_long_as_unsigned_char(), sip_api_long_as_short(),
- *      sip_api_long_as_unsigned_short(), sip_api_long_as_int(),
- *      sip_api_long_as_unsigned_int(), sip_api_long_as_long(),
- *      sip_api_long_as_unsigned_long(), sip_api_long_as_long_long(),
- *      sip_api_long_as_unsigned_long_long() to the public API.
- *      Deprecated sip_api_can_convert_to_enum().
- *
- * 12.2 Added sip_api_print_object() to the public API.
- *      Renamed sip_api_common_dtor() to sip_api_instance_destroyed() and added
- *      it to the public API.
- *      Added sipEventType and sip_api_register_event_handler() to the public
- *      API.
- *
- * 12.1 Added sip_api_enable_gc() to the public API.
- *
- * 12.0 Added SIP_TYPE_LIMITED_API to the sipTypeDef flags.
- *      Added sip_api_py_type_dict() and sip_api_py_type_name() to the public
- *      API.
- *      Added sip_api_set_new_user_type_handler() to the public API.
- *      Added sip_api_is_user_type() to the public API.
- *      Added sip_api_set_type_user_data() and sip_api_get_type_user_data() to
- *      the public API.
- *      Added sip_api_set_user_object() and sip_api_get_user_object() to the
- *      public API.
- *      Added sip_api_get_method() and sip_api_from_method() to the public API.
- *      Added sip_api_get_c_function() to the public API.
- *      Added sip_api_get_date() and sip_api_from_date() to the public API.
- *      Added sip_api_get_datetime() and sip_api_from_datetime() to the public
- *      API.
- *      Added sip_api_get_time() and sip_api_from_time() to the public API.
- *      Added sip_api_get_frame() to the public API.
- *      Added sip_api_check_plugin_for_type() to the public API.
- *      Added sip_api_unicode_new(), sip_api_unicode_write() and
- *      sip_api_unicode_data() to the public API.
- *      Added sip_api_get_buffer_info() and sip_api_relese_buffer_info() to the
- *      public API.
- *      Added sip_api_call_procedure_method() to the public API.
- *      Added sip_api_is_owned_by_python() to the private API.
- *      Added sip_api_is_derived_class() to the private API.
- *      Removed the im_version member from sipImportedModuleDef.
- *      Removed the im_module member from sipImportedModuleDef.
- *      Removed the em_version member from sipExportedModuleDef.
- *      Removed the em_virthandlers member from sipExportedModuleDef.
- *      Re-ordered the API functions.
- *
- * 11.3 Added sip_api_get_interpreter() to the public API.
- *
- * 11.2 Added sip_api_get_reference() to the private API.
- *
- * 11.1 Added sip_api_invoke_slot_ex().
- *
- * 11.0 Added the pyqt5QtSignal and pyqt5ClassTypeDef structures.
- *      Removed qt_interface from pyqt4ClassTypeDef.
- *      Added hack to pyqt4QtSignal.
- *
- * 10.1 Added ctd_final to sipClassTypeDef.
- *      Added ctd_init_mixin to sipClassTypeDef.
- *      Added sip_api_get_mixin_address() to the public API.
- *      Added sip_api_convert_from_new_pytype() to the public API.
- *      Added sip_api_convert_to_array() to the public API.
- *      Added sip_api_convert_to_typed_array() to the public API.
- *      Added sip_api_register_proxy_resolver() to the public API.
- *      Added sip_api_init_mixin() to the private API.
- *      Added qt_interface to pyqt4ClassTypeDef.
- *
- * 10.0 Added sip_api_set_destroy_on_exit().
- *      Added sip_api_enable_autoconversion().
- *      Removed sip_api_call_error_handler_old().
- *      Removed sip_api_start_thread().
- *
- * 9.2  Added sip_gilstate_t and SIP_RELEASE_GIL to the public API.
- *      Renamed sip_api_call_error_handler() to
- *      sip_api_call_error_handler_old().
- *      Added the new sip_api_call_error_handler() to the private API.
- *
- * 9.1  Added the capsule type.
- *      Added the 'z' format character to sip_api_build_result().
- *      Added the 'z', '!' and '$' format characters to
- *      sip_api_parse_result_ex().
- *
- * 9.0  Changed the sipVariableGetterFunc signature.
- *      Added sip_api_parse_result_ex() to the private API.
- *      Added sip_api_call_error_handler() to the private API.
- *      Added em_virterrorhandlers to sipExportedModuleDef.
- *      Re-ordered the API functions.
- *
- * 8.1  Revised the sipVariableDef structure.
- *      sip_api_get_address() is now part of the public API.
- *
- * 8.0  Changed the size of the sipSimpleWrapper structure.
- *      Added sip_api_get_address().
- *
- * 7.1  Added the 'H' format character to sip_api_parse_result().
- *      Deprecated the 'D' format character of sip_api_parse_result().
- *
- * 7.0  Added sip_api_parse_kwd_args().
- *      Added sipErrorState, sip_api_add_exception().
- *      The type initialisation function is now passed a dictionary of keyword
- *      arguments.
- *      All argument parsers now update a set of error messages rather than an
- *      argument count.
- *      The signatures of sip_api_no_function() and sip_api_no_method() have
- *      changed.
- *      Added ctd_docstring to sipClassTypeDef.
- *      Added vf_docstring to sipVersionedFunctionDef.
- *
- * 6.0  Added the sipContainerDef structure to define the contents of a class
- *      or mapped type.  Restructured sipClassDef and sipMappedTypeDef
- *      accordingly.
- *      Added the 'r' format character to sip_api_parse_args().
- *      Added the 'r' format character to sip_api_call_method() and
- *      sip_api_build_result().
- *      Added the assignment, array and copy allocation helpers.
- *
- * 5.0  Added sip_api_is_api_enabled().
- *      Renamed the td_version_nr member of sipTypeDef to be int and where -1
- *      indicates it is not versioned.
- *      Added the em_versions member to sipExportedModuleDef.
- *      Added the em_versioned_functions member to sipExportedModuleDef.
- *
- * 4.0  Much refactoring.
- *
- * 3.8  Added sip_api_register_qt_metatype() and sip_api_deprecated().
- *      Added qt_register_meta_type() to the Qt support API.
- *      The C/C++ names of enums and types are now always defined in the
- *      relevant structures and don't default to the Python name.
- *      Added the 'XE' format characters to sip_api_parse_args().
- *
- * 3.7  Added sip_api_convert_from_const_void_ptr(),
- *      sip_api_convert_from_void_ptr_and_size() and
- *      sip_api_convert_from_const_void_ptr_and_size().
- *      Added the 'g' and 'G' format characters (to replace the now deprecated
- *      'a' and 'A' format characters) to sip_api_build_result(),
- *      sip_api_call_method() and sip_api_parse_result().
- *      Added the 'k' and 'K' format characters (to replace the now deprecated
- *      'a' and 'A' format characters) to sip_api_parse_args().
- *      Added sip_api_invoke_slot().
- *      Added sip_api_parse_type().
- *      Added sip_api_is_exact_wrapped_type().
- *      Added the td_assign and td_qt fields to the sipTypeDef structure.
- *      Added the mt_assign field to the sipMappedType structure.
- *
- * 3.6  Added the 'g' format character to sip_api_parse_args().
- *
- * 3.5  Added the td_pickle field to the sipTypeDef structure.
- *      Added sip_api_transfer_break().
- *
- * 3.4  Added qt_find_connection() to the Qt support API.
- *      Added sip_api_string_as_char(), sip_api_unicode_as_wchar(),
- *      sip_api_unicode_as_wstring(), sip_api_find_class(),
- *      sip_api_find_named_enum() and sip_api_parse_signature().
- *      Added the 'A', 'w' and 'x' format characters to sip_api_parse_args(),
- *      sip_api_parse_result(), sip_api_build_result() and
- *      sip_api_call_method().
- *
- * 3.3  Added sip_api_register_int_types().
- *
- * 3.2  Added sip_api_export_symbol() and sip_api_import_symbol().
- *
- * 3.1  Added sip_api_add_mapped_type_instance().
- *
- * 3.0  Moved the Qt support out of the sip module and into PyQt.  This is
- *      such a dramatic change that there is no point in attempting to maintain
- *      backwards compatibility.
- *
- * 2.0  Added the td_flags field to the sipTypeDef structure.
- *      Added the first_child, sibling_next, sibling_prev and parent fields to
- *      the sipWrapper structure.
- *      Added the td_traverse and td_clear fields to the sipTypeDef structure.
- *      Added the em_api_minor field to the sipExportedModuleDef structure.
- *      Added sip_api_bad_operator_arg().
- *      Added sip_api_wrapper_check().
- *
- * 1.1  Added support for __pos__ and __abs__.
- *
- * 1.0  Removed all deprecated parts of the API.
- *      Removed the td_proxy field from the sipTypeDef structure.
- *      Removed the create proxy function from the 'q' and 'y' format
- *      characters to sip_api_parse_args().
- *      Removed sip_api_emit_to_slot().
- *      Reworked the enum related structures.
- *
- * 0.2  Added the 'H' format character to sip_api_parse_args().
- *
- * 0.1  Added sip_api_add_class_instance().
- *      Added the 't' format character to sip_api_parse_args().
- *      Deprecated the 'J' and 'K' format characters to sip_api_parse_result().
- *
- * 0.0  Original version.
- */
-#define SIP_API_MAJOR_NR    12
-#define SIP_API_MINOR_NR    7
+/* The ABI version implemented. */
+#define SIP_ABI_MAJOR_VERSION       12
+#define SIP_ABI_MINOR_VERSION       8
+
+/* The version of the code generator. */
+#define SIP_VERSION                 0x50500
+#define SIP_VERSION_STR             "5.5.0"
+
+/* These are all dependent on the user-specified name of the sip module. */
+#define _SIP_MODULE_FQ_NAME         "wx.siplib"
+#define _SIP_MODULE_NAME            "siplib"
+#define _SIP_MODULE_SHARED          1
+#define _SIP_MODULE_ENTRY           PyInit_siplib
+#define _SIP_MODULE_LEGACY          0
+
+/* Support the historical names. */
+#define SIP_API_MAJOR_NR    SIP_ABI_MAJOR_VERSION
+#define SIP_API_MINOR_NR    SIP_ABI_MINOR_VERSION
 
 
 /*
@@ -324,29 +110,16 @@ typedef unsigned int uint;
 #endif
 
 
-/* Some Python compatibility stuff. */
-#if PY_VERSION_HEX >= 0x02050000
-
-#define SIP_SSIZE_T         Py_ssize_t
-#define SIP_SSIZE_T_FORMAT  "%zd"
-
-#define SIP_MLNAME_CAST(s)  (s)
-#define SIP_MLDOC_CAST(s)   (s)
-#define SIP_TPNAME_CAST(s)  (s)
-
-#else
-
-#define SIP_SSIZE_T         int
-#define SIP_SSIZE_T_FORMAT  "%d"
-
-#define SIP_MLNAME_CAST(s)  ((char *)(s))
-#define SIP_MLDOC_CAST(s)   ((char *)(s))
-#define SIP_TPNAME_CAST(s)  ((char *)(s))
-
-#endif
-
-#if PY_MAJOR_VERSION >= 3
+/* Remove in v5.1. */
+#define SIP_SSIZE_T             Py_ssize_t
+#define SIP_SSIZE_T_FORMAT      "%zd"
+#define SIP_USE_PYCAPSULE
+#define SIP_MODULE_RETURN(v)    return (v)
 
+/*
+ * Remove in v5.1.  These are undocumented and can be removed when PyQt5 drops
+ * support for Python v2.
+ */
 #define SIPLong_Check       PyLong_Check
 #define SIPLong_FromLong    PyLong_FromLong
 #define SIPLong_AsLong      PyLong_AsLong
@@ -359,57 +132,6 @@ typedef unsigned int uint;
 #define SIPBytes_AS_STRING  PyBytes_AS_STRING
 #define SIPBytes_GET_SIZE   PyBytes_GET_SIZE
 
-#if PY_MINOR_VERSION >= 1
-#define SIP_USE_PYCAPSULE
-#endif
-
-#if PY_MINOR_VERSION < 2
-#define SIP_SUPPORT_PYCOBJECT
-#endif
-
-#else
-
-#define SIPLong_Check       PyInt_Check
-#define SIPLong_FromLong    PyInt_FromLong
-#define SIPLong_AsLong      PyInt_AsLong
-
-#define SIPBytes_Check      PyString_Check
-#define SIPBytes_FromString PyString_FromString
-#define SIPBytes_FromStringAndSize  PyString_FromStringAndSize
-#define SIPBytes_AsString   PyString_AsString
-#define SIPBytes_Size       PyString_Size
-#define SIPBytes_AS_STRING  PyString_AS_STRING
-#define SIPBytes_GET_SIZE   PyString_GET_SIZE
-
-#if PY_MINOR_VERSION >= 7
-#define SIP_USE_PYCAPSULE
-#endif
-
-#define SIP_SUPPORT_PYCOBJECT
-
-#endif
-
-#if !defined(Py_REFCNT)
-#define Py_REFCNT(ob)       (((PyObject*)(ob))->ob_refcnt)
-#endif
-
-#if !defined(Py_TYPE)
-#define Py_TYPE(ob)         (((PyObject*)(ob))->ob_type)
-#endif
-
-#if !defined(PyVarObject_HEAD_INIT)
-#define PyVarObject_HEAD_INIT(type, size)   PyObject_HEAD_INIT(type) size,
-#endif
-
-
-#if defined(SIP_USE_PYCAPSULE)
-#define SIPCapsule_FromVoidPtr(p, n)    PyCapsule_New((p), (n), NULL)
-#define SIPCapsule_AsVoidPtr(p, n)      PyCapsule_GetPointer((p), (n))
-#else
-#define SIPCapsule_FromVoidPtr(p, n)    sipConvertFromVoidPtr((p))
-#define SIPCapsule_AsVoidPtr(p, n)      sipConvertToVoidPtr((p))
-#endif
-
 
 /*
  * The mask that can be passed to sipTrace().
@@ -512,17 +234,12 @@ typedef int (*sipFinalFunc)(PyObject *, void *, PyObject *, PyObject **);
 typedef void *(*sipAccessFunc)(sipSimpleWrapper *, AccessFuncOp);
 typedef int (*sipTraverseFunc)(void *, visitproc, void *);
 typedef int (*sipClearFunc)(void *);
-#if PY_MAJOR_VERSION >= 3
 typedef int (*sipGetBufferFuncLimited)(PyObject *, void *, sipBufferDef *);
 typedef void (*sipReleaseBufferFuncLimited)(PyObject *, void *);
 #if !defined(Py_LIMITED_API)
 typedef int (*sipGetBufferFunc)(PyObject *, void *, Py_buffer *, int);
 typedef void (*sipReleaseBufferFunc)(PyObject *, void *, Py_buffer *);
 #endif
-#else
-typedef SIP_SSIZE_T (*sipBufferFunc)(PyObject *, void *, SIP_SSIZE_T, void **);
-typedef SIP_SSIZE_T (*sipSegCountFunc)(PyObject *, void *, SIP_SSIZE_T *);
-#endif
 typedef void (*sipDeallocFunc)(sipSimpleWrapper *);
 typedef void *(*sipCastFunc)(void *, const sipTypeDef *);
 typedef const sipTypeDef *(*sipSubClassConvertFunc)(void **);
@@ -531,9 +248,9 @@ typedef PyObject *(*sipConvertFromFunc)(void *, PyObject *);
 typedef void (*sipVirtErrorHandlerFunc)(sipSimpleWrapper *, sip_gilstate_t);
 typedef int (*sipVirtHandlerFunc)(sip_gilstate_t, sipVirtErrorHandlerFunc,
         sipSimpleWrapper *, PyObject *, ...);
-typedef void (*sipAssignFunc)(void *, SIP_SSIZE_T, void *);
-typedef void *(*sipArrayFunc)(SIP_SSIZE_T);
-typedef void *(*sipCopyFunc)(const void *, SIP_SSIZE_T);
+typedef void (*sipAssignFunc)(void *, Py_ssize_t, void *);
+typedef void *(*sipArrayFunc)(Py_ssize_t);
+typedef void *(*sipCopyFunc)(const void *, Py_ssize_t);
 typedef void (*sipReleaseFunc)(void *, int);
 typedef PyObject *(*sipPickleFunc)(void *);
 typedef int (*sipAttrGetterFunc)(const sipTypeDef *, PyObject *);
@@ -544,7 +261,7 @@ typedef int (*sipNewUserTypeFunc)(sipWrapperType *);
 typedef void (*sipWrapperVisitorFunc)(sipSimpleWrapper *, void *);
 
 
-#if !defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x03020000
+#if !defined(Py_LIMITED_API)
 /*
  * The meta-type of a wrapper type.
  */
@@ -638,6 +355,7 @@ struct _sipWrapper {
 
 
 /*
+ * Removed in v5.1.
  * The meta-type of an enum type.  (This is exposed only to support the
  * deprecated sipConvertFromNamedEnum() macro.)
  */
@@ -761,7 +479,7 @@ struct _sipBufferDef {
     void *bd_buffer;
 
     /* The length of the buffer. */
-    SIP_SSIZE_T bd_length;
+    Py_ssize_t bd_length;
 
     /* Set if the buffer is read-only. */
     int bd_readonly;
@@ -782,7 +500,7 @@ struct _sipBufferInfoDef {
     PyObject *bi_obj;
 
     /* The length of the buffer in bytes. */
-    SIP_SSIZE_T bi_len;
+    Py_ssize_t bi_len;
 
     /* The number of dimensions. */
     int bi_ndim;
@@ -813,11 +531,6 @@ struct _sipMethodDef {
 
     /* The bound object. */
     PyObject *pm_self;
-
-#if PY_MAJOR_VERSION < 3
-    /* The class. */
-    PyObject *pm_class;
-#endif
 };
 
 
@@ -871,9 +584,6 @@ typedef enum {
 typedef enum {
     str_slot,           /* __str__ */
     int_slot,           /* __int__ */
-#if PY_MAJOR_VERSION < 3
-    long_slot,          /* __long__ */
-#endif
     float_slot,         /* __float__ */
     len_slot,           /* __len__ */
     contains_slot,      /* __contains__ */
@@ -916,18 +626,13 @@ typedef enum {
     ne_slot,            /* __ne__ */
     gt_slot,            /* __gt__ */
     ge_slot,            /* __ge__ */
-#if PY_MAJOR_VERSION < 3
-    cmp_slot,           /* __cmp__ */
-#endif
     bool_slot,          /* __bool__, __nonzero__ */
     neg_slot,           /* __neg__ */
     repr_slot,          /* __repr__ */
     hash_slot,          /* __hash__ */
     pos_slot,           /* __pos__ */
     abs_slot,           /* __abs__ */
-#if PY_VERSION_HEX >= 0x02050000
     index_slot,         /* __index__ */
-#endif
     iter_slot,          /* __iter__ */
     next_slot,          /* __next__ */
     setattr_slot,       /* __setattr__, __delattr__ */
@@ -1039,14 +744,8 @@ struct _sipTypeDef {
     /* The C/C++ name of the type. */
     int td_cname;
 
-    /*
-     * The Python type object.  This needs to be a union until we remove the
-     * deprecated sipClass_* macros.
-     */
-    union {
-        PyTypeObject *td_py_type;
-        sipWrapperType *td_wrapper_type;
-    } u;
+    /* The Python type object. */
+    PyTypeObject *td_py_type;
 
     /* Any additional fixed data generated by a plugin. */
     void *td_plugin_data;
@@ -1130,7 +829,6 @@ typedef struct _sipClassTypeDef {
     /* The clear function. */
     sipClearFunc ctd_clear;
 
-#if PY_MAJOR_VERSION >= 3
     /* The get buffer function. */
 #if defined(Py_LIMITED_API)
     sipGetBufferFuncLimited ctd_getbuffer;
@@ -1144,19 +842,6 @@ typedef struct _sipClassTypeDef {
 #else
     sipReleaseBufferFunc ctd_releasebuffer;
 #endif
-#else
-    /* The read buffer function. */
-    sipBufferFunc ctd_readbuffer;
-
-    /* The write buffer function. */
-    sipBufferFunc ctd_writebuffer;
-
-    /* The segment count function. */
-    sipSegCountFunc ctd_segcount;
-
-    /* The char buffer function. */
-    sipBufferFunc ctd_charbuffer;
-#endif
 
     /* The deallocation function. */
     sipDeallocFunc ctd_dealloc;
@@ -1257,6 +942,7 @@ typedef struct _sipExternalTypeDef {
 
 
 /*
+ * Remove in v5.1.
  * The information describing a mapped class.  This (and anything that uses it)
  * is deprecated.
  */
@@ -1625,8 +1311,9 @@ typedef struct _sipTypeInstanceDef {
 
 
 /*
+ * Remove in v5.1.
  * Define a mapping between a wrapped type identified by a string and the
- * corresponding Python type.  This is deprecated.
+ * corresponding Python type.
  */
 typedef struct _sipStringTypeClassMap {
     /* The type as a string. */
@@ -1638,8 +1325,9 @@ typedef struct _sipStringTypeClassMap {
 
 
 /*
+ * Remove in v5.1.
  * Define a mapping between a wrapped type identified by an integer and the
- * corresponding Python type.  This is deprecated.
+ * corresponding Python type.
  */
 typedef struct _sipIntTypeClassMap {
     /* The type as an integer. */
@@ -1660,11 +1348,6 @@ typedef struct _sipPyMethod {
 
     /* Self if it is a bound method. */
     PyObject *mself;
-
-#if PY_MAJOR_VERSION < 3
-    /* The class. */
-    PyObject *mclass;
-#endif
 } sipPyMethod;
 
 
@@ -1707,7 +1390,7 @@ typedef struct _sipAPIDef {
     PyTypeObject *api_voidptr_type;
 
     void (*api_bad_catcher_result)(PyObject *method);
-    void (*api_bad_length_for_slice)(SIP_SSIZE_T seqlen, SIP_SSIZE_T slicelen);
+    void (*api_bad_length_for_slice)(Py_ssize_t seqlen, Py_ssize_t slicelen);
     PyObject *(*api_build_result)(int *isErr, const char *fmt, ...);
     PyObject *(*api_call_method)(int *isErr, PyObject *method, const char *fmt,
             ...);
@@ -1715,8 +1398,8 @@ typedef struct _sipAPIDef {
             sipSimpleWrapper *, PyObject *, const char *, ...);
     PyObject *(*api_connect_rx)(PyObject *txObj, const char *sig,
             PyObject *rxObj, const char *slot, int type);
-    SIP_SSIZE_T (*api_convert_from_sequence_index)(SIP_SSIZE_T idx,
-            SIP_SSIZE_T len);
+    Py_ssize_t (*api_convert_from_sequence_index)(Py_ssize_t idx,
+            Py_ssize_t len);
     int (*api_can_convert_to_type)(PyObject *pyObj, const sipTypeDef *td,
             int flags);
     void *(*api_convert_to_type)(PyObject *pyObj, const sipTypeDef *td,
@@ -1754,9 +1437,9 @@ typedef struct _sipAPIDef {
     PyObject *(*api_convert_from_void_ptr)(void *val);
     PyObject *(*api_convert_from_const_void_ptr)(const void *val);
     PyObject *(*api_convert_from_void_ptr_and_size)(void *val,
-            SIP_SSIZE_T size);
+            Py_ssize_t size);
     PyObject *(*api_convert_from_const_void_ptr_and_size)(const void *val,
-            SIP_SSIZE_T size);
+            Py_ssize_t size);
     void *(*api_convert_to_void_ptr)(PyObject *obj);
     int (*api_export_symbol)(const char *name, void *sym);
     void *(*api_import_symbol)(const char *name);
@@ -1777,12 +1460,12 @@ typedef struct _sipAPIDef {
     PyObject *(*api_convert_from_new_pytype)(void *cpp, PyTypeObject *py_type,
             sipWrapper *owner, sipSimpleWrapper **selfp, const char *fmt, ...);
     PyObject *(*api_convert_to_typed_array)(void *data, const sipTypeDef *td,
-            const char *format, size_t stride, SIP_SSIZE_T len, int flags);
+            const char *format, size_t stride, Py_ssize_t len, int flags);
     PyObject *(*api_convert_to_array)(void *data, const char *format,
-            SIP_SSIZE_T len, int flags);
+            Py_ssize_t len, int flags);
     int (*api_register_proxy_resolver)(const sipTypeDef *td,
             sipProxyResolverFunc resolver);
-    PyInterpreterState *(*api_get_interpreter)();
+    PyInterpreterState *(*api_get_interpreter)(void);
     sipNewUserTypeFunc (*api_set_new_user_type_handler)(const sipTypeDef *,
             sipNewUserTypeFunc);
     void (*api_set_type_user_data)(sipWrapperType *, void *);
@@ -1801,9 +1484,9 @@ typedef struct _sipAPIDef {
     int (*api_is_user_type)(const sipWrapperType *);
     struct _frame *(*api_get_frame)(int);
     int (*api_check_plugin_for_type)(const sipTypeDef *, const char *);
-    PyObject *(*api_unicode_new)(SIP_SSIZE_T, unsigned, int *, void **);
+    PyObject *(*api_unicode_new)(Py_ssize_t, unsigned, int *, void **);
     void (*api_unicode_write)(int, void *, int, unsigned);
-    void *(*api_unicode_data)(PyObject *, int *, SIP_SSIZE_T *);
+    void *(*api_unicode_data)(PyObject *, int *, Py_ssize_t *);
     int (*api_get_buffer_info)(PyObject *, sipBufferInfoDef *);
     void (*api_release_buffer_info)(sipBufferInfoDef *);
     PyObject *(*api_get_user_object)(const sipSimpleWrapper *);
@@ -1948,14 +1631,22 @@ typedef struct _sipAPIDef {
     /*
      * The following are part of the public API.
      */
-    int (*api_convert_from_slice_object)(PyObject *slice, SIP_SSIZE_T length,
-            SIP_SSIZE_T *start, SIP_SSIZE_T *stop, SIP_SSIZE_T *step,
-            SIP_SSIZE_T *slicelength);
+    int (*api_convert_from_slice_object)(PyObject *slice, Py_ssize_t length,
+            Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step,
+            Py_ssize_t *slicelength);
     size_t (*api_long_as_size_t)(PyObject *o);
     void (*api_visit_wrappers)(sipWrapperVisitorFunc visitor, void *closure);
     int (*api_register_exit_notifier)(PyMethodDef *md);
+
+    /*
+     * The following are not part of the public API.
+     */
+    PyObject *(*api_is_py_method_12_8)(sip_gilstate_t *gil, char *pymc,
+            sipSimpleWrapper **sipSelfp, const char *cname, const char *mname);
 } sipAPIDef;
 
+const sipAPIDef *sip_init_library(PyObject *mod_dict);
+
 
 /*
  * The API implementing the optional Qt support.
@@ -2020,7 +1711,7 @@ typedef struct _sipQtAPI {
 #define SIP_ACCFUNC         0x0008  /* If there is an access function. */
 #define SIP_NOT_IN_MAP      0x0010  /* If Python object is not in the map. */
 
-#if !defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x03020000
+#if !defined(Py_LIMITED_API)
 #define SIP_PY_OWNED        0x0020  /* If owned by Python. */
 #define SIP_SHARE_MAP       0x0040  /* If the map slot might be occupied. */
 #define SIP_CPP_HAS_REF     0x0080  /* If C/C++ has a reference. */
@@ -2068,22 +1759,16 @@ typedef struct _sipQtAPI {
 #define sipTypeIsMapped(td) (((td)->td_flags & SIP_TYPE_TYPE_MASK) == SIP_TYPE_MAPPED)
 #define sipTypeIsEnum(td)   (((td)->td_flags & SIP_TYPE_TYPE_MASK) == SIP_TYPE_ENUM)
 #define sipTypeIsScopedEnum(td) (((td)->td_flags & SIP_TYPE_TYPE_MASK) == SIP_TYPE_SCOPED_ENUM)
-#define sipTypeAsPyTypeObject(td)   ((td)->u.td_py_type)
+#define sipTypeAsPyTypeObject(td)   ((td)->td_py_type)
 #define sipTypeName(td)     sipNameFromPool((td)->td_module, (td)->td_cname)
 #define sipTypePluginData(td)   ((td)->td_plugin_data)
 
 
 /*
- * Note that this was never actually documented as being part of the public
- * API.  It is now deprecated.  sipIsUserType() should be used instead.
- */
-#define sipIsExactWrappedType(wt)   (sipTypeAsPyTypeObject((wt)->wt_td) == (PyTypeObject *)(wt))
-
-
-/*
- * The following are deprecated parts of the public API.
+ * Remove in v5.1.
  */
 #define sipClassName(w)     PyString_FromString(Py_TYPE(w)->tp_name)
+#define sipIsExactWrappedType(wt)   (sipTypeAsPyTypeObject((wt)->wt_td) == (PyTypeObject *)(wt))
 
 
 /*
From c385873d705581260e3f9f22524062a3045fec00 Mon Sep 17 00:00:00 2001
From: Robin Dunn <robin@alldunn.com>
Date: Tue, 5 Jan 2021 11:11:26 -0800
Subject: [PATCH] Add SIP_ABI to the config object

---
 build.py             | 2 +-
 buildtools/config.py | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/build.py b/build.py
index 9ffd520b6..1db1b92ae 100755
--- a/build.py
+++ b/build.py
@@ -1278,7 +1278,7 @@ def cmd_sip(options, args):
         pycode = 'pycode'+base+':'+pycode
 
         sip_runner(src_name,
-            abi_version = '12.8',       # siplib abi version
+            abi_version = cfg.SIP_ABI,  # siplib abi version
             warnings = True,            # enable warning messages
             docstrings = True,          # enable the automatic generation of docstrings
             release_gil = True,         # always release and reacquire the GIL
diff --git a/buildtools/config.py b/buildtools/config.py
index a7c2a0d59..e9b865dd7 100644
--- a/buildtools/config.py
+++ b/buildtools/config.py
@@ -85,6 +85,8 @@ class Configuration(object):
     PKGDIR = 'wx'
     # The name of the top-level package
 
+    SIP_ABI = '12.8'
+
     # ---------------------------------------------------------------
     # Basic initialization and configuration code
 
From 224630cf647341bc719d5fbde08e42e99317aab6 Mon Sep 17 00:00:00 2001
From: Robin Dunn <robin@alldunn.com>
Date: Thu, 7 Jan 2021 13:43:11 -0800
Subject: [PATCH] Add Config.SIP_TRACE flag

---
 build.py             | 2 +-
 buildtools/config.py | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/build.py b/build.py
index 1db1b92ae..73e5d5018 100755
--- a/build.py
+++ b/build.py
@@ -1285,7 +1285,7 @@ def cmd_sip(options, args):
             sip_module = 'wx.siplib',   # the fully qualified name of the sip module
             sbf_file=sbf,               # File to write the generated file lists to
             exceptions = False,         # enable support for exceptions
-            tracing = False,            # generate code with tracing enabled
+            tracing = cfg.SIP_TRACE,    # generate code with tracing enabled
             sources_dir = tmpdir,       # the name of the code directory
             extracts = [pycode],        # add <ID:FILE> to the list of extracts to generate
             pyi_extract=pyi_extract,    # the name of the .pyi stub file
diff --git a/buildtools/config.py b/buildtools/config.py
index e9b865dd7..c6d94da12 100644
--- a/buildtools/config.py
+++ b/buildtools/config.py
@@ -86,6 +86,7 @@ class Configuration(object):
     # The name of the top-level package
 
     SIP_ABI = '12.8'
+    SIP_TRACE = False
 
     # ---------------------------------------------------------------
     # Basic initialization and configuration code