Update to use sip 6.6.x

1) Switch to running sip using its new sip-build command line interface,
   which requires writing out a pyproject.toml to configure it.
2) Generate the sip build file (sbf) ourselves since sip no longer writes
   them.
3) Remove the embedded sip module code and generate it on the fly during
   the build process.

Fixes #2169.
This commit is contained in:
Scott Talbert
2022-06-06 22:23:29 -04:00
parent 6a5e93d0c8
commit 90171ba216
20 changed files with 63 additions and 21531 deletions

View File

@@ -46,7 +46,6 @@ from buildtools.config import Config, msg, opj, posixjoin, loadETG, etg2sip, fi
getVcsRev, runcmd, textfile_open, getSipFiles, \ getVcsRev, runcmd, textfile_open, getSipFiles, \
getVisCVersion, getToolsPlatformName, updateLicenseFiles, \ getVisCVersion, getToolsPlatformName, updateLicenseFiles, \
TemporaryDirectory, getMSVCInfo TemporaryDirectory, getMSVCInfo
from buildtools.wxpysip import sip_runner
import buildtools.version as version import buildtools.version as version
@@ -1263,7 +1262,7 @@ def cmd_sip(options, args):
base = os.path.basename(os.path.splitext(src_name)[0]) base = os.path.basename(os.path.splitext(src_name)[0])
sbf = posixjoin(cfg.SIPOUT, base) + '.sbf' sbf = posixjoin(cfg.SIPOUT, base) + '.sbf'
pycode = base[1:] # remove the leading _ pycode = base[1:] # remove the leading _
pycode = posixjoin(cfg.PKGDIR, pycode) + '.py' pycode = opj(cfg.ROOT_DIR, cfg.PKGDIR, pycode) + '.py'
# Check if any of the included files are newer than the .sbf file # Check if any of the included files are newer than the .sbf file
# produced by the previous run of sip. If not then we don't need to # produced by the previous run of sip. If not then we don't need to
@@ -1281,23 +1280,54 @@ def cmd_sip(options, args):
# module's .py file # module's .py file
pycode = 'pycode'+base+':'+pycode pycode = 'pycode'+base+':'+pycode
sip_runner(src_name, # Write out a pyproject.toml to configure sip
abi_version = cfg.SIP_ABI, # siplib abi version pyproject_toml = (
warnings = True, # enable warning messages '[build-system]\n'
docstrings = True, # enable the automatic generation of docstrings 'requires = ["sip >=5.5.0, <7"]\n'
release_gil = True, # always release and reacquire the GIL 'build-backend = "sipbuild.api"\n'
sip_module = 'wx.siplib', # the fully qualified name of the sip module '\n'
sbf_file=sbf, # File to write the generated file lists to '[tool.sip.metadata]\n'
exceptions = False, # enable support for exceptions 'name = "{base}"\n'
tracing = cfg.SIP_TRACE, # generate code with tracing enabled '\n'
sources_dir = tmpdir, # the name of the code directory '[tool.sip.bindings.{base}]\n'
extracts = [pycode], # add <ID:FILE> to the list of extracts to generate 'docstrings = true\n'
pyi_extract=pyi_extract, # the name of the .pyi stub file 'release-gil = true\n'
include_dirs = [ 'exceptions = false\n'
os.path.join(phoenixDir(), 'src'), 'tracing = {tracing}\n'
os.path.join(phoenixDir(), 'sip', 'gen'), 'protected-is-public = false\n'
]) 'generate-extracts = [\'{extracts}\']\n'
'pep484-pyi = false\n'
'\n'
'[tool.sip.project]\n'
'abi-version = "{abi_version}"\n'
'sip-files-dir = \'{sip_gen_dir}\'\n'
'sip-include-dirs = [\'{src_dir}\']\n'
'sip-module = "wx.siplib"\n'
).format(
base=base,
abi_version=cfg.SIP_ABI,
tracing=str(cfg.SIP_TRACE).lower(),
extracts=pycode,
src_dir=opj(phoenixDir(), 'src'),
sip_gen_dir=opj(phoenixDir(), 'sip', 'gen'),
)
with open(opj(tmpdir, 'pyproject.toml'), 'w') as f:
f.write(pyproject_toml)
sip_pwd = pushDir(tmpdir)
cmd = 'sip-build --no-compile'
runcmd(cmd)
del sip_pwd
# Write out a sip build file (no longer done by sip itself)
sip_tmp_out_dir = opj(tmpdir, 'build', base)
sip_pwd = pushDir(sip_tmp_out_dir)
header = glob.glob('*.h')[0]
sources = glob.glob('*.cpp')
del sip_pwd
with open(sbf, 'w') as f:
f.write("sources = {}\n".format(' '.join(sources)))
f.write("headers = {}\n".format(header))
classesNeedingClassInfo = { 'sip_corewxTreeCtrl.cpp' : 'wxTreeCtrl', } classesNeedingClassInfo = { 'sip_corewxTreeCtrl.cpp' : 'wxTreeCtrl', }
@@ -1306,7 +1336,7 @@ def cmd_sip(options, args):
srcTxt = f.read() srcTxt = f.read()
if keepHashLines: if keepHashLines:
# Either just fix the pathnames in the #line lines... # Either just fix the pathnames in the #line lines...
srcTxt = srcTxt.replace(tmpdir, cfg.SIPOUT) srcTxt = srcTxt.replace(sip_tmp_out_dir, cfg.SIPOUT)
else: else:
# ...or totally remove them by replacing those lines with '' # ...or totally remove them by replacing those lines with ''
import re import re
@@ -1337,7 +1367,7 @@ def cmd_sip(options, args):
# Check each file in tmpdir to see if it is different than the same file # Check each file in tmpdir to see if it is different than the same file
# in cfg.SIPOUT. If so then copy the new one to cfg.SIPOUT, otherwise # in cfg.SIPOUT. If so then copy the new one to cfg.SIPOUT, otherwise
# ignore it. # ignore it.
for src in glob.glob(tmpdir + '/*'): for src in glob.glob(sip_tmp_out_dir + '/*'):
dest = opj(cfg.SIPOUT, os.path.basename(src)) dest = opj(cfg.SIPOUT, os.path.basename(src))
if not os.path.exists(dest): if not os.path.exists(dest):
msg('%s is a new file, copying...' % os.path.basename(src)) msg('%s is a new file, copying...' % os.path.basename(src))
@@ -1360,6 +1390,17 @@ def cmd_sip(options, args):
# Remove tmpdir and its contents # Remove tmpdir and its contents
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)
# Generate sip module code
deleteIfExists(cfg.SIPINC)
with tempfile.TemporaryDirectory() as tmpdir:
cmd = 'sip-module --sdist --abi-version {} --target-dir {} wx.siplib'.format(cfg.SIP_ABI, tmpdir)
runcmd(cmd)
tf_name = glob.glob(tmpdir + '/*.tar*')[0]
tf_dir = os.path.splitext(os.path.splitext(tf_name)[0])[0]
with tarfile.open(tf_name) as tf:
tf.extractall(tmpdir)
shutil.move(tf_dir, cfg.SIPINC)
def cmd_touch(options, args): def cmd_touch(options, args):
cmdTimer = CommandTimer('touch') cmdTimer = CommandTimer('touch')

View File

@@ -1,110 +0,0 @@
#----------------------------------------------------------------------
# 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))

View File

@@ -3,7 +3,7 @@
appdirs appdirs
setuptools < 45 ; python_version < '3.0' setuptools < 45 ; python_version < '3.0'
setuptools ; python_version >= '3.0' setuptools ; python_version >= '3.0'
sip == 5.5.0 sip == 6.6.2
wheel wheel
twine twine

View File

@@ -1,48 +0,0 @@
RIVERBANK COMPUTING LIMITED LICENSE AGREEMENT FOR SIP
1. This LICENSE AGREEMENT is between Riverbank Computing Limited ("Riverbank"),
and the Individual or Organization ("Licensee") accessing and otherwise using
SIP software in source or binary form and its associated documentation. SIP
comprises a software tool for generating Python bindings for software C and C++
libraries, and a Python extension module used at runtime by those generated
bindings.
2. Subject to the terms and conditions of this License Agreement, Riverbank
hereby grants Licensee a nonexclusive, royalty-free, world-wide license to
reproduce, analyze, test, perform and/or display publicly, prepare derivative
works, distribute, and otherwise use SIP alone or in any derivative version,
provided, however, that Riverbank's License Agreement and Riverbank's notice of
copyright, e.g., "Copyright (c) 2015 Riverbank Computing Limited; All Rights
Reserved" are retained in SIP alone or in any derivative version prepared by
Licensee.
3. In the event Licensee prepares a derivative work that is based on or
incorporates SIP or any part thereof, and wants to make the derivative work
available to others as provided herein, then Licensee hereby agrees to include
in any such work a brief summary of the changes made to SIP.
4. Licensee may not use SIP to generate Python bindings for any C or C++
library for which bindings are already provided by Riverbank.
5. Riverbank is making SIP available to Licensee on an "AS IS" basis.
RIVERBANK MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY
OF EXAMPLE, BUT NOT LIMITATION, RIVERBANK MAKES NO AND DISCLAIMS ANY
REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR
PURPOSE OR THAT THE USE OF SIP WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
6. RIVERBANK SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF SIP FOR ANY
INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING,
DISTRIBUTING, OR OTHERWISE USING SIP, OR ANY DERIVATIVE THEREOF, EVEN IF
ADVISED OF THE POSSIBILITY THEREOF.
7. This License Agreement will automatically terminate upon a material breach
of its terms and conditions.
8. Nothing in this License Agreement shall be deemed to create any relationship
of agency, partnership, or joint venture between Riverbank and Licensee. This
License Agreement does not grant permission to use Riverbank trademarks or
trade name in a trademark sense to endorse or promote products or services of
Licensee, or any third party.
9. By copying, installing or otherwise using SIP, Licensee agrees to be bound
by the terms and conditions of this License Agreement.

View File

@@ -1,11 +0,0 @@
This folder contains a copy of the SIP runtime library code. It is
here so we can make it part of the wxPython build instead of needing
to have a dependency upon SIP already being installed on user
machines.
3rd party extension modules that need to use or interact with wxPython
types or other items will need to ensure that they #include the sip.h
located in this folder so they will know the proper module name to
import to find this version of the runtime library. This feature was
added in SIP 4.12.

View File

@@ -1,300 +0,0 @@
/*
* The implementation of the support for setting API versions.
*
* Copyright (c) 2019 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <Python.h>
#include <string.h>
#include "sipint.h"
/*
* The structure that defines the version number of an API.
*/
typedef struct _apiVersionDef {
/* The name of the API. */
const char *api_name;
/*
* The version number of the API. This will either be set explicitly via
* a call to sip.setapi() or implicitly by an imported module.
*/
int version_nr;
/* The next in the list of APIs. */
struct _apiVersionDef *next;
} apiVersionDef;
/*
* The list of API versions.
*/
static apiVersionDef *api_versions = NULL;
/*
* Forward declarations.
*/
static int add_api(const char *api, int version_nr);
static apiVersionDef *find_api(const char *api);
/*
* See if a range of versions of a particular API is enabled.
*/
int sip_api_is_api_enabled(const char *name, int from, int to)
{
const apiVersionDef *avd;
if ((avd = find_api(name)) == NULL)
return FALSE;
if (from > 0 && avd->version_nr < from)
return FALSE;
if (to > 0 && avd->version_nr >= to)
return FALSE;
return TRUE;
}
/*
* Initialise the the API for a module and return a negative value on error.
*/
int sipInitAPI(sipExportedModuleDef *em, PyObject *mod_dict)
{
int *apis, i;
sipVersionedFunctionDef *vf;
sipTypeDef **tdp;
/* See if the module defines any APIs. */
if ((apis = em->em_versions) != NULL)
{
while (apis[0] >= 0)
{
/*
* See if it is an API definition rather than a range
* definition.
*/
if (apis[2] < 0)
{
const char *api_name;
const apiVersionDef *avd;
api_name = sipNameFromPool(em, apis[0]);
/* Use the default version if not already set explicitly. */
if ((avd = find_api(api_name)) == NULL)
if (add_api(api_name, apis[1]) < 0)
return -1;
}
apis += 3;
}
}
/* Add any versioned global functions to the module dictionary. */
if ((vf = em->em_versioned_functions) != NULL)
{
while (vf->vf_name >= 0)
{
if (sipIsRangeEnabled(em, vf->vf_api_range))
{
const char *func_name = sipNameFromPool(em, vf->vf_name);
PyMethodDef *pmd;
PyObject *py_func;
if ((pmd = sip_api_malloc(sizeof (PyMethodDef))) == NULL)
return -1;
pmd->ml_name = func_name;
pmd->ml_meth = vf->vf_function;
pmd->ml_flags = vf->vf_flags;
pmd->ml_doc = vf->vf_docstring;
if ((py_func = PyCFunction_New(pmd, NULL)) == NULL)
return -1;
if (PyDict_SetItemString(mod_dict, func_name, py_func) < 0)
{
Py_DECREF(py_func);
return -1;
}
Py_DECREF(py_func);
}
++vf;
}
}
/* Update the types table according to any version information. */
for (tdp = em->em_types, i = 0; i < em->em_nrtypes; ++i, ++tdp)
{
sipTypeDef *td;
if ((td = *tdp) != NULL && td->td_version >= 0)
{
do
{
if (sipIsRangeEnabled(em, td->td_version))
{
/* Update the type with the enabled version. */
*tdp = td;
break;
}
}
while ((td = td->td_next_version) != NULL);
/*
* If there is no enabled version then stub the disabled version
* so that we don't lose the name from the (sorted) types table.
*/
if (td == NULL)
sipTypeSetStub(*tdp);
}
}
return 0;
}
/*
* Get the version number for an API.
*/
PyObject *sipGetAPI(PyObject *self, PyObject *args)
{
const char *api;
const apiVersionDef *avd;
(void)self;
if (sip_api_deprecated(NULL, "getapi") < 0)
return NULL;
if (!PyArg_ParseTuple(args, "s:getapi", &api))
return NULL;
if ((avd = find_api(api)) == NULL)
{
PyErr_Format(PyExc_ValueError, "unknown API '%s'", api);
return NULL;
}
return PyLong_FromLong(avd->version_nr);
}
/*
* Set the version number for an API.
*/
PyObject *sipSetAPI(PyObject *self, PyObject *args)
{
const char *api;
int version_nr;
const apiVersionDef *avd;
(void)self;
if (sip_api_deprecated(NULL, "setapi") < 0)
return NULL;
if (!PyArg_ParseTuple(args, "si:setapi", &api, &version_nr))
return NULL;
if (version_nr < 1)
{
PyErr_Format(PyExc_ValueError,
"API version numbers must be greater or equal to 1, not %d",
version_nr);
return NULL;
}
if ((avd = find_api(api)) == NULL)
{
char *api_copy;
/* Make a deep copy of the name. */
if ((api_copy = sip_api_malloc(strlen(api) + 1)) == NULL)
return NULL;
strcpy(api_copy, api);
if (add_api(api_copy, version_nr) < 0)
return NULL;
}
else if (avd->version_nr != version_nr)
{
PyErr_Format(PyExc_ValueError,
"API '%s' has already been set to version %d", api,
avd->version_nr);
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
/*
* Add a new API to the global list returning a negative value on error.
*/
static int add_api(const char *api, int version_nr)
{
apiVersionDef *avd;
if ((avd = sip_api_malloc(sizeof (apiVersionDef))) == NULL)
return -1;
avd->api_name = api;
avd->version_nr = version_nr;
avd->next = api_versions;
api_versions = avd;
return 0;
}
/*
* Return the definition for the given API, or NULL if there was none.
*/
static apiVersionDef *find_api(const char *api)
{
apiVersionDef *avd;
for (avd = api_versions; avd != NULL; avd = avd->next)
if (strcmp(avd->api_name, api) == 0)
break;
return avd;
}
/*
* Return TRUE if a range defined by a range index is enabled.
*/
int sipIsRangeEnabled(sipExportedModuleDef *em, int range_index)
{
int *range = &em->em_versions[range_index * 3];
const char *api_name = sipNameFromPool(em, range[0]);
return sip_api_is_api_enabled(api_name, range[1], range[2]);
}

View File

@@ -1,703 +0,0 @@
/*
* This file implements the API for the array type.
*
* Copyright (c) 2019 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <Python.h>
#include <stddef.h>
#include <string.h>
#include "sipint.h"
#include "array.h"
/* The object data structure. */
typedef struct {
PyObject_HEAD
void *data;
const sipTypeDef *td;
const char *format;
size_t stride;
Py_ssize_t len;
int flags;
PyObject *owner;
} sipArrayObject;
static int check_writable(sipArrayObject *array);
static int check_index(sipArrayObject *array, Py_ssize_t idx);
static void *get_value(sipArrayObject *array, PyObject *value);
static void *get_slice(sipArrayObject *array, PyObject *value, Py_ssize_t len);
static void bad_key(PyObject *key);
static void *element(sipArrayObject *array, Py_ssize_t idx);
static PyObject *make_array(void *data, const sipTypeDef *td,
const char *format, size_t stride, Py_ssize_t len, int flags,
PyObject *owner);
/*
* Implement len() for the type.
*/
static Py_ssize_t sipArray_length(PyObject *self)
{
return ((sipArrayObject *)self)->len;
}
/*
* Implement sequence item sub-script for the type.
*/
static PyObject *sipArray_item(PyObject *self, Py_ssize_t idx)
{
sipArrayObject *array = (sipArrayObject *)self;
PyObject *py_item;
void *data;
if (check_index(array, idx) < 0)
return NULL;
data = element(array, idx);
if (array->td != NULL)
{
py_item = sip_api_convert_from_type(data, array->td, NULL);
}
else
{
switch (*array->format)
{
case 'b':
py_item = PyLong_FromLong(*(char *)data);
break;
case 'B':
py_item = PyLong_FromUnsignedLong(*(unsigned char *)data);
break;
case 'h':
py_item = PyLong_FromLong(*(short *)data);
break;
case 'H':
py_item = PyLong_FromUnsignedLong(*(unsigned short *)data);
break;
case 'i':
py_item = PyLong_FromLong(*(int *)data);
break;
case 'I':
py_item = PyLong_FromUnsignedLong(*(unsigned int *)data);
break;
case 'f':
py_item = PyFloat_FromDouble(*(float *)data);
break;
case 'd':
py_item = PyFloat_FromDouble(*(double *)data);
break;
default:
py_item = NULL;
}
}
return py_item;
}
/* The sequence methods data structure. */
static PySequenceMethods sipArray_SequenceMethods = {
sipArray_length, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
sipArray_item, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
0, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
/*
* Implement mapping sub-script for the type.
*/
static PyObject *sipArray_subscript(PyObject *self, PyObject *key)
{
sipArrayObject *array = (sipArrayObject *)self;
if (PyIndex_Check(key))
{
Py_ssize_t idx = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (idx == -1 && PyErr_Occurred())
return NULL;
if (idx < 0)
idx += array->len;
return sipArray_item(self, idx);
}
if (PySlice_Check(key))
{
Py_ssize_t start, stop, step, slicelength;
if (sip_api_convert_from_slice_object(key, array->len, &start, &stop, &step, &slicelength) < 0)
return NULL;
if (step != 1)
{
PyErr_SetNone(PyExc_NotImplementedError);
return NULL;
}
return make_array(element(array->data, start), array->td,
array->format, array->stride, slicelength,
(array->flags & ~SIP_OWNS_MEMORY), array->owner);
}
bad_key(key);
return NULL;
}
/*
* Implement mapping assignment sub-script for the type.
*/
static int sipArray_ass_subscript(PyObject *self, PyObject *key,
PyObject *value)
{
sipArrayObject *array = (sipArrayObject *)self;
Py_ssize_t start, len;
void *value_data;
if (check_writable(array) < 0)
return -1;
if (PyIndex_Check(key))
{
start = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (start == -1 && PyErr_Occurred())
return -1;
if (start < 0)
start += array->len;
if (check_index(array, start) < 0)
return -1;
if ((value_data = get_value(array, value)) == NULL)
return -1;
len = 1;
}
else if (PySlice_Check(key))
{
Py_ssize_t stop, step;
if (sip_api_convert_from_slice_object(key, array->len, &start, &stop, &step, &len) < 0)
return -1;
if (step != 1)
{
PyErr_SetNone(PyExc_NotImplementedError);
return -1;
}
if ((value_data = get_slice(array, value, len)) == NULL)
return -1;
}
else
{
bad_key(key);
return -1;
}
memmove(element(array, start), value_data, len * array->stride);
return 0;
}
/* The mapping methods data structure. */
static PyMappingMethods sipArray_MappingMethods = {
sipArray_length, /* mp_length */
sipArray_subscript, /* mp_subscript */
sipArray_ass_subscript, /* mp_ass_subscript */
};
/*
* The buffer implementation for Python v2.6.3 and later.
*/
static int sipArray_getbuffer(PyObject *self, Py_buffer *view, int flags)
{
sipArrayObject *array = (sipArrayObject *)self;
if (view == NULL)
return 0;
if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) && (array->flags & SIP_READ_ONLY))
{
PyErr_SetString(PyExc_BufferError, "object is not writable.");
return -1;
}
view->obj = self;
Py_INCREF(self);
view->buf = array->data;
view->len = array->len;
view->readonly = (array->flags & SIP_READ_ONLY);
view->itemsize = array->stride;
view->format = NULL;
if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
view->format = (char *)array->format;
view->ndim = 1;
view->shape = NULL;
if ((flags & PyBUF_ND) == PyBUF_ND)
view->shape = &view->len;
view->strides = NULL;
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES)
view->strides = &view->itemsize;
view->suboffsets = NULL;
view->internal = NULL;
return 0;
}
/* The buffer methods data structure. */
static PyBufferProcs sipArray_BufferProcs = {
sipArray_getbuffer, /* bf_getbuffer */
0 /* bf_releasebuffer */
};
/* The instance deallocation function. */
static void sipArray_dealloc(PyObject *self)
{
sipArrayObject *array = (sipArrayObject *)self;
if (array->flags & SIP_OWNS_MEMORY)
sip_api_free(array->data);
else
Py_XDECREF(array->owner);
}
/* The type data structure. */
PyTypeObject sipArray_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"sip.array", /* tp_name */
sizeof (sipArrayObject), /* tp_basicsize */
0, /* tp_itemsize */
sipArray_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
&sipArray_SequenceMethods, /* tp_as_sequence */
&sipArray_MappingMethods, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
&sipArray_BufferProcs, /* tp_as_buffer */
#if defined(Py_TPFLAGS_HAVE_NEWBUFFER)
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_NEWBUFFER, /* tp_flags */
#else
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
#endif
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* 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 */
0, /* tp_alloc */
0, /* tp_new */
0, /* tp_free */
0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
0, /* tp_del */
0, /* tp_version_tag */
0, /* tp_finalize */
#if PY_VERSION_HEX >= 0x03080000
0, /* tp_vectorcall */
#endif
};
/*
* Check that an array is writable.
*/
static int check_writable(sipArrayObject *array)
{
if (array->flags & SIP_READ_ONLY)
{
PyErr_SetString(PyExc_TypeError, "sip.array object is read-only");
return -1;
}
return 0;
}
/*
* Check that an index is valid for an array.
*/
static int check_index(sipArrayObject *array, Py_ssize_t idx)
{
if (idx >= 0 && idx < array->len)
return 0;
PyErr_SetString(PyExc_IndexError, "index out of bounds");
return -1;
}
/*
* Raise an exception about a bad sub-script key.
*/
static void bad_key(PyObject *key)
{
PyErr_Format(PyExc_TypeError,
"cannot index a sip.array object using '%s'",
Py_TYPE(key)->tp_name);
}
/*
* Get the address of an element of an array.
*/
static void *element(sipArrayObject *array, Py_ssize_t idx)
{
return (unsigned char *)(array->data) + idx * array->stride;
}
/*
* Get the address of a value that will be copied to an array.
*/
static void *get_value(sipArrayObject *array, PyObject *value)
{
static union {
signed char s_char_t;
unsigned char u_char_t;
signed short s_short_t;
unsigned short u_short_t;
signed int s_int_t;
unsigned int u_int_t;
float float_t;
double double_t;
} static_data;
void *data;
if (array->td != NULL)
{
int iserr = FALSE;
data = sip_api_force_convert_to_type(value, array->td, NULL,
SIP_NOT_NONE|SIP_NO_CONVERTORS, NULL, &iserr);
}
else
{
PyErr_Clear();
switch (*array->format)
{
case 'b':
static_data.s_char_t = sip_api_long_as_char(value);
data = &static_data.s_char_t;
break;
case 'B':
static_data.u_char_t = sip_api_long_as_unsigned_char(value);
data = &static_data.u_char_t;
break;
case 'h':
static_data.s_short_t = sip_api_long_as_short(value);
data = &static_data.s_short_t;
break;
case 'H':
static_data.u_short_t = sip_api_long_as_unsigned_short(value);
data = &static_data.u_short_t;
break;
case 'i':
static_data.s_int_t = sip_api_long_as_int(value);
data = &static_data.s_int_t;
break;
case 'I':
static_data.u_int_t = sip_api_long_as_unsigned_int(value);
data = &static_data.u_int_t;
break;
case 'f':
static_data.float_t = (float)PyFloat_AsDouble(value);
data = &static_data.float_t;
break;
case 'd':
static_data.double_t = PyFloat_AsDouble(value);
data = &static_data.double_t;
break;
default:
data = NULL;
}
if (PyErr_Occurred())
data = NULL;
}
return data;
}
/*
* Get the address of an value that will be copied to an array slice.
*/
static void *get_slice(sipArrayObject *array, PyObject *value, Py_ssize_t len)
{
sipArrayObject *other = (sipArrayObject *)value;
if (!PyObject_IsInstance(value, (PyObject *)&sipArray_Type) || array->td != other->td || strcmp(array->format, other->format) != 0)
{
const char *type;
if (array->td != NULL)
{
type = sipTypeName(array->td);
}
else
{
switch (*array->format)
{
case 'b':
type = "char";
break;
case 'B':
type = "unsigned char";
break;
case 'h':
type = "short";
break;
case 'H':
type = "unsigned short";
break;
case 'i':
type = "int";
break;
case 'I':
type = "unsigned int";
break;
case 'f':
type = "float";
break;
case 'd':
type = "double";
break;
default:
type = "";
}
}
PyErr_Format(PyExc_TypeError,
"can only assign another array of %s to the slice", type);
return NULL;
}
if (other->len != len)
{
PyErr_Format(PyExc_TypeError,
"the array being assigned must have length %zd", len);
return NULL;
}
if (other->stride == array->stride)
{
PyErr_Format(PyExc_TypeError,
"the array being assigned must have stride %zu",
array->stride);
return NULL;
}
return other->data;
}
/*
* Do the work of creating an array.
*/
static PyObject *make_array(void *data, const sipTypeDef *td,
const char *format, size_t stride, Py_ssize_t len, int flags,
PyObject *owner)
{
sipArrayObject *array;
if ((array = PyObject_NEW(sipArrayObject, &sipArray_Type)) == NULL)
return NULL;
array->data = data;
array->td = td;
array->format = format;
array->stride = stride;
array->len = len;
array->flags = flags;
if (flags & SIP_OWNS_MEMORY)
{
/* This is a borrowed reference to itself. */
array->owner = (PyObject *)array;
}
else
{
Py_XINCREF(owner);
array->owner = owner;
}
return (PyObject *)array;
}
/*
* Wrap an array of instances of a fundamental type. At the moment format must
* be either "b" (char), "B" (unsigned char), "h" (short), "H" (unsigned
* short), "i" (int), "I" (unsigned int), "f" (float) or "d" (double).
*/
PyObject *sip_api_convert_to_array(void *data, const char *format,
Py_ssize_t len, int flags)
{
size_t stride;
if (data == NULL)
{
Py_INCREF(Py_None);
return Py_None;
}
switch (*format)
{
case 'b':
stride = sizeof (char);
break;
case 'B':
stride = sizeof (unsigned char);
break;
case 'h':
stride = sizeof (short);
break;
case 'H':
stride = sizeof (unsigned short);
break;
case 'i':
stride = sizeof (int);
break;
case 'I':
stride = sizeof (unsigned int);
break;
case 'f':
stride = sizeof (float);
break;
case 'd':
stride = sizeof (double);
break;
default:
stride = 0;
}
assert(stride > 0);
assert(len >= 0);
return make_array(data, NULL, format, stride, len, flags, NULL);
}
/*
* Wrap an array of instances of a defined type.
*/
PyObject *sip_api_convert_to_typed_array(void *data, const sipTypeDef *td,
const char *format, size_t stride, Py_ssize_t len, int flags)
{
if (data == NULL)
{
Py_INCREF(Py_None);
return Py_None;
}
assert(stride > 0);
assert(len >= 0);
return make_array(data, td, format, stride, len, flags, NULL);
}

View File

@@ -1,46 +0,0 @@
/*
* This file defines the API for the array type.
*
* Copyright (c) 2019 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef _ARRAY_H
#define _ARRAY_H
#include <Python.h>
#include "sipint.h"
#ifdef __cplusplus
extern "C" {
#endif
extern PyTypeObject sipArray_Type;
PyObject *sip_api_convert_to_array(void *data, const char *format,
Py_ssize_t len, int flags);
PyObject *sip_api_convert_to_typed_array(void *data, const sipTypeDef *td,
const char *format, size_t stride, Py_ssize_t len, int flags);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,22 +0,0 @@
// This contains all the C++ code that is needed by the sip module.
//
// Copyright (c) 2015 Riverbank Computing Limited <info@riverbankcomputing.com>
//
// This file is part of SIP.
//
// This copy of SIP is licensed for use under the terms of the SIP License
// Agreement. See the file LICENSE for more details.
//
// This copy of SIP may also used under the terms of the GNU General Public
// License v2 or v3 as published by the Free Software Foundation which can be
// found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
//
// SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// Set a C++ bool for the main C implementation of the module.
extern "C" void sipSetBool(void *ptr, int val)
{
*reinterpret_cast<bool *>(ptr) = !!val;
}

View File

@@ -1,478 +0,0 @@
/*
* The implementation of the different descriptors.
*
* Copyright (c) 2019 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <Python.h>
#include "sipint.h"
/*****************************************************************************
* A method descriptor. We don't use the similar Python descriptor because it
* doesn't support a method having static and non-static overloads, and we
* handle mixins via a delegate.
*****************************************************************************/
/* Forward declarations of slots. */
static PyObject *sipMethodDescr_descr_get(PyObject *self, PyObject *obj,
PyObject *type);
static PyObject *sipMethodDescr_repr(PyObject *self);
static int sipMethodDescr_traverse(PyObject *self, visitproc visit, void *arg);
static int sipMethodDescr_clear(PyObject *self);
static void sipMethodDescr_dealloc(PyObject *self);
/*
* The object data structure.
*/
typedef struct _sipMethodDescr {
PyObject_HEAD
/* The method definition. */
PyMethodDef *pmd;
/* The mixin name, if any. */
PyObject *mixin_name;
} sipMethodDescr;
/*
* The type data structure.
*/
PyTypeObject sipMethodDescr_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"sip.methoddescriptor", /* tp_name */
sizeof (sipMethodDescr), /* tp_basicsize */
0, /* tp_itemsize */
sipMethodDescr_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
sipMethodDescr_repr, /* tp_repr */
0, /* tp_as_number */
0, /* 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|Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
sipMethodDescr_traverse,/* tp_traverse */
sipMethodDescr_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
sipMethodDescr_descr_get, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
0, /* tp_free */
0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
0, /* tp_del */
0, /* tp_version_tag */
0, /* tp_finalize */
#if PY_VERSION_HEX >= 0x03080000
0, /* tp_vectorcall */
#endif
};
/*
* Return a new method descriptor for the given method.
*/
PyObject *sipMethodDescr_New(PyMethodDef *pmd)
{
PyObject *descr = PyType_GenericAlloc(&sipMethodDescr_Type, 0);
if (descr != NULL)
{
((sipMethodDescr *)descr)->pmd = pmd;
((sipMethodDescr *)descr)->mixin_name = NULL;
}
return descr;
}
/*
* Return a new method descriptor based on an existing one and a mixin name.
*/
PyObject *sipMethodDescr_Copy(PyObject *orig, PyObject *mixin_name)
{
PyObject *descr = PyType_GenericAlloc(&sipMethodDescr_Type, 0);
if (descr != NULL)
{
((sipMethodDescr *)descr)->pmd = ((sipMethodDescr *)orig)->pmd;
((sipMethodDescr *)descr)->mixin_name = mixin_name;
Py_INCREF(mixin_name);
}
return descr;
}
/*
* The descriptor's descriptor get slot.
*/
static PyObject *sipMethodDescr_descr_get(PyObject *self, PyObject *obj,
PyObject *type)
{
sipMethodDescr *md = (sipMethodDescr *)self;
(void)type;
if (obj == Py_None)
obj = NULL;
else if (md->mixin_name != NULL)
obj = PyObject_GetAttr(obj, md->mixin_name);
return PyCFunction_New(md->pmd, obj);
}
/*
* The descriptor's repr slot. This is for the benefit of cProfile which seems
* to determine attribute names differently to the rest of Python.
*/
static PyObject *sipMethodDescr_repr(PyObject *self)
{
sipMethodDescr *md = (sipMethodDescr *)self;
return PyUnicode_FromFormat("<built-in method %s>", md->pmd->ml_name);
}
/*
* The descriptor's traverse slot.
*/
static int sipMethodDescr_traverse(PyObject *self, visitproc visit, void *arg)
{
if (((sipMethodDescr *)self)->mixin_name != NULL)
{
int vret = visit(((sipMethodDescr *)self)->mixin_name, arg);
if (vret != 0)
return vret;
}
return 0;
}
/*
* The descriptor's clear slot.
*/
static int sipMethodDescr_clear(PyObject *self)
{
PyObject *tmp = ((sipMethodDescr *)self)->mixin_name;
((sipMethodDescr *)self)->mixin_name = NULL;
Py_XDECREF(tmp);
return 0;
}
/*
* The descriptor's dealloc slot.
*/
static void sipMethodDescr_dealloc(PyObject *self)
{
sipMethodDescr_clear(self);
Py_TYPE(self)->tp_free(self);
}
/*****************************************************************************
* A variable descriptor. We don't use the similar Python descriptor because
* it doesn't support static variables.
*****************************************************************************/
/* Forward declarations of slots. */
static PyObject *sipVariableDescr_descr_get(PyObject *self, PyObject *obj,
PyObject *type);
static int sipVariableDescr_descr_set(PyObject *self, PyObject *obj,
PyObject *value);
static int sipVariableDescr_traverse(PyObject *self, visitproc visit,
void *arg);
static int sipVariableDescr_clear(PyObject *self);
static void sipVariableDescr_dealloc(PyObject *self);
/*
* The object data structure.
*/
typedef struct _sipVariableDescr {
PyObject_HEAD
/* The getter/setter definition. */
sipVariableDef *vd;
/* The generated type definition. */
const sipTypeDef *td;
/* The generated container definition. */
const sipContainerDef *cod;
/* The mixin name, if any. */
PyObject *mixin_name;
} sipVariableDescr;
/*
* The type data structure.
*/
PyTypeObject sipVariableDescr_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"sip.variabledescriptor", /* tp_name */
sizeof (sipVariableDescr), /* tp_basicsize */
0, /* tp_itemsize */
sipVariableDescr_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* 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|Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
sipVariableDescr_traverse, /* tp_traverse */
sipVariableDescr_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
sipVariableDescr_descr_get, /* tp_descr_get */
sipVariableDescr_descr_set, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
0, /* tp_free */
0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
0, /* tp_del */
0, /* tp_version_tag */
0, /* tp_finalize */
#if PY_VERSION_HEX >= 0x03080000
0, /* tp_vectorcall */
#endif
};
/* Forward declarations. */
static int get_instance_address(sipVariableDescr *vd, PyObject *obj,
void **addrp);
/*
* Return a new method descriptor for the given getter/setter.
*/
PyObject *sipVariableDescr_New(sipVariableDef *vd, const sipTypeDef *td,
const sipContainerDef *cod)
{
PyObject *descr = PyType_GenericAlloc(&sipVariableDescr_Type, 0);
if (descr != NULL)
{
((sipVariableDescr *)descr)->vd = vd;
((sipVariableDescr *)descr)->td = td;
((sipVariableDescr *)descr)->cod = cod;
((sipVariableDescr *)descr)->mixin_name = NULL;
}
return descr;
}
/*
* Return a new variable descriptor based on an existing one and a mixin name.
*/
PyObject *sipVariableDescr_Copy(PyObject *orig, PyObject *mixin_name)
{
PyObject *descr = PyType_GenericAlloc(&sipVariableDescr_Type, 0);
if (descr != NULL)
{
((sipVariableDescr *)descr)->vd = ((sipVariableDescr *)orig)->vd;
((sipVariableDescr *)descr)->td = ((sipVariableDescr *)orig)->td;
((sipVariableDescr *)descr)->cod = ((sipVariableDescr *)orig)->cod;
((sipVariableDescr *)descr)->mixin_name = mixin_name;
Py_INCREF(mixin_name);
}
return descr;
}
/*
* The descriptor's descriptor get slot.
*/
static PyObject *sipVariableDescr_descr_get(PyObject *self, PyObject *obj,
PyObject *type)
{
sipVariableDescr *vd = (sipVariableDescr *)self;
void *addr;
if (get_instance_address(vd, obj, &addr) < 0)
return NULL;
return ((sipVariableGetterFunc)vd->vd->vd_getter)(addr, obj, type);
}
/*
* The descriptor's descriptor set slot.
*/
static int sipVariableDescr_descr_set(PyObject *self, PyObject *obj,
PyObject *value)
{
sipVariableDescr *vd = (sipVariableDescr *)self;
void *addr;
/* Check that the value isn't const. */
if (vd->vd->vd_setter == NULL)
{
PyErr_Format(PyExc_AttributeError,
"'%s' object attribute '%s' is read-only",
sipPyNameOfContainer(vd->cod, vd->td), vd->vd->vd_name);
return -1;
}
if (get_instance_address(vd, obj, &addr) < 0)
return -1;
return ((sipVariableSetterFunc)vd->vd->vd_setter)(addr, value, obj);
}
/*
* Return the C/C++ address of any instance.
*/
static int get_instance_address(sipVariableDescr *vd, PyObject *obj,
void **addrp)
{
void *addr;
if (vd->vd->vd_type == ClassVariable)
{
addr = NULL;
}
else
{
/* Check that access was via an instance. */
if (obj == NULL || obj == Py_None)
{
PyErr_Format(PyExc_AttributeError,
"'%s' object attribute '%s' is an instance attribute",
sipPyNameOfContainer(vd->cod, vd->td), vd->vd->vd_name);
return -1;
}
if (vd->mixin_name != NULL)
obj = PyObject_GetAttr(obj, vd->mixin_name);
/* Get the C++ instance. */
if ((addr = sip_api_get_cpp_ptr((sipSimpleWrapper *)obj, vd->td)) == NULL)
return -1;
}
*addrp = addr;
return 0;
}
/*
* The descriptor's traverse slot.
*/
static int sipVariableDescr_traverse(PyObject *self, visitproc visit, void *arg)
{
if (((sipVariableDescr *)self)->mixin_name != NULL)
{
int vret = visit(((sipVariableDescr *)self)->mixin_name, arg);
if (vret != 0)
return vret;
}
return 0;
}
/*
* The descriptor's clear slot.
*/
static int sipVariableDescr_clear(PyObject *self)
{
PyObject *tmp = ((sipVariableDescr *)self)->mixin_name;
((sipVariableDescr *)self)->mixin_name = NULL;
Py_XDECREF(tmp);
return 0;
}
/*
* The descriptor's dealloc slot.
*/
static void sipVariableDescr_dealloc(PyObject *self)
{
sipVariableDescr_clear(self);
Py_TYPE(self)->tp_free(self);
}

View File

@@ -1,358 +0,0 @@
/*
* The implementation of the Python object to C/C++ integer converters.
*
* Copyright (c) 2019 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
/*
* NOTES
*
* The legacy integer conversions (ie. without support for overflow checking)
* are flawed and inconsistent. Large Python signed values were converted to
* -1 whereas small values were truncated. When converting function arguments
* all overlows were ignored, however when converting the results returned by
* Python re-implementations then large Python values raised an exception
* whereas small values were truncated.
*
* With the new integer conversions large Python signed values will always
* raise an overflow exception (even if overflow checking is disabled). This
* is because a truncated value is not available - it would have to be
* computed. This may cause new exceptions to be raised but is justified in
* that the value that was being used bore no relation to the original value.
*/
#include <Python.h>
#include <limits.h>
#include "sipint.h"
/* Wrappers to deal with lack of long long support. */
#if defined(HAVE_LONG_LONG)
#define SIPLong_AsLongLong PyLong_AsLongLong
#define SIP_LONG_LONG PY_LONG_LONG
#define SIP_LONG_LONG_FORMAT "%lld"
#define SIP_UNSIGNED_LONG_LONG_FORMAT "%llu"
#else
#define SIPLong_AsLongLong PyLong_AsLong
#define SIP_LONG_LONG long
#define SIP_LONG_LONG_FORMAT "%ld"
#define SIP_UNSIGNED_LONG_LONG_FORMAT "%lu"
#endif
static int overflow_checking = FALSE; /* Check for overflows. */
static SIP_LONG_LONG long_as_long_long(PyObject *o, SIP_LONG_LONG min,
SIP_LONG_LONG max);
static unsigned long long_as_unsigned_long(PyObject *o, unsigned long max);
static void raise_signed_overflow(SIP_LONG_LONG min, SIP_LONG_LONG max);
static void raise_unsigned_overflow(unsigned SIP_LONG_LONG max);
/*
* Enable or disable overflow checking (Python API).
*/
PyObject *sipEnableOverflowChecking(PyObject *self, PyObject *args)
{
int enable;
(void)self;
if (PyArg_ParseTuple(args, "i:enableoverflowchecking", &enable))
{
PyObject *res;
res = (sip_api_enable_overflow_checking(enable) ? Py_True : Py_False);
Py_INCREF(res);
return res;
}
return NULL;
}
/*
* Enable or disable overflow checking (C API).
*/
int sip_api_enable_overflow_checking(int enable)
{
int was_enabled = overflow_checking;
overflow_checking = enable;
return was_enabled;
}
/*
* Convert a Python object to a C++ bool (returned as an int).
*/
int sip_api_convert_to_bool(PyObject *o)
{
int was_enabled, v;
/* Convert the object to an int while checking for overflow. */
was_enabled = sip_api_enable_overflow_checking(TRUE);
v = sip_api_long_as_int(o);
sip_api_enable_overflow_checking(was_enabled);
if (PyErr_Occurred())
{
if (PyErr_ExceptionMatches(PyExc_OverflowError))
{
PyErr_Clear();
/* The value must have been non-zero. */
v = 1;
}
else
{
PyErr_Format(PyExc_TypeError, "a 'bool' is expected not '%s'",
Py_TYPE(o)->tp_name);
v = -1;
}
}
else if (v != 0)
{
v = 1;
}
return v;
}
/*
* Convert a Python object to a C char.
*/
char sip_api_long_as_char(PyObject *o)
{
return (char)long_as_long_long(o, CHAR_MIN, CHAR_MAX);
}
/*
* Convert a Python object to a C signed char.
*/
signed char sip_api_long_as_signed_char(PyObject *o)
{
return (signed char)long_as_long_long(o, SCHAR_MIN, SCHAR_MAX);
}
/*
* Convert a Python object to a C unsigned char.
*/
unsigned char sip_api_long_as_unsigned_char(PyObject *o)
{
return (unsigned char)long_as_unsigned_long(o, UCHAR_MAX);
}
/*
* Convert a Python object to a C short.
*/
short sip_api_long_as_short(PyObject *o)
{
return (short)long_as_long_long(o, SHRT_MIN, SHRT_MAX);
}
/*
* Convert a Python object to a C unsigned short.
*/
unsigned short sip_api_long_as_unsigned_short(PyObject *o)
{
return (unsigned short)long_as_unsigned_long(o, USHRT_MAX);
}
/*
* Convert a Python object to a C int.
*/
int sip_api_long_as_int(PyObject *o)
{
return (int)long_as_long_long(o, INT_MIN, INT_MAX);
}
/*
* Convert a Python object to a C unsigned int.
*/
unsigned sip_api_long_as_unsigned_int(PyObject *o)
{
return (unsigned)long_as_unsigned_long(o, UINT_MAX);
}
/*
* Convert a Python object to a C size_t.
*/
size_t sip_api_long_as_size_t(PyObject *o)
{
return (size_t)long_as_unsigned_long(o, SIZE_MAX);
}
/*
* Convert a Python object to a C long.
*/
long sip_api_long_as_long(PyObject *o)
{
return (long)long_as_long_long(o, LONG_MIN, LONG_MAX);
}
/*
* Convert a Python object to a C unsigned long.
*/
unsigned long sip_api_long_as_unsigned_long(PyObject *o)
{
return long_as_unsigned_long(o, ULONG_MAX);
}
#if defined(HAVE_LONG_LONG)
/*
* Convert a Python object to a C long long.
*/
PY_LONG_LONG sip_api_long_as_long_long(PyObject *o)
{
return long_as_long_long(o, LLONG_MIN, LLONG_MAX);
}
/*
* Convert a Python object to a C unsigned long long.
*/
unsigned PY_LONG_LONG sip_api_long_as_unsigned_long_long(PyObject *o)
{
unsigned PY_LONG_LONG value;
/*
* Note that this doesn't handle Python v2 int objects, but the old
* converters didn't either.
*/
PyErr_Clear();
if (overflow_checking)
{
value = PyLong_AsUnsignedLongLong(o);
if (PyErr_Occurred())
{
/* Provide a better exception message. */
if (PyErr_ExceptionMatches(PyExc_OverflowError))
raise_unsigned_overflow(ULLONG_MAX);
}
}
else
{
value = PyLong_AsUnsignedLongLongMask(o);
}
return value;
}
#endif
/*
* Convert a Python object to a long long checking that the value is within a
* range if overflow checking is enabled.
*/
static SIP_LONG_LONG long_as_long_long(PyObject *o, SIP_LONG_LONG min,
SIP_LONG_LONG max)
{
SIP_LONG_LONG value;
PyErr_Clear();
value = SIPLong_AsLongLong(o);
if (PyErr_Occurred())
{
/* Provide a better exception message. */
if (PyErr_ExceptionMatches(PyExc_OverflowError))
raise_signed_overflow(min, max);
}
else if (overflow_checking && (value < min || value > max))
{
raise_signed_overflow(min, max);
}
return value;
}
/*
* Convert a Python object to an unsigned long checking that the value is
* within a range if overflow checking is enabled.
*/
static unsigned long long_as_unsigned_long(PyObject *o, unsigned long max)
{
unsigned long value;
PyErr_Clear();
if (overflow_checking)
{
value = PyLong_AsUnsignedLong(o);
if (PyErr_Occurred())
{
/* Provide a better exception message. */
if (PyErr_ExceptionMatches(PyExc_OverflowError))
raise_unsigned_overflow(max);
}
else if (value > max)
{
raise_unsigned_overflow(max);
}
}
else
{
value = PyLong_AsUnsignedLongMask(o);
}
return value;
}
/*
* Raise an overflow exception if a signed value is out of range.
*/
static void raise_signed_overflow(SIP_LONG_LONG min, SIP_LONG_LONG max)
{
PyErr_Format(PyExc_OverflowError,
"value must be in the range " SIP_LONG_LONG_FORMAT " to " SIP_LONG_LONG_FORMAT,
min, max);
}
/*
* Raise an overflow exception if an unsigned value is out of range.
*/
static void raise_unsigned_overflow(unsigned SIP_LONG_LONG max)
{
PyErr_Format(PyExc_OverflowError,
"value must be in the range 0 to " SIP_UNSIGNED_LONG_LONG_FORMAT,
max);
}

View File

@@ -1,489 +0,0 @@
/*
* This module implements a hash table class for mapping C/C++ addresses to the
* corresponding wrapped Python object.
*
* Copyright (c) 2017 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <string.h>
#include "sipint.h"
#define hash_1(k,s) (((unsigned long)(k)) % (s))
#define hash_2(k,s) ((s) - 2 - (hash_1((k),(s)) % ((s) - 2)))
/* Prime numbers to use as hash table sizes. */
static unsigned long hash_primes[] = {
521, 1031, 2053, 4099,
8209, 16411, 32771, 65537, 131101, 262147,
524309, 1048583, 2097169, 4194319, 8388617, 16777259,
33554467, 67108879, 134217757, 268435459, 536870923, 1073741827,
2147483659U,0
};
static sipHashEntry *newHashTable(unsigned long);
static sipHashEntry *findHashEntry(sipObjectMap *,void *);
static void add_object(sipObjectMap *om, void *addr, sipSimpleWrapper *val);
static void add_aliases(sipObjectMap *om, void *addr, sipSimpleWrapper *val,
const sipClassTypeDef *base_ctd, const sipClassTypeDef *ctd);
static int remove_object(sipObjectMap *om, void *addr, sipSimpleWrapper *val);
static void remove_aliases(sipObjectMap *om, void *addr, sipSimpleWrapper *val,
const sipClassTypeDef *base_ctd, const sipClassTypeDef *ctd);
static void reorganiseMap(sipObjectMap *om);
static void *getUnguardedPointer(sipSimpleWrapper *w);
/*
* Initialise an object map.
*/
void sipOMInit(sipObjectMap *om)
{
om -> primeIdx = 0;
om -> unused = om -> size = hash_primes[om -> primeIdx];
om -> stale = 0;
om -> hash_array = newHashTable(om -> size);
}
/*
* Finalise an object map.
*/
void sipOMFinalise(sipObjectMap *om)
{
sip_api_free(om -> hash_array);
}
/*
* Allocate and initialise a new hash table.
*/
static sipHashEntry *newHashTable(unsigned long size)
{
size_t nbytes;
sipHashEntry *hashtab;
nbytes = sizeof (sipHashEntry) * size;
if ((hashtab = (sipHashEntry *)sip_api_malloc(nbytes)) != NULL)
memset(hashtab,0,nbytes);
return hashtab;
}
/*
* Return a pointer to the hash entry that is used, or should be used, for the
* given C/C++ address.
*/
static sipHashEntry *findHashEntry(sipObjectMap *om,void *key)
{
unsigned long hash, inc;
void *hek;
hash = hash_1(key,om -> size);
inc = hash_2(key,om -> size);
while ((hek = om -> hash_array[hash].key) != NULL && hek != key)
hash = (hash + inc) % om -> size;
return &om -> hash_array[hash];
}
/*
* Return the wrapped Python object of a specific type for a C/C++ address or
* NULL if it wasn't found.
*/
sipSimpleWrapper *sipOMFindObject(sipObjectMap *om, void *key,
const sipTypeDef *td)
{
sipHashEntry *he = findHashEntry(om, key);
sipSimpleWrapper *sw;
PyTypeObject *py_type = sipTypeAsPyTypeObject(td);
/* Go through each wrapped object at this address. */
for (sw = he->first; sw != NULL; sw = sw->next)
{
sipSimpleWrapper *unaliased;
unaliased = (sipIsAlias(sw) ? (sipSimpleWrapper *)sw->data : sw);
/*
* If the reference count is 0 then it is in the process of being
* deleted, so ignore it. It's not completely clear how this can
* happen (but it can) because it implies that the garbage collection
* code is being re-entered (and there are guards in place to prevent
* this).
*/
if (Py_REFCNT(unaliased) == 0)
continue;
/* Ignore it if the C/C++ address is no longer valid. */
if (sip_api_get_address(unaliased) == NULL)
continue;
/*
* If this wrapped object is of the given type, or a sub-type of it,
* then we assume it is the same C++ object.
*/
if (PyObject_TypeCheck(unaliased, py_type))
return unaliased;
}
return NULL;
}
/*
* Add a C/C++ address and the corresponding wrapped Python object to the map.
*/
void sipOMAddObject(sipObjectMap *om, sipSimpleWrapper *val)
{
void *addr = getUnguardedPointer(val);
const sipClassTypeDef *base_ctd;
/* Add the object. */
add_object(om, addr, val);
/* Add any aliases. */
base_ctd = (const sipClassTypeDef *)((sipWrapperType *)Py_TYPE(val))->wt_td;
add_aliases(om, addr, val, base_ctd, base_ctd);
}
/*
* Add an alias for any address that is different when cast to a super-type.
*/
static void add_aliases(sipObjectMap *om, void *addr, sipSimpleWrapper *val,
const sipClassTypeDef *base_ctd, const sipClassTypeDef *ctd)
{
const sipEncodedTypeDef *sup;
/* See if there are any super-classes. */
if ((sup = ctd->ctd_supers) != NULL)
{
sipClassTypeDef *sup_ctd = sipGetGeneratedClassType(sup, ctd);
/* Recurse up the hierarchy for the first super-class. */
add_aliases(om, addr, val, base_ctd, sup_ctd);
/*
* We only check for aliases for subsequent super-classes because the
* first one can never need one.
*/
while (!sup++->sc_flag)
{
void *sup_addr;
sup_ctd = sipGetGeneratedClassType(sup, ctd);
/* Recurse up the hierarchy for the remaining super-classes. */
add_aliases(om, addr, val, base_ctd, sup_ctd);
sup_addr = (*base_ctd->ctd_cast)(addr, (sipTypeDef *)sup_ctd);
if (sup_addr != addr)
{
sipSimpleWrapper *alias;
/* Note that we silently ignore errors. */
if ((alias = sip_api_malloc(sizeof (sipSimpleWrapper))) != NULL)
{
/*
* An alias is basically a bit-wise copy of the Python
* object but only to ensure the fields we are subverting
* are in the right place. An alias should never be passed
* to the Python API.
*/
*alias = *val;
alias->sw_flags = (val->sw_flags & SIP_SHARE_MAP) | SIP_ALIAS;
alias->data = val;
alias->next = NULL;
add_object(om, sup_addr, alias);
}
}
}
}
}
/*
* Add a wrapper (which may be an alias) to the map.
*/
static void add_object(sipObjectMap *om, void *addr, sipSimpleWrapper *val)
{
sipHashEntry *he = findHashEntry(om, addr);
/*
* If the bucket is in use then we appear to have several objects at the
* same address.
*/
if (he->first != NULL)
{
/*
* This can happen for three reasons. A variable of one class can be
* declared at the start of another class. Therefore there are two
* objects, of different classes, with the same address. The second
* reason is that the old C/C++ object has been deleted by C/C++ but we
* didn't get to find out for some reason, and a new C/C++ instance has
* been created at the same address. The third reason is if we are in
* the process of deleting a Python object but the C++ object gets
* wrapped again because the C++ dtor called a method that has been
* re-implemented in Python. The absence of the SIP_SHARE_MAP flag
* tells us that a new C++ instance has just been created and so we
* know the second reason is the correct one so we mark the old
* pointers as invalid and reuse the entry. Otherwise we just add this
* one to the existing list of objects at this address.
*/
if (!(val->sw_flags & SIP_SHARE_MAP))
{
sipSimpleWrapper *sw = he->first;
he->first = NULL;
while (sw != NULL)
{
sipSimpleWrapper *next = sw->next;
if (sipIsAlias(sw))
{
sip_api_free(sw);
}
else
{
/*
* We are removing it from the map here. However, note
* that we first have to call the destructor before marking
* it as not being in the map, as the destructor itself
* might end up trying to remove the wrapper and its
* aliases from the map. In that case, if the wrapper is
* already marked as not in the map, the removal will just
* return early, leaving any potential aliases as stale
* entries in the map. If we later try to wrap a different
* object at the same address, we end up retrieving the
* stale alias entry from the object map, triggering a
* use-after-free when accessing its C++ object.
*/
sip_api_instance_destroyed(sw);
sipSetNotInMap(sw);
}
sw = next;
}
}
val->next = he->first;
he->first = val;
return;
}
/* See if the bucket was unused or stale. */
if (he->key == NULL)
{
he->key = addr;
om->unused--;
}
else
{
om->stale--;
}
/* Add the rest of the new value. */
he->first = val;
val->next = NULL;
reorganiseMap(om);
}
/*
* Reorganise a map if it is running short of space.
*/
static void reorganiseMap(sipObjectMap *om)
{
unsigned long old_size, i;
sipHashEntry *ohe, *old_tab;
/* Don't bother if it still has more than 12% available. */
if (om -> unused > om -> size >> 3)
return;
/*
* If reorganising (ie. making the stale buckets unused) using the same
* sized table would make 25% available then do that. Otherwise use a
* bigger table (if possible).
*/
if (om -> unused + om -> stale < om -> size >> 2 && hash_primes[om -> primeIdx + 1] != 0)
om -> primeIdx++;
old_size = om -> size;
old_tab = om -> hash_array;
om -> unused = om -> size = hash_primes[om -> primeIdx];
om -> stale = 0;
om -> hash_array = newHashTable(om -> size);
/* Transfer the entries from the old table to the new one. */
ohe = old_tab;
for (i = 0; i < old_size; ++i)
{
if (ohe -> key != NULL && ohe -> first != NULL)
{
*findHashEntry(om,ohe -> key) = *ohe;
om -> unused--;
}
++ohe;
}
sip_api_free(old_tab);
}
/*
* Remove a C/C++ object from the table. Return 0 if it was removed
* successfully.
*/
int sipOMRemoveObject(sipObjectMap *om, sipSimpleWrapper *val)
{
void *addr;
const sipClassTypeDef *base_ctd;
/* Handle the trivial case. */
if (sipNotInMap(val))
return 0;
if ((addr = getUnguardedPointer(val)) == NULL)
return 0;
/* Remove any aliases. */
base_ctd = (const sipClassTypeDef *)((sipWrapperType *)Py_TYPE(val))->wt_td;
remove_aliases(om, addr, val, base_ctd, base_ctd);
/* Remove the object. */
return remove_object(om, addr, val);
}
/*
* Remove an alias for any address that is different when cast to a super-type.
*/
static void remove_aliases(sipObjectMap *om, void *addr, sipSimpleWrapper *val,
const sipClassTypeDef *base_ctd, const sipClassTypeDef *ctd)
{
const sipEncodedTypeDef *sup;
/* See if there are any super-classes. */
if ((sup = ctd->ctd_supers) != NULL)
{
sipClassTypeDef *sup_ctd = sipGetGeneratedClassType(sup, ctd);
/* Recurse up the hierarchy for the first super-class. */
remove_aliases(om, addr, val, base_ctd, sup_ctd);
/*
* We only check for aliases for subsequent super-classes because the
* first one can never need one.
*/
while (!sup++->sc_flag)
{
void *sup_addr;
sup_ctd = sipGetGeneratedClassType(sup, ctd);
/* Recurse up the hierarchy for the remaining super-classes. */
remove_aliases(om, addr, val, base_ctd, sup_ctd);
sup_addr = (*base_ctd->ctd_cast)(addr, (sipTypeDef *)sup_ctd);
if (sup_addr != addr)
remove_object(om, sup_addr, val);
}
}
}
/*
* Remove a wrapper from the map.
*/
static int remove_object(sipObjectMap *om, void *addr, sipSimpleWrapper *val)
{
sipHashEntry *he = findHashEntry(om, addr);
sipSimpleWrapper **swp;
for (swp = &he->first; *swp != NULL; swp = &(*swp)->next)
{
sipSimpleWrapper *sw, *next;
int do_remove;
sw = *swp;
next = sw->next;
if (sipIsAlias(sw))
{
if (sw->data == val)
{
sip_api_free(sw);
do_remove = TRUE;
}
else
{
do_remove = FALSE;
}
}
else
{
do_remove = (sw == val);
}
if (do_remove)
{
*swp = next;
/*
* If the bucket is now empty then count it as stale. Note that we
* do not NULL the key and count it as unused because that might
* throw out the search for another entry that wanted to go here,
* found it already occupied, and was put somewhere else. In other
* words, searches must be repeatable until we reorganise the
* table.
*/
if (he->first == NULL)
om->stale++;
return 0;
}
}
return -1;
}
/*
* Return the unguarded pointer to a C/C++ instance, ie. the pointer was valid
* but may longer be.
*/
static void *getUnguardedPointer(sipSimpleWrapper *w)
{
return (w->access_func != NULL) ? w->access_func(w, UnguardedPointer) : w->data;
}

View File

@@ -1,686 +0,0 @@
/*
* The SIP library code that implements the interface to the optional module
* supplied Qt support.
*
* Copyright (c) 2020 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <Python.h>
#include <assert.h>
#include <string.h>
#include "sipint.h"
/* This is how Qt "types" signals and slots. */
#define isQtSlot(s) (*(s) == '1')
#define isQtSignal(s) (*(s) == '2')
static PyObject *getWeakRef(PyObject *obj);
static char *sipStrdup(const char *);
static void *createUniversalSlot(sipWrapper *txSelf, const char *sig,
PyObject *rxObj, const char *slot, const char **member, int flags);
static void *findSignal(void *txrx, const char **sig);
static void *newSignal(void *txrx, const char **sig);
/*
* Find an existing signal.
*/
static void *findSignal(void *txrx, const char **sig)
{
if (sipQtSupport->qt_find_universal_signal != NULL)
txrx = sipQtSupport->qt_find_universal_signal(txrx, sig);
return txrx;
}
/*
* Return a usable signal, creating a new universal signal if needed.
*/
static void *newSignal(void *txrx, const char **sig)
{
void *new_txrx = findSignal(txrx, sig);
if (new_txrx == NULL && sipQtSupport->qt_create_universal_signal != NULL)
new_txrx = sipQtSupport->qt_create_universal_signal(txrx, sig);
return new_txrx;
}
/*
* Create a universal slot. Returns a pointer to it or 0 if there was an
* error.
*/
static void *createUniversalSlot(sipWrapper *txSelf, const char *sig,
PyObject *rxObj, const char *slot, const char **member, int flags)
{
void *us;
assert(sipQtSupport->qt_create_universal_slot);
us = sipQtSupport->qt_create_universal_slot(txSelf, sig, rxObj, slot,
member, flags);
if (us && txSelf)
sipSetPossibleProxy((sipSimpleWrapper *)txSelf);
return us;
}
/*
* Invoke a single slot (Qt or Python) and return the result. Don't check if
* any receiver C++ object still exists.
*/
PyObject *sip_api_invoke_slot(const sipSlot *slot, PyObject *sigargs)
{
return sip_api_invoke_slot_ex(slot, sigargs, TRUE);
}
/*
* Invoke a single slot (Qt or Python) and return the result. Optionally check
* that any receiver C++ object still exist.
*/
PyObject *sip_api_invoke_slot_ex(const sipSlot *slot, PyObject *sigargs,
int no_receiver_check)
{
PyObject *sa, *oxtype, *oxvalue, *oxtb, *sfunc, *sref;
assert(sipQtSupport);
/* Keep some compilers quiet. */
oxtype = oxvalue = oxtb = NULL;
/* Fan out Qt signals. (Only PyQt3 would do this.) */
if (slot->name != NULL && slot->name[0] != '\0')
{
assert(sipQtSupport->qt_emit_signal);
if (sipQtSupport->qt_emit_signal(slot->pyobj, slot->name, sigargs) < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
/* Get the object to call, resolving any weak references. */
if (slot->weakSlot == Py_True)
{
/*
* The slot is guaranteed to be Ok because it has an extra reference or
* is None.
*/
sref = slot->pyobj;
Py_INCREF(sref);
}
else if (slot -> weakSlot == NULL)
sref = NULL;
else if ((sref = PyWeakref_GetObject(slot -> weakSlot)) == NULL)
return NULL;
else
Py_INCREF(sref);
if (sref == Py_None)
{
/*
* If the real object has gone then we pretend everything is Ok. This
* mimics the Qt behaviour of not caring if a receiving object has been
* deleted.
*/
Py_DECREF(sref);
Py_INCREF(Py_None);
return Py_None;
}
if (slot -> pyobj == NULL)
{
PyObject *self = (sref != NULL ? sref : slot->meth.mself);
/*
* If the receiver wraps a C++ object then ignore the call if it no
* longer exists.
*/
if (!no_receiver_check &&
PyObject_TypeCheck(self, (PyTypeObject *)&sipSimpleWrapper_Type) &&
sip_api_get_address((sipSimpleWrapper *)self) == NULL)
{
Py_XDECREF(sref);
Py_INCREF(Py_None);
return Py_None;
}
if ((sfunc = PyMethod_New(slot->meth.mfunc, self)) == NULL)
{
Py_XDECREF(sref);
return NULL;
}
}
else if (slot -> name != NULL)
{
char *mname = slot -> name + 1;
PyObject *self = (sref != NULL ? sref : slot->pyobj);
if ((sfunc = PyObject_GetAttrString(self, mname)) == NULL || !PyCFunction_Check(sfunc))
{
/*
* Note that in earlier versions of SIP this error would be
* detected when the slot was connected.
*/
PyErr_Format(PyExc_NameError,"Invalid slot %s",mname);
Py_XDECREF(sfunc);
Py_XDECREF(sref);
return NULL;
}
}
else
{
sfunc = slot->pyobj;
Py_INCREF(sfunc);
}
/*
* We make repeated attempts to call a slot. If we work out that it failed
* because of an immediate type error we try again with one less argument.
* We keep going until we run out of arguments to drop. This emulates the
* Qt ability of the slot to accept fewer arguments than a signal provides.
*/
sa = sigargs;
Py_INCREF(sa);
for (;;)
{
PyObject *nsa, *xtype, *xvalue, *xtb, *resobj;
if ((resobj = PyObject_CallObject(sfunc, sa)) != NULL)
{
Py_DECREF(sfunc);
Py_XDECREF(sref);
/* Remove any previous exception. */
if (sa != sigargs)
{
Py_XDECREF(oxtype);
Py_XDECREF(oxvalue);
Py_XDECREF(oxtb);
PyErr_Clear();
}
Py_DECREF(sa);
return resobj;
}
/* Get the exception. */
PyErr_Fetch(&xtype,&xvalue,&xtb);
/*
* See if it is unacceptable. An acceptable failure is a type error
* with no traceback - so long as we can still reduce the number of
* arguments and try again.
*/
if (!PyErr_GivenExceptionMatches(xtype,PyExc_TypeError) ||
xtb != NULL ||
PyTuple_GET_SIZE(sa) == 0)
{
/*
* If there is a traceback then we must have called the slot and
* the exception was later on - so report the exception as is.
*/
if (xtb != NULL)
{
if (sa != sigargs)
{
Py_XDECREF(oxtype);
Py_XDECREF(oxvalue);
Py_XDECREF(oxtb);
}
PyErr_Restore(xtype,xvalue,xtb);
}
else if (sa == sigargs)
PyErr_Restore(xtype,xvalue,xtb);
else
{
/*
* Discard the latest exception and restore the original one.
*/
Py_XDECREF(xtype);
Py_XDECREF(xvalue);
Py_XDECREF(xtb);
PyErr_Restore(oxtype,oxvalue,oxtb);
}
break;
}
/* If this is the first attempt, save the exception. */
if (sa == sigargs)
{
oxtype = xtype;
oxvalue = xvalue;
oxtb = xtb;
}
else
{
Py_XDECREF(xtype);
Py_XDECREF(xvalue);
Py_XDECREF(xtb);
}
/* Create the new argument tuple. */
if ((nsa = PyTuple_GetSlice(sa,0,PyTuple_GET_SIZE(sa) - 1)) == NULL)
{
/* Tidy up. */
Py_XDECREF(oxtype);
Py_XDECREF(oxvalue);
Py_XDECREF(oxtb);
break;
}
Py_DECREF(sa);
sa = nsa;
}
Py_DECREF(sfunc);
Py_XDECREF(sref);
Py_DECREF(sa);
return NULL;
}
/*
* Compare two slots to see if they are the same.
*/
int sip_api_same_slot(const sipSlot *sp, PyObject *rxObj, const char *slot)
{
assert(sipQtSupport);
assert(sipQtSupport->qt_same_name);
/* See if they are signals or Qt slots, ie. they have a name. */
if (slot != NULL)
{
if (sp->name == NULL || sp->name[0] == '\0')
return 0;
return (sipQtSupport->qt_same_name(sp->name, slot) && sp->pyobj == rxObj);
}
/* See if they are pure Python methods. */
if (PyMethod_Check(rxObj))
{
if (sp->pyobj != NULL)
return 0;
return (sp->meth.mfunc == PyMethod_GET_FUNCTION(rxObj)
&& sp->meth.mself == PyMethod_GET_SELF(rxObj));
}
/* See if they are wrapped C++ methods. */
if (PyCFunction_Check(rxObj))
{
if (sp->name == NULL || sp->name[0] != '\0')
return 0;
return (sp->pyobj == PyCFunction_GET_SELF(rxObj) &&
strcmp(&sp->name[1], ((PyCFunctionObject *)rxObj)->m_ml->ml_name) == 0);
}
/* The objects must be the same. */
return (sp->pyobj == rxObj);
}
/*
* Convert a valid Python signal or slot to an existing universal slot.
*/
void *sipGetRx(sipSimpleWrapper *txSelf, const char *sigargs, PyObject *rxObj,
const char *slot, const char **memberp)
{
assert(sipQtSupport);
assert(sipQtSupport->qt_find_slot);
if (slot != NULL)
if (isQtSlot(slot) || isQtSignal(slot))
{
void *rx;
*memberp = slot;
if ((rx = sip_api_get_cpp_ptr((sipSimpleWrapper *)rxObj, sipQObjectType)) == NULL)
return NULL;
if (isQtSignal(slot))
rx = findSignal(rx, memberp);
return rx;
}
/*
* The slot was either a Python callable or PyQt3 Python signal so there
* should be a universal slot.
*/
return sipQtSupport->qt_find_slot(sip_api_get_address(txSelf), sigargs, rxObj, slot, memberp);
}
/*
* Convert a Python receiver (either a Python signal or slot or a Qt signal or
* slot) to a Qt receiver. It is only ever called when the signal is a Qt
* signal. Return NULL is there was an error.
*/
void *sip_api_convert_rx(sipWrapper *txSelf, const char *sigargs,
PyObject *rxObj, const char *slot, const char **memberp, int flags)
{
assert(sipQtSupport);
if (slot == NULL)
return createUniversalSlot(txSelf, sigargs, rxObj, NULL, memberp, flags);
if (isQtSlot(slot) || isQtSignal(slot))
{
void *rx;
*memberp = slot;
if ((rx = sip_api_get_cpp_ptr((sipSimpleWrapper *)rxObj, sipQObjectType)) == NULL)
return NULL;
if (isQtSignal(slot))
rx = newSignal(rx, memberp);
return rx;
}
/* The slot is a Python signal so we need a universal slot to catch it. */
return createUniversalSlot(txSelf, sigargs, rxObj, slot, memberp, 0);
}
/*
* Connect a Qt signal or a Python signal to a Qt slot, a Qt signal, a Python
* slot or a Python signal. This is all possible combinations.
*/
PyObject *sip_api_connect_rx(PyObject *txObj, const char *sig, PyObject *rxObj,
const char *slot, int type)
{
assert(sipQtSupport);
assert(sipQtSupport->qt_connect);
/* Handle Qt signals. */
if (isQtSignal(sig))
{
void *tx, *rx;
const char *member, *real_sig;
int res;
if ((tx = sip_api_get_cpp_ptr((sipSimpleWrapper *)txObj, sipQObjectType)) == NULL)
return NULL;
real_sig = sig;
if ((tx = newSignal(tx, &real_sig)) == NULL)
return NULL;
if ((rx = sip_api_convert_rx((sipWrapper *)txObj, sig, rxObj, slot, &member, 0)) == NULL)
return NULL;
res = sipQtSupport->qt_connect(tx, real_sig, rx, member, type);
return PyBool_FromLong(res);
}
/* Handle Python signals. Only PyQt3 would get this far. */
assert(sipQtSupport->qt_connect_py_signal);
if (sipQtSupport->qt_connect_py_signal(txObj, sig, rxObj, slot) < 0)
return NULL;
Py_INCREF(Py_True);
return Py_True;
}
/*
* Disconnect a signal to a signal or a Qt slot.
*/
PyObject *sip_api_disconnect_rx(PyObject *txObj,const char *sig,
PyObject *rxObj,const char *slot)
{
assert(sipQtSupport);
assert(sipQtSupport->qt_disconnect);
assert(sipQtSupport->qt_destroy_universal_slot);
/* Handle Qt signals. */
if (isQtSignal(sig))
{
sipSimpleWrapper *txSelf = (sipSimpleWrapper *)txObj;
void *tx, *rx;
const char *member;
int res;
if ((tx = sip_api_get_cpp_ptr(txSelf, sipQObjectType)) == NULL)
return NULL;
if ((rx = sipGetRx(txSelf, sig, rxObj, slot, &member)) == NULL)
{
Py_INCREF(Py_False);
return Py_False;
}
/* Handle Python signals. */
tx = findSignal(tx, &sig);
res = sipQtSupport->qt_disconnect(tx, sig, rx, member);
/*
* Delete it if it is a universal slot as this will be it's only
* connection. If the slot is actually a universal signal then it
* should leave it in place.
*/
sipQtSupport->qt_destroy_universal_slot(rx);
return PyBool_FromLong(res);
}
/* Handle Python signals. Only PyQt3 would get this far. */
assert(sipQtSupport->qt_disconnect_py_signal);
sipQtSupport->qt_disconnect_py_signal(txObj, sig, rxObj, slot);
Py_INCREF(Py_True);
return Py_True;
}
/*
* Free the resources of a slot.
*/
void sip_api_free_sipslot(sipSlot *slot)
{
assert(sipQtSupport);
if (slot->name != NULL)
{
sip_api_free(slot->name);
}
else if (slot->weakSlot == Py_True)
{
Py_DECREF(slot->pyobj);
}
/* Remove any weak reference. */
Py_XDECREF(slot->weakSlot);
}
/*
* Implement strdup() using sip_api_malloc().
*/
static char *sipStrdup(const char *s)
{
char *d;
if ((d = (char *)sip_api_malloc(strlen(s) + 1)) != NULL)
strcpy(d,s);
return d;
}
/*
* Initialise a slot, returning 0 if there was no error. If the signal was a
* Qt signal, then the slot may be a Python signal or a Python slot. If the
* signal was a Python signal, then the slot may be anything.
*/
int sip_api_save_slot(sipSlot *sp, PyObject *rxObj, const char *slot)
{
assert(sipQtSupport);
sp -> weakSlot = NULL;
if (slot == NULL)
{
sp -> name = NULL;
if (PyMethod_Check(rxObj))
{
/*
* Python creates methods on the fly. We could increment the
* reference count to keep it alive, but that would keep "self"
* alive as well and would probably be a circular reference.
* Instead we remember the component parts and hope they are still
* valid when we re-create the method when we need it.
*/
sipSaveMethod(&sp -> meth,rxObj);
/* Notice if the class instance disappears. */
sp -> weakSlot = getWeakRef(sp -> meth.mself);
/* This acts a flag to say that the slot is a method. */
sp -> pyobj = NULL;
}
else
{
PyObject *self;
/*
* We know that it is another type of callable, ie. a
* function/builtin.
*/
if (PyCFunction_Check(rxObj) &&
(self = PyCFunction_GET_SELF(rxObj)) != NULL &&
PyObject_TypeCheck(self, (PyTypeObject *)&sipSimpleWrapper_Type))
{
/*
* It is a wrapped C++ class method. We can't keep a copy
* because they are generated on the fly and we can't take a
* reference as that may keep the instance (ie. self) alive.
* We therefore treat it as if the user had specified the slot
* at "obj, SLOT('meth()')" rather than "obj.meth" (see below).
*/
const char *meth;
/* Get the method name. */
meth = ((PyCFunctionObject *)rxObj) -> m_ml -> ml_name;
if ((sp -> name = (char *)sip_api_malloc(strlen(meth) + 2)) == NULL)
return -1;
/*
* Copy the name and set the marker that it needs converting to
* a built-in method.
*/
sp -> name[0] = '\0';
strcpy(&sp -> name[1],meth);
sp -> pyobj = self;
sp -> weakSlot = getWeakRef(self);
}
else
{
/*
* Give the slot an extra reference to keep it alive and
* remember we have done so by treating weakSlot specially.
*/
Py_INCREF(rxObj);
sp->pyobj = rxObj;
Py_INCREF(Py_True);
sp->weakSlot = Py_True;
}
}
}
else if ((sp -> name = sipStrdup(slot)) == NULL)
return -1;
else if (isQtSlot(slot))
{
/*
* The user has decided to connect a Python signal to a Qt slot and
* specified the slot as "obj, SLOT('meth()')" rather than "obj.meth".
*/
char *tail;
/* Remove any arguments. */
if ((tail = strchr(sp -> name,'(')) != NULL)
*tail = '\0';
/*
* A bit of a hack to indicate that this needs converting to a built-in
* method.
*/
sp -> name[0] = '\0';
/* Notice if the class instance disappears. */
sp -> weakSlot = getWeakRef(rxObj);
sp -> pyobj = rxObj;
}
else
/* It's a Qt signal. */
sp -> pyobj = rxObj;
return 0;
}
/*
* Return a weak reference to the given object.
*/
static PyObject *getWeakRef(PyObject *obj)
{
PyObject *wr;
if ((wr = PyWeakref_NewRef(obj,NULL)) == NULL)
PyErr_Clear();
return wr;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,189 +0,0 @@
/*
* This file defines the SIP library internal interfaces.
*
* Copyright (c) 2020 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef _SIPINT_H
#define _SIPINT_H
#include <Python.h>
#include "sip.h"
#ifdef __cplusplus
extern "C" {
#endif
#undef TRUE
#define TRUE 1
#undef FALSE
#define FALSE 0
/*
* This defines a single entry in an object map's hash table.
*/
typedef struct
{
void *key; /* The C/C++ address. */
sipSimpleWrapper *first; /* The first object at this address. */
} sipHashEntry;
/*
* This defines the interface to a hash table class for mapping C/C++ addresses
* to the corresponding wrapped Python object.
*/
typedef struct
{
int primeIdx; /* Index into table sizes. */
unsigned long size; /* Size of hash table. */
unsigned long unused; /* Nr. unused in hash table. */
unsigned long stale; /* Nr. stale in hash table. */
sipHashEntry *hash_array; /* Current hash table. */
} sipObjectMap;
/*
* Support for the descriptors.
*/
extern PyTypeObject sipMethodDescr_Type;
PyObject *sipMethodDescr_New(PyMethodDef *pmd);
PyObject *sipMethodDescr_Copy(PyObject *orig, PyObject *mixin_name);
extern PyTypeObject sipVariableDescr_Type;
PyObject *sipVariableDescr_New(sipVariableDef *vd, const sipTypeDef *td,
const sipContainerDef *cod);
PyObject *sipVariableDescr_Copy(PyObject *orig, PyObject *mixin_name);
/*
* Support for API versions.
*/
PyObject *sipGetAPI(PyObject *self, PyObject *args);
PyObject *sipSetAPI(PyObject *self, PyObject *args);
int sip_api_is_api_enabled(const char *name, int from, int to);
int sipIsRangeEnabled(sipExportedModuleDef *em, int range_index);
int sipInitAPI(sipExportedModuleDef *em, PyObject *mod_dict);
/*
* Support for void pointers.
*/
extern PyTypeObject sipVoidPtr_Type;
void *sip_api_convert_to_void_ptr(PyObject *obj);
PyObject *sip_api_convert_from_void_ptr(void *val);
PyObject *sip_api_convert_from_const_void_ptr(const void *val);
PyObject *sip_api_convert_from_void_ptr_and_size(void *val, Py_ssize_t size);
PyObject *sip_api_convert_from_const_void_ptr_and_size(const void *val,
Py_ssize_t size);
/*
* Support for int converters.
*/
PyObject *sipEnableOverflowChecking(PyObject *self, PyObject *args);
int sip_api_enable_overflow_checking(int enable);
int sip_api_convert_to_bool(PyObject *o);
char sip_api_long_as_char(PyObject *o);
signed char sip_api_long_as_signed_char(PyObject *o);
unsigned char sip_api_long_as_unsigned_char(PyObject *o);
short sip_api_long_as_short(PyObject *o);
unsigned short sip_api_long_as_unsigned_short(PyObject *o);
int sip_api_long_as_int(PyObject *o);
unsigned int sip_api_long_as_unsigned_int(PyObject *o);
long sip_api_long_as_long(PyObject *o);
unsigned long sip_api_long_as_unsigned_long(PyObject *o);
#if defined(HAVE_LONG_LONG)
PY_LONG_LONG sip_api_long_as_long_long(PyObject *o);
unsigned PY_LONG_LONG sip_api_long_as_unsigned_long_long(PyObject *o);
#endif
size_t sip_api_long_as_size_t(PyObject *o);
extern sipQtAPI *sipQtSupport; /* The Qt support API. */
extern sipWrapperType sipSimpleWrapper_Type; /* The simple wrapper type. */
extern sipTypeDef *sipQObjectType; /* The QObject type. */
void *sipGetRx(sipSimpleWrapper *txSelf, const char *sigargs, PyObject *rxObj,
const char *slot, const char **memberp);
PyObject *sip_api_connect_rx(PyObject *txObj, const char *sig, PyObject *rxObj,
const char *slot, int type);
PyObject *sip_api_disconnect_rx(PyObject *txObj, const char *sig,
PyObject *rxObj,const char *slot);
/*
* These are part of the SIP API but are also used within the SIP module.
*/
void *sip_api_malloc(size_t nbytes);
void sip_api_free(void *mem);
void *sip_api_get_address(sipSimpleWrapper *w);
void *sip_api_get_cpp_ptr(sipSimpleWrapper *w, const sipTypeDef *td);
PyObject *sip_api_convert_from_type(void *cppPtr, const sipTypeDef *td,
PyObject *transferObj);
void sip_api_instance_destroyed(sipSimpleWrapper *sipSelf);
void sip_api_end_thread(void);
void *sip_api_force_convert_to_type(PyObject *pyObj, const sipTypeDef *td,
PyObject *transferObj, int flags, int *statep, int *iserrp);
void sip_api_free_sipslot(sipSlot *slot);
int sip_api_same_slot(const sipSlot *sp, PyObject *rxObj, const char *slot);
PyObject *sip_api_invoke_slot(const sipSlot *slot, PyObject *sigargs);
PyObject *sip_api_invoke_slot_ex(const sipSlot *slot, PyObject *sigargs,
int no_receiver_check);
void *sip_api_convert_rx(sipWrapper *txSelf, const char *sigargs,
PyObject *rxObj, const char *slot, const char **memberp, int flags);
int sip_api_save_slot(sipSlot *sp, PyObject *rxObj, const char *slot);
int sip_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);
int sip_api_deprecated(const char *classname, const char *method);
/*
* These are not part of the SIP API but are used within the SIP module.
*/
sipClassTypeDef *sipGetGeneratedClassType(const sipEncodedTypeDef *enc,
const sipClassTypeDef *ctd);
void sipSaveMethod(sipPyMethod *pm,PyObject *meth);
int sipGetPending(void **pp, sipWrapper **op, int *fp);
int sipIsPending(void);
PyObject *sipWrapInstance(void *cpp, PyTypeObject *py_type, PyObject *args,
sipWrapper *owner, int flags);
void *sipConvertRxEx(sipWrapper *txSelf, const char *sigargs,
PyObject *rxObj, const char *slot, const char **memberp, int flags);
void sipOMInit(sipObjectMap *om);
void sipOMFinalise(sipObjectMap *om);
sipSimpleWrapper *sipOMFindObject(sipObjectMap *om, void *key,
const sipTypeDef *td);
void sipOMAddObject(sipObjectMap *om, sipSimpleWrapper *val);
int sipOMRemoveObject(sipObjectMap *om, sipSimpleWrapper *val);
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define sipSetBool(p, v) (*(_Bool *)(p) = (v))
#endif
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,218 +0,0 @@
/*
* Thread support for the SIP library. This module provides the hooks for
* C++ classes that provide a thread interface to interact properly with the
* Python threading infrastructure.
*
* Copyright (c) 2020 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "sipint.h"
/*
* The data associated with pending request to wrap an object.
*/
typedef struct _pendingDef {
void *cpp; /* The C/C++ object ot be wrapped. */
sipWrapper *owner; /* The owner of the object. */
int flags; /* The flags. */
} pendingDef;
#ifdef WITH_THREAD
#include <pythread.h>
/*
* The per thread data we need to maintain.
*/
typedef struct _threadDef {
long thr_ident; /* The thread identifier. */
pendingDef pending; /* An object waiting to be wrapped. */
struct _threadDef *next; /* Next in the list. */
} threadDef;
static threadDef *threads = NULL; /* Linked list of threads. */
static threadDef *currentThreadDef(int auto_alloc);
#endif
static pendingDef *get_pending(int auto_alloc);
/*
* Get the address etc. of any C/C++ object waiting to be wrapped.
*/
int sipGetPending(void **pp, sipWrapper **op, int *fp)
{
pendingDef *pd;
if ((pd = get_pending(TRUE)) == NULL)
return -1;
*pp = pd->cpp;
*op = pd->owner;
*fp = pd->flags;
/* Clear in case we execute Python code before finishing this wrapping. */
pd->cpp = NULL;
return 0;
}
/*
* Return TRUE if anything is pending.
*/
int sipIsPending(void)
{
pendingDef *pd;
if ((pd = get_pending(FALSE)) == NULL)
return FALSE;
return (pd->cpp != NULL);
}
/*
* Convert a new C/C++ pointer to a Python instance.
*/
PyObject *sipWrapInstance(void *cpp, PyTypeObject *py_type, PyObject *args,
sipWrapper *owner, int flags)
{
pendingDef old_pending, *pd;
PyObject *self;
if (cpp == NULL)
{
Py_INCREF(Py_None);
return Py_None;
}
/*
* Object creation can trigger the Python garbage collector which in turn
* can execute arbitrary Python code which can then call this function
* recursively. Therefore we save any existing pending object before
* setting the new one.
*/
if ((pd = get_pending(TRUE)) == NULL)
return NULL;
old_pending = *pd;
pd->cpp = cpp;
pd->owner = owner;
pd->flags = flags;
self = PyObject_Call((PyObject *)py_type, args, NULL);
*pd = old_pending;
return self;
}
/*
* Handle the termination of a thread.
*/
void sip_api_end_thread(void)
{
#ifdef WITH_THREAD
threadDef *thread;
PyGILState_STATE gil = PyGILState_Ensure();
if ((thread = currentThreadDef(FALSE)) != NULL)
thread->thr_ident = 0;
PyGILState_Release(gil);
#endif
}
/*
* Return the pending data for the current thread, allocating it if necessary,
* or NULL if there was an error.
*/
static pendingDef *get_pending(int auto_alloc)
{
#ifdef WITH_THREAD
threadDef *thread;
if ((thread = currentThreadDef(auto_alloc)) == NULL)
return NULL;
return &thread->pending;
#else
static pendingDef pending;
return &pending;
#endif
}
#ifdef WITH_THREAD
/*
* Return the thread data for the current thread, allocating it if necessary,
* or NULL if there was an error.
*/
static threadDef *currentThreadDef(int auto_alloc)
{
threadDef *thread, *empty = NULL;
long ident = PyThread_get_thread_ident();
/* See if we already know about the thread. */
for (thread = threads; thread != NULL; thread = thread->next)
{
if (thread->thr_ident == ident)
return thread;
if (thread->thr_ident == 0)
empty = thread;
}
if (!auto_alloc)
{
/* This is not an error. */
return NULL;
}
if (empty != NULL)
{
/* Use an empty entry in the list. */
thread = empty;
}
else if ((thread = sip_api_malloc(sizeof (threadDef))) == NULL)
{
return NULL;
}
else
{
thread->next = threads;
threads = thread;
}
thread->thr_ident = ident;
thread->pending.cpp = NULL;
return thread;
}
#endif

View File

@@ -1,752 +0,0 @@
/*
* SIP library code.
*
* Copyright (c) 2019 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* This file is part of SIP.
*
* This copy of SIP is licensed for use under the terms of the SIP License
* Agreement. See the file LICENSE for more details.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <Python.h>
#include <stddef.h>
#include <string.h>
#include "sipint.h"
#include "array.h"
/* The object data structure. */
typedef struct {
PyObject_HEAD
void *voidptr;
Py_ssize_t size;
int rw;
} sipVoidPtrObject;
/* The structure used to hold the results of a voidptr conversion. */
struct vp_values {
void *voidptr;
Py_ssize_t size;
int rw;
};
static int check_size(PyObject *self);
static int check_rw(PyObject *self);
static int check_index(PyObject *self, Py_ssize_t idx);
static void bad_key(PyObject *key);
static int check_slice_size(Py_ssize_t size, Py_ssize_t value_size);
static PyObject *make_voidptr(void *voidptr, Py_ssize_t size, int rw);
static int vp_convertor(PyObject *arg, struct vp_values *vp);
static Py_ssize_t get_size_from_arg(sipVoidPtrObject *v, Py_ssize_t size);
/*
* Implement ascapsule() for the type.
*/
static PyObject *sipVoidPtr_ascapsule(sipVoidPtrObject *v, PyObject *arg)
{
(void)arg;
return PyCapsule_New(v->voidptr, NULL, NULL);
}
/*
* Implement asarray() for the type.
*/
static PyObject *sipVoidPtr_asarray(sipVoidPtrObject *v, PyObject *args,
PyObject *kw)
{
static char *kwlist[] = {"size", NULL};
Py_ssize_t size = -1;
if (!PyArg_ParseTupleAndKeywords(args, kw, "|n:asarray", kwlist, &size))
return NULL;
if ((size = get_size_from_arg(v, size)) < 0)
return NULL;
return sip_api_convert_to_array(v->voidptr, "B", size,
(v->rw ? 0 : SIP_READ_ONLY));
}
/*
* Implement asstring() for the type.
*/
static PyObject *sipVoidPtr_asstring(sipVoidPtrObject *v, PyObject *args,
PyObject *kw)
{
static char *kwlist[] = {"size", NULL};
Py_ssize_t size = -1;
if (!PyArg_ParseTupleAndKeywords(args, kw, "|n:asstring", kwlist, &size))
return NULL;
if ((size = get_size_from_arg(v, size)) < 0)
return NULL;
return PyBytes_FromStringAndSize(v->voidptr, size);
}
/*
* Implement getsize() for the type.
*/
static PyObject *sipVoidPtr_getsize(sipVoidPtrObject *v, PyObject *arg)
{
(void)arg;
return PyLong_FromSsize_t(v->size);
}
/*
* Implement setsize() for the type.
*/
static PyObject *sipVoidPtr_setsize(sipVoidPtrObject *v, PyObject *arg)
{
Py_ssize_t size = PyLong_AsSsize_t(arg);
if (PyErr_Occurred())
return NULL;
v->size = size;
Py_INCREF(Py_None);
return Py_None;
}
/*
* Implement getwriteable() for the type.
*/
static PyObject *sipVoidPtr_getwriteable(sipVoidPtrObject *v, PyObject *arg)
{
(void)arg;
return PyBool_FromLong(v->rw);
}
/*
* Implement setwriteable() for the type.
*/
static PyObject *sipVoidPtr_setwriteable(sipVoidPtrObject *v, PyObject *arg)
{
int rw;
if ((rw = PyObject_IsTrue(arg)) < 0)
return NULL;
v->rw = rw;
Py_INCREF(Py_None);
return Py_None;
}
/* The methods data structure. */
static PyMethodDef sipVoidPtr_Methods[] = {
{"asarray", (PyCFunction)sipVoidPtr_asarray, METH_VARARGS|METH_KEYWORDS, NULL},
{"ascapsule", (PyCFunction)sipVoidPtr_ascapsule, METH_NOARGS, NULL},
{"asstring", (PyCFunction)sipVoidPtr_asstring, METH_VARARGS|METH_KEYWORDS, NULL},
{"getsize", (PyCFunction)sipVoidPtr_getsize, METH_NOARGS, NULL},
{"setsize", (PyCFunction)sipVoidPtr_setsize, METH_O, NULL},
{"getwriteable", (PyCFunction)sipVoidPtr_getwriteable, METH_NOARGS, NULL},
{"setwriteable", (PyCFunction)sipVoidPtr_setwriteable, METH_O, NULL},
{NULL, NULL, 0, NULL}
};
/*
* Implement bool() for the type.
*/
static int sipVoidPtr_bool(PyObject *self)
{
return (((sipVoidPtrObject *)self)->voidptr != NULL);
}
/*
* Implement int() for the type.
*/
static PyObject *sipVoidPtr_int(PyObject *self)
{
return PyLong_FromVoidPtr(((sipVoidPtrObject *)self)->voidptr);
}
/* The number methods data structure. */
static PyNumberMethods sipVoidPtr_NumberMethods = {
0, /* nb_add */
0, /* nb_subtract */
0, /* nb_multiply */
0, /* nb_remainder */
0, /* nb_divmod */
0, /* nb_power */
0, /* nb_negative */
0, /* nb_positive */
0, /* nb_absolute */
sipVoidPtr_bool, /* nb_bool */
0, /* nb_invert */
0, /* nb_lshift */
0, /* nb_rshift */
0, /* nb_and */
0, /* nb_xor */
0, /* nb_or */
sipVoidPtr_int, /* nb_int */
0, /* nb_reserved */
0, /* nb_float */
0, /* nb_inplace_add */
0, /* nb_inplace_subtract */
0, /* nb_inplace_multiply */
0, /* nb_inplace_remainder */
0, /* nb_inplace_power */
0, /* nb_inplace_lshift */
0, /* nb_inplace_rshift */
0, /* nb_inplace_and */
0, /* nb_inplace_xor */
0, /* nb_inplace_or */
0, /* nb_floor_divide */
0, /* nb_true_divide */
0, /* nb_inplace_floor_divide */
0, /* nb_inplace_true_divide */
0, /* nb_index */
0, /* nb_matrix_multiply */
0, /* nb_inplace_matrix_multiply */
};
/*
* Implement len() for the type.
*/
static Py_ssize_t sipVoidPtr_length(PyObject *self)
{
if (check_size(self) < 0)
return -1;
return ((sipVoidPtrObject *)self)->size;
}
/*
* Implement sequence item sub-script for the type.
*/
static PyObject *sipVoidPtr_item(PyObject *self, Py_ssize_t idx)
{
if (check_size(self) < 0 || check_index(self, idx) < 0)
return NULL;
return PyBytes_FromStringAndSize(
(char *)((sipVoidPtrObject *)self)->voidptr + idx, 1);
}
/* The sequence methods data structure. */
static PySequenceMethods sipVoidPtr_SequenceMethods = {
sipVoidPtr_length, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
sipVoidPtr_item, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
0, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
/*
* Implement mapping sub-script for the type.
*/
static PyObject *sipVoidPtr_subscript(PyObject *self, PyObject *key)
{
sipVoidPtrObject *v;
if (check_size(self) < 0)
return NULL;
v = (sipVoidPtrObject *)self;
if (PyIndex_Check(key))
{
Py_ssize_t idx = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (idx == -1 && PyErr_Occurred())
return NULL;
if (idx < 0)
idx += v->size;
return sipVoidPtr_item(self, idx);
}
if (PySlice_Check(key))
{
Py_ssize_t start, stop, step, slicelength;
if (sip_api_convert_from_slice_object(key, v->size, &start, &stop, &step, &slicelength) < 0)
return NULL;
if (step != 1)
{
PyErr_SetNone(PyExc_NotImplementedError);
return NULL;
}
return make_voidptr((char *)v->voidptr + start, slicelength, v->rw);
}
bad_key(key);
return NULL;
}
/*
* Implement mapping assignment sub-script for the type.
*/
static int sipVoidPtr_ass_subscript(PyObject *self, PyObject *key,
PyObject *value)
{
sipVoidPtrObject *v;
Py_ssize_t start, size;
Py_buffer value_view;
if (check_rw(self) < 0 || check_size(self) < 0)
return -1;
v = (sipVoidPtrObject *)self;
if (PyIndex_Check(key))
{
start = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (start == -1 && PyErr_Occurred())
return -1;
if (start < 0)
start += v->size;
if (check_index(self, start) < 0)
return -1;
size = 1;
}
else if (PySlice_Check(key))
{
Py_ssize_t stop, step;
if (sip_api_convert_from_slice_object(key, v->size, &start, &stop, &step, &size) < 0)
return -1;
if (step != 1)
{
PyErr_SetNone(PyExc_NotImplementedError);
return -1;
}
}
else
{
bad_key(key);
return -1;
}
if (PyObject_GetBuffer(value, &value_view, PyBUF_CONTIG_RO) < 0)
return -1;
/* We could allow any item size... */
if (value_view.itemsize != 1)
{
PyErr_Format(PyExc_TypeError, "'%s' must have an item size of 1",
Py_TYPE(value_view.obj)->tp_name);
PyBuffer_Release(&value_view);
return -1;
}
if (check_slice_size(size, value_view.len) < 0)
{
PyBuffer_Release(&value_view);
return -1;
}
memmove((char *)v->voidptr + start, value_view.buf, size);
PyBuffer_Release(&value_view);
return 0;
}
/* The mapping methods data structure. */
static PyMappingMethods sipVoidPtr_MappingMethods = {
sipVoidPtr_length, /* mp_length */
sipVoidPtr_subscript, /* mp_subscript */
sipVoidPtr_ass_subscript, /* mp_ass_subscript */
};
/*
* The buffer implementation for Python v2.6.3 and later.
*/
static int sipVoidPtr_getbuffer(PyObject *self, Py_buffer *buf, int flags)
{
sipVoidPtrObject *v;
if (check_size(self) < 0)
return -1;
v = (sipVoidPtrObject *)self;
return PyBuffer_FillInfo(buf, self, v->voidptr, v->size, !v->rw, flags);
}
/* The buffer methods data structure. */
static PyBufferProcs sipVoidPtr_BufferProcs = {
sipVoidPtr_getbuffer, /* bf_getbuffer */
0 /* bf_releasebuffer */
};
/*
* Implement __new__ for the type.
*/
static PyObject *sipVoidPtr_new(PyTypeObject *subtype, PyObject *args,
PyObject *kw)
{
static char *kwlist[] = {"address", "size", "writeable", NULL};
struct vp_values vp_conversion;
Py_ssize_t size = -1;
int rw = -1;
PyObject *obj;
if (!PyArg_ParseTupleAndKeywords(args, kw, "O&|ni:voidptr", kwlist, vp_convertor, &vp_conversion, &size, &rw))
return NULL;
/* Use the explicit size if one was given. */
if (size >= 0)
vp_conversion.size = size;
/* Use the explicit writeable flag if one was given. */
if (rw >= 0)
vp_conversion.rw = rw;
/* Create the instance. */
if ((obj = subtype->tp_alloc(subtype, 0)) == NULL)
return NULL;
/* Save the values. */
((sipVoidPtrObject *)obj)->voidptr = vp_conversion.voidptr;
((sipVoidPtrObject *)obj)->size = vp_conversion.size;
((sipVoidPtrObject *)obj)->rw = vp_conversion.rw;
return obj;
}
/* The type data structure. */
PyTypeObject sipVoidPtr_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"sip.voidptr", /* tp_name */
sizeof (sipVoidPtrObject), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved (Python v3), tp_compare (Python v2) */
0, /* tp_repr */
&sipVoidPtr_NumberMethods, /* tp_as_number */
&sipVoidPtr_SequenceMethods, /* tp_as_sequence */
&sipVoidPtr_MappingMethods, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
&sipVoidPtr_BufferProcs, /* tp_as_buffer */
#if defined(Py_TPFLAGS_HAVE_NEWBUFFER)
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_NEWBUFFER, /* tp_flags */
#else
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
#endif
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
sipVoidPtr_Methods, /* 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 */
0, /* tp_alloc */
sipVoidPtr_new, /* tp_new */
0, /* tp_free */
0, /* tp_is_gc */
0, /* tp_bases */
0, /* tp_mro */
0, /* tp_cache */
0, /* tp_subclasses */
0, /* tp_weaklist */
0, /* tp_del */
0, /* tp_version_tag */
0, /* tp_finalize */
#if PY_VERSION_HEX >= 0x03080000
0, /* tp_vectorcall */
#endif
};
/*
* A convenience function to convert a C/C++ void pointer from a Python object.
*/
void *sip_api_convert_to_void_ptr(PyObject *obj)
{
struct vp_values vp;
if (obj == NULL)
{
PyErr_SetString(PyExc_TypeError, "sip.voidptr is NULL");
return NULL;
}
if (vp_convertor(obj, &vp))
return vp.voidptr;
return PyLong_AsVoidPtr(obj);
}
/*
* Convert a C/C++ void pointer to a sip.voidptr object.
*/
PyObject *sip_api_convert_from_void_ptr(void *val)
{
return make_voidptr(val, -1, TRUE);
}
/*
* Convert a C/C++ void pointer to a sip.voidptr object.
*/
PyObject *sip_api_convert_from_const_void_ptr(const void *val)
{
return make_voidptr((void *)val, -1, FALSE);
}
/*
* Convert a sized C/C++ void pointer to a sip.voidptr object.
*/
PyObject *sip_api_convert_from_void_ptr_and_size(void *val, Py_ssize_t size)
{
return make_voidptr(val, size, TRUE);
}
/*
* Convert a sized C/C++ const void pointer to a sip.voidptr object.
*/
PyObject *sip_api_convert_from_const_void_ptr_and_size(const void *val,
Py_ssize_t size)
{
return make_voidptr((void *)val, size, FALSE);
}
/*
* Check that a void pointer has an explicit size and raise an exception if it
* hasn't.
*/
static int check_size(PyObject *self)
{
if (((sipVoidPtrObject *)self)->size >= 0)
return 0;
PyErr_SetString(PyExc_IndexError,
"sip.voidptr object has an unknown size");
return -1;
}
/*
* Check that a void pointer is writable.
*/
static int check_rw(PyObject *self)
{
if (((sipVoidPtrObject *)self)->rw)
return 0;
PyErr_SetString(PyExc_TypeError,
"cannot modify a read-only sip.voidptr object");
return -1;
}
/*
* Check that an index is valid for a void pointer.
*/
static int check_index(PyObject *self, Py_ssize_t idx)
{
if (idx >= 0 && idx < ((sipVoidPtrObject *)self)->size)
return 0;
PyErr_SetString(PyExc_IndexError, "index out of bounds");
return -1;
}
/*
* Raise an exception about a bad sub-script key.
*/
static void bad_key(PyObject *key)
{
PyErr_Format(PyExc_TypeError,
"cannot index a sip.voidptr object using '%s'",
Py_TYPE(key)->tp_name);
}
/*
* Check that the size of a value is the same as the size of the slice it is
* replacing.
*/
static int check_slice_size(Py_ssize_t size, Py_ssize_t value_size)
{
if (value_size == size)
return 0;
PyErr_SetString(PyExc_ValueError,
"cannot modify the size of a sip.voidptr object");
return -1;
}
/*
* Do the work of converting a void pointer.
*/
static PyObject *make_voidptr(void *voidptr, Py_ssize_t size, int rw)
{
sipVoidPtrObject *self;
if (voidptr == NULL)
{
Py_INCREF(Py_None);
return Py_None;
}
if ((self = PyObject_NEW(sipVoidPtrObject, &sipVoidPtr_Type)) == NULL)
return NULL;
self->voidptr = voidptr;
self->size = size;
self->rw = rw;
return (PyObject *)self;
}
/*
* Convert a Python object to the values needed to create a voidptr.
*/
static int vp_convertor(PyObject *arg, struct vp_values *vp)
{
void *ptr;
Py_ssize_t size = -1;
int rw = TRUE;
if (arg == Py_None)
{
ptr = NULL;
}
else if (PyCapsule_CheckExact(arg))
{
ptr = PyCapsule_GetPointer(arg, NULL);
}
else if (PyObject_TypeCheck(arg, &sipVoidPtr_Type))
{
ptr = ((sipVoidPtrObject *)arg)->voidptr;
size = ((sipVoidPtrObject *)arg)->size;
rw = ((sipVoidPtrObject *)arg)->rw;
}
else if (PyObject_CheckBuffer(arg))
{
Py_buffer view;
if (PyObject_GetBuffer(arg, &view, PyBUF_SIMPLE) < 0)
return 0;
ptr = view.buf;
size = view.len;
rw = !view.readonly;
PyBuffer_Release(&view);
}
else
{
PyErr_Clear();
ptr = PyLong_AsVoidPtr(arg);
if (PyErr_Occurred())
{
PyErr_SetString(PyExc_TypeError, "a single integer, Capsule, None, bytes-like object or another sip.voidptr object is required");
return 0;
}
}
vp->voidptr = ptr;
vp->size = size;
vp->rw = rw;
return 1;
}
/*
* Get a size possibly supplied as an argument, otherwise get it from the
* object. Raise an exception if there was no size specified.
*/
static Py_ssize_t get_size_from_arg(sipVoidPtrObject *v, Py_ssize_t size)
{
/* Use the current size if one wasn't explicitly given. */
if (size < 0)
size = v->size;
if (size < 0)
PyErr_SetString(PyExc_ValueError,
"a size must be given or the sip.voidptr object must have a size");
return size;
}

View File

@@ -563,12 +563,12 @@ def build(bld):
features = 'c cxx cshlib cxxshlib pyext', features = 'c cxx cshlib cxxshlib pyext',
target = makeTargetName(bld, 'siplib'), target = makeTargetName(bld, 'siplib'),
source = ['sip/siplib/apiversions.c', source = ['sip/siplib/apiversions.c',
'sip/siplib/array.c',
'sip/siplib/bool.cpp', 'sip/siplib/bool.cpp',
'sip/siplib/descriptors.c', 'sip/siplib/descriptors.c',
'sip/siplib/int_convertors.c', 'sip/siplib/int_convertors.c',
'sip/siplib/objmap.c', 'sip/siplib/objmap.c',
'sip/siplib/qtlib.c', 'sip/siplib/qtlib.c',
'sip/siplib/sip_array.c',
'sip/siplib/siplib.c', 'sip/siplib/siplib.c',
'sip/siplib/threads.c', 'sip/siplib/threads.c',
'sip/siplib/voidptr.c', 'sip/siplib/voidptr.c',

File diff suppressed because it is too large Load Diff