import unittest
from test.support import import_helper
-import types
xxlimited = import_helper.import_module('xxlimited')
-xxlimited_35 = import_helper.import_module('xxlimited_35')
-
-class CommonTests:
- module: types.ModuleType
-
- def test_xxo_new(self):
- xxo = self.module.Xxo()
-
- def test_xxo_attributes(self):
- xxo = self.module.Xxo()
+# if import of xxlimited succeeded, the other ones should be importable.
+import xxlimited_3_13
+import xxlimited_35
+
+MODULES = {
+ (3, 15): xxlimited,
+ (3, 13): xxlimited_3_13,
+ (3, 5): xxlimited_35,
+}
+
+def test_with_xxlimited_modules(since=None, until=None):
+ def _decorator(func):
+ def _wrapper(self, *args, **kwargs):
+ for version, module in MODULES.items():
+ if since and version < since:
+ continue
+ if until and version >= until:
+ continue
+ with self.subTest(version=version):
+ func(self, module, *args, **kwargs)
+ return _wrapper
+ return _decorator
+
+class XXLimitedTests(unittest.TestCase):
+ @test_with_xxlimited_modules()
+ def test_xxo_new(self, module):
+ xxo = module.Xxo()
+
+ @test_with_xxlimited_modules()
+ def test_xxo_attributes(self, module):
+ xxo = module.Xxo()
with self.assertRaises(AttributeError):
xxo.foo
with self.assertRaises(AttributeError):
with self.assertRaises(AttributeError):
xxo.foo
- def test_foo(self):
+ @test_with_xxlimited_modules()
+ def test_foo(self, module):
# the foo function adds 2 numbers
- self.assertEqual(self.module.foo(1, 2), 3)
+ self.assertEqual(module.foo(1, 2), 3)
- def test_str(self):
- self.assertIsSubclass(self.module.Str, str)
- self.assertIsNot(self.module.Str, str)
+ @test_with_xxlimited_modules()
+ def test_str(self, module):
+ self.assertIsSubclass(module.Str, str)
+ self.assertIsNot(module.Str, str)
- custom_string = self.module.Str("abcd")
+ custom_string = module.Str("abcd")
self.assertEqual(custom_string, "abcd")
self.assertEqual(custom_string.upper(), "ABCD")
- def test_new(self):
- xxo = self.module.new()
+ @test_with_xxlimited_modules()
+ def test_new(self, module):
+ xxo = module.new()
self.assertEqual(xxo.demo("abc"), "abc")
-
-class TestXXLimited(CommonTests, unittest.TestCase):
- module = xxlimited
-
- def test_xxo_demo(self):
- xxo = self.module.Xxo()
- other = self.module.Xxo()
+ @test_with_xxlimited_modules()
+ def test_xxo_demo(self, module):
+ xxo = module.Xxo()
self.assertEqual(xxo.demo("abc"), "abc")
+ self.assertEqual(xxo.demo(0), None)
+ self.assertEqual(xxo.__module__, module.__name__)
+ with self.assertRaises(TypeError):
+ module.Xxo('arg')
+ with self.assertRaises(TypeError):
+ module.Xxo(kwarg='arg')
+
+ @test_with_xxlimited_modules(since=(3, 13))
+ def test_xxo_demo_extra(self, module):
+ xxo = module.Xxo()
+ other = module.Xxo()
self.assertEqual(xxo.demo(xxo), xxo)
self.assertEqual(xxo.demo(other), other)
- self.assertEqual(xxo.demo(0), None)
- def test_error(self):
- with self.assertRaises(self.module.Error):
- raise self.module.Error
-
- def test_buffer(self):
- xxo = self.module.Xxo()
+ @test_with_xxlimited_modules(since=(3, 15))
+ def test_xxo_subclass(self, module):
+ class Sub(module.Xxo):
+ pass
+ sub = Sub()
+ sub.a = 123
+ self.assertEqual(sub.a, 123)
+ with self.assertRaisesRegex(AttributeError, "cannot set 'reserved'"):
+ sub.reserved = 123
+
+ @test_with_xxlimited_modules(since=(3, 13))
+ def test_error(self, module):
+ with self.assertRaises(module.Error):
+ raise module.Error
+
+ @test_with_xxlimited_modules(since=(3, 13))
+ def test_buffer(self, module):
+ xxo = module.Xxo()
self.assertEqual(xxo.x_exports, 0)
b1 = memoryview(xxo)
self.assertEqual(xxo.x_exports, 1)
self.assertEqual(b1[0], 1)
self.assertEqual(b2[0], 1)
-
-class TestXXLimited35(CommonTests, unittest.TestCase):
- module = xxlimited_35
-
- def test_xxo_demo(self):
- xxo = self.module.Xxo()
- other = self.module.Xxo()
- self.assertEqual(xxo.demo("abc"), "abc")
- self.assertEqual(xxo.demo(0), None)
-
+ @test_with_xxlimited_modules(until=(3, 5))
def test_roj(self):
# the roj function always fails
with self.assertRaises(SystemError):
self.module.roj(0)
+ @test_with_xxlimited_modules(until=(3, 5))
def test_null(self):
null1 = self.module.Null()
null2 = self.module.Null()
#xx xxmodule.c
#xxlimited xxlimited.c
#xxlimited_35 xxlimited_35.c
+#xxlimited_3_13 xxlimited_3_13.c
#xxsubtype xxsubtype.c
# Testing
# Limited API template modules; must be built as shared modules.
@MODULE_XXLIMITED_TRUE@xxlimited xxlimited.c
@MODULE_XXLIMITED_35_TRUE@xxlimited_35 xxlimited_35.c
+@MODULE_XXLIMITED_3_13_TRUE@xxlimited_3_13 xxlimited_3_13.c
# for performance
other files, you'll have to create a file "foobarobject.h"; see
floatobject.h for an example.
- This module roughly corresponds to::
+ This module uses Limited API 3.15.
+ See ``xxlimited_3_13.c`` if you want to support older CPython versions.
+
+ This module roughly corresponds to the following.
+ (All underscore-prefixed attributes are not accessible from Python.)
+
+ ::
class Xxo:
"""A class that explicitly stores attributes in an internal dict
return self._x_attr[name]
def __setattr__(self, name, value):
+ if name == "reserved":
+ raise AttributeError("cannot set 'reserved'")
self._x_attr[name] = value
def __delattr__(self, name):
pass
*/
-// Need limited C API version 3.13 for Py_mod_gil
-#include "pyconfig.h" // Py_GIL_DISABLED
-#ifndef Py_GIL_DISABLED
-# define Py_LIMITED_API 0x030d0000
-#endif
+// Target both flavors of the Stable ABI.
+// Both are set to version 3.15, which adds PyModExport
+// (When using a build tool, check if it has an option to set these
+// so they do not need to be defined in the source.)
+#define Py_LIMITED_API 0x030f0000 // abi3 (GIL-enabled builds)
+#define Py_TARGET_ABI3T 0x030f0000 // abi3t (free-threaded builds)
+
#include "Python.h"
#include <string.h>
// Module state
typedef struct {
- PyObject *Xxo_Type; // Xxo class
+ PyTypeObject *Xxo_Type; // Xxo class
PyObject *Error_Type; // Error class
} xx_state;
-/* Xxo objects */
+/* Xxo objects.
+ *
+ * A non-trivial extension type, intentionally showing a number of features
+ * that aren't easy to implement in the Limited API.
+ */
+
+// Forward declaration
+static PyType_Spec Xxo_Type_spec;
+
+// Get the module state (xx_state*) from a given type object 'type', which
+// must be a subclass of Xxo (the type we're defining).
+// This is complicated by the fact that the Xxo type is dynamically allocated,
+// and there may be several such types in a given Python process -- for
+// example, in different subinterpreters, or through loading this
+// extension module several times.
+// So, we don't have a "global" pointer to the type, or to the module, etc.;
+// instead we search based on `Xxo_Type_spec` (which is static, immutable,
+// and process-global).
+//
+// When possible, it's better to avoid `PyType_GetBaseByToken` -- for an
+// example, see the `demo` method (Xxo_demo C function), which uses a
+// "defining class". But, in many cases it's the best solution.
+static xx_state *
+Xxo_state_from_type(PyTypeObject *type)
+{
+ PyTypeObject *base;
+ // Search all superclasses of 'type' for one that was defined using
+ // "Xxo_Type_spec". That must be our 'Xxo' class.
+ if (PyType_GetBaseByToken(type, &Xxo_Type_spec, &base) < 0) {
+ return NULL;
+ }
+ if (base == NULL) {
+ PyErr_SetString(PyExc_TypeError, "need Xxo subclass");
+ return NULL;
+ }
+ // From this type, get the associated module. That must be the
+ // relevant `xxlimited` module.
+ xx_state *state = PyType_GetModuleState(base);
+ Py_DECREF(base);
+ return state;
+}
-// Instance state
+// Structure for data needed by the XxoObject type.
+// Since the object may be shared across threads, access to the fields
+// usually needs to be synchronized (using Py_BEGIN_CRITICAL_SECTION).
typedef struct {
- PyObject_HEAD
- PyObject *x_attr; /* Attributes dictionary.
- * May be NULL, which acts as an
- * empty dict.
- */
- char x_buffer[BUFSIZE]; /* buffer for Py_buffer */
- Py_ssize_t x_exports; /* how many buffer are exported */
-} XxoObject;
-
-#define XxoObject_CAST(op) ((XxoObject *)(op))
-// TODO: full support for type-checking was added in 3.14 (Py_tp_token)
-// #define XxoObject_Check(v) Py_IS_TYPE(v, Xxo_Type)
-
-static XxoObject *
-newXxoObject(PyObject *module)
+ PyObject *x_attr; /* Attributes dictionary.
+ * May be NULL, which acts as an
+ * empty dict.
+ */
+ Py_ssize_t x_exports; /* how many buffers are exported */
+ char x_buffer[BUFSIZE]; /* buffer for Py_buffer (for simplicity,
+ * this is constant, so does not need
+ * synchronization)
+ */
+} XxoObject_Data;
+
+// Get the `XxoObject_Data` structure for a given instance of our type.
+static XxoObject_Data *
+Xxo_get_data(PyObject *self)
{
- xx_state *state = PyModule_GetState(module);
+ xx_state *state = Xxo_state_from_type(Py_TYPE(self));
+ if (!state) {
+ return NULL;
+ }
+ XxoObject_Data *data = PyObject_GetTypeData(self, state->Xxo_Type);
+ return data;
+}
+
+// A variant of Xxo_get_data to be used in the tp_traverse handler.
+// This function cannot have side effects (including reference count
+// manipulation, creating objects, and raising exceptions), and must not
+// call API functions that might have side effects.
+// See: https://docs.python.org/3.15/c-api/gcsupport.html#traversal
+static XxoObject_Data *
+Xxo_get_data_DuringGC(PyObject *self)
+{
+ PyTypeObject *base;
+ PyType_GetBaseByToken_DuringGC(Py_TYPE(self), &Xxo_Type_spec, &base);
+ if (base == NULL) {
+ return NULL;
+ }
+ xx_state *state = PyType_GetModuleState_DuringGC(base);
if (state == NULL) {
return NULL;
}
- XxoObject *self;
- self = PyObject_GC_New(XxoObject, (PyTypeObject*)state->Xxo_Type);
+ XxoObject_Data *data = PyObject_GetTypeData_DuringGC(self, state->Xxo_Type);
+ return data;
+}
+
+// Xxo initialization
+// This is the implementation of Xxo.__new__
+static PyObject *
+Xxo_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ // Validate that we did not get any arguments.
+ if ((args != NULL && PyObject_Length(args))
+ || (kwargs != NULL && PyObject_Length(kwargs)))
+ {
+ PyErr_SetString(PyExc_TypeError, "Xxo.__new__() takes no arguments");
+ return NULL;
+ }
+ // Create an instance of *type* (which may be a subclass)
+ allocfunc alloc = PyType_GetSlot(type, Py_tp_alloc);
+ PyObject *self = alloc(type, 0);
if (self == NULL) {
return NULL;
}
- self->x_attr = NULL;
- memset(self->x_buffer, 0, BUFSIZE);
- self->x_exports = 0;
+
+ // Initialize the C members on the instance.
+ // This is only included for the sake of example. The default alloc
+ // function zeroes instance memory; we don't need to do it again.
+ // Note that we during initialization (and finalization), we hold the only
+ // reference to the object, so we don't need to synchronize with
+ // other threads.
+ XxoObject_Data *xxo_data = Xxo_get_data(self);
+ if (xxo_data == NULL) {
+ Py_DECREF(self);
+ return NULL;
+ }
+
+ xxo_data->x_attr = NULL;
+ memset(xxo_data->x_buffer, 0, BUFSIZE);
+ xxo_data->x_exports = 0;
return self;
}
// traverse: Visit all references from an object, including its type
static int
-Xxo_traverse(PyObject *op, visitproc visit, void *arg)
+Xxo_traverse(PyObject *self, visitproc visit, void *arg)
{
// Visit the type
- Py_VISIT(Py_TYPE(op));
+ Py_VISIT(Py_TYPE(self));
// Visit the attribute dict
- XxoObject *self = XxoObject_CAST(op);
- Py_VISIT(self->x_attr);
+ XxoObject_Data *data = Xxo_get_data_DuringGC(self);
+ if (data == NULL) {
+ return 0;
+ }
+ Py_VISIT(data->x_attr);
return 0;
}
// clear: drop references in order to break all reference cycles
static int
-Xxo_clear(PyObject *op)
+Xxo_clear(PyObject *self)
{
- XxoObject *self = XxoObject_CAST(op);
- Py_CLEAR(self->x_attr);
+ XxoObject_Data *data = Xxo_get_data(self);
+ if (data == NULL) {
+ return 0;
+ }
+ Py_CLEAR(data->x_attr);
return 0;
}
// finalize: like clear, but should leave the object in a consistent state.
// Equivalent to `__del__` in Python.
static void
-Xxo_finalize(PyObject *op)
+Xxo_finalize(PyObject *self)
{
- XxoObject *self = XxoObject_CAST(op);
- Py_CLEAR(self->x_attr);
+ XxoObject_Data *data = Xxo_get_data(self);
+ if (data == NULL) {
+ return;
+ }
+ Py_CLEAR(data->x_attr);
}
// dealloc: drop all remaining references and free memory
static void
Xxo_dealloc(PyObject *self)
{
+ // This function must preserve currently raised exception, if any.
+ PyObject *exc = PyErr_GetRaisedException();
+
PyObject_GC_UnTrack(self);
Xxo_finalize(self);
+
PyTypeObject *tp = Py_TYPE(self);
freefunc free = PyType_GetSlot(tp, Py_tp_free);
free(self);
Py_DECREF(tp);
+
+ if (PyErr_Occurred()) {
+ PyErr_WriteUnraisable(NULL);
+ }
+ PyErr_SetRaisedException(exc);
}
// Get an attribute.
static PyObject *
-Xxo_getattro(PyObject *op, PyObject *name)
+Xxo_getattro(PyObject *self, PyObject *name)
{
- XxoObject *self = XxoObject_CAST(op);
- if (self->x_attr != NULL) {
- PyObject *v = PyDict_GetItemWithError(self->x_attr, name);
+ XxoObject_Data *data = Xxo_get_data(self);
+ if (data == NULL) {
+ return 0;
+ }
+
+ PyObject *x_attr;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ x_attr = data->x_attr;
+ Py_END_CRITICAL_SECTION();
+
+ if (x_attr != NULL) {
+ PyObject *v = PyDict_GetItemWithError(x_attr, name);
if (v != NULL) {
return Py_NewRef(v);
}
}
// Fall back to generic implementation (this handles special attributes,
// raising AttributeError, etc.)
- return PyObject_GenericGetAttr(op, name);
+ return PyObject_GenericGetAttr(self, name);
}
// Set or delete an attribute.
static int
-Xxo_setattro(PyObject *op, PyObject *name, PyObject *v)
+Xxo_setattro(PyObject *self, PyObject *name, PyObject *v)
{
- XxoObject *self = XxoObject_CAST(op);
- if (self->x_attr == NULL) {
+ // filter a specific attribute name
+ if (PyUnicode_Check(name) && PyUnicode_EqualToUTF8(name, "reserved")) {
+ PyErr_Format(PyExc_AttributeError, "cannot set %R", name);
+ return -1;
+ }
+
+ XxoObject_Data *data = Xxo_get_data(self);
+ if (data == NULL) {
+ return -1;
+ }
+
+ // If the attribute dict is not created yet, make one.
+ // This needs to be protected by a critical section to avoid another thread
+ // creating a duplicate dict.
+ PyObject *x_attr;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ x_attr = data->x_attr;
+ if (x_attr == NULL) {
// prepare the attribute dict
- self->x_attr = PyDict_New();
- if (self->x_attr == NULL) {
- return -1;
- }
+ data->x_attr = x_attr = PyDict_New();
}
+ Py_END_CRITICAL_SECTION();
+ if (x_attr == NULL) {
+ return -1;
+ }
+
if (v == NULL) {
// delete an attribute
- int rv = PyDict_DelItem(self->x_attr, name);
+ int rv = PyDict_DelItem(x_attr, name);
if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
PyErr_SetString(PyExc_AttributeError,
"delete non-existing Xxo attribute");
}
else {
// set an attribute
- return PyDict_SetItem(self->x_attr, name, v);
+ return PyDict_SetItem(x_attr, name, v);
}
}
*/
static PyObject *
-Xxo_demo(PyObject *op, PyTypeObject *defining_class,
+Xxo_demo(PyObject *self, PyTypeObject *defining_class,
PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
if (kwnames != NULL && PyObject_Length(kwnames)) {
*/
static int
-Xxo_getbuffer(PyObject *op, Py_buffer *view, int flags)
+Xxo_getbuffer(PyObject *self, Py_buffer *view, int flags)
{
- XxoObject *self = XxoObject_CAST(op);
- int res = PyBuffer_FillInfo(view, op,
- (void *)self->x_buffer, BUFSIZE,
+ XxoObject_Data *data = Xxo_get_data(self);
+ if (data == NULL) {
+ return -1;
+ }
+ int res = PyBuffer_FillInfo(view, self,
+ (void *)data->x_buffer, BUFSIZE,
0, flags);
if (res == 0) {
- self->x_exports++;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ data->x_exports++;
+ Py_END_CRITICAL_SECTION();
}
return res;
}
static void
-Xxo_releasebuffer(PyObject *op, Py_buffer *Py_UNUSED(view))
+Xxo_releasebuffer(PyObject *self, Py_buffer *Py_UNUSED(view))
{
- XxoObject *self = XxoObject_CAST(op);
- self->x_exports--;
+ XxoObject_Data *data = Xxo_get_data(self);
+ if (data == NULL) {
+ return;
+ }
+ Py_BEGIN_CRITICAL_SECTION(self);
+ data->x_exports--;
+ Py_END_CRITICAL_SECTION();
}
static PyObject *
-Xxo_get_x_exports(PyObject *op, void *Py_UNUSED(closure))
+Xxo_get_x_exports(PyObject *self, void *Py_UNUSED(closure))
{
- XxoObject *self = XxoObject_CAST(op);
- return PyLong_FromSsize_t(self->x_exports);
+ XxoObject_Data *data = Xxo_get_data(self);
+ if (data == NULL) {
+ return NULL;
+ }
+ Py_ssize_t result;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ result = data->x_exports;
+ Py_END_CRITICAL_SECTION();
+
+ return PyLong_FromSsize_t(result);
}
/* Xxo type definition */
static PyType_Slot Xxo_Type_slots[] = {
{Py_tp_doc, (char *)Xxo_doc},
+ {Py_tp_new, Xxo_new},
{Py_tp_traverse, Xxo_traverse},
{Py_tp_clear, Xxo_clear},
{Py_tp_finalize, Xxo_finalize},
{Py_bf_getbuffer, Xxo_getbuffer},
{Py_bf_releasebuffer, Xxo_releasebuffer},
{Py_tp_getset, Xxo_getsetlist},
+ {Py_tp_token, Py_TP_USE_SPEC},
{0, 0}, /* sentinel */
};
static PyType_Spec Xxo_Type_spec = {
.name = "xxlimited.Xxo",
- .basicsize = sizeof(XxoObject),
- .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .basicsize = -(Py_ssize_t)sizeof(XxoObject_Data),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
.slots = Xxo_Type_slots,
};
}
-/* Function of no arguments returning new Xxo object */
+/* Function of no arguments returning new Xxo object.
+ * Note that a function exposed to Python with METH_NOARGS requires an unused
+ * second argument, so we cannot use newXxoObject directly.
+ */
static PyObject *
xx_new(PyObject *module, PyObject *Py_UNUSED(unused))
{
- XxoObject *rv;
+ xx_state *state = PyModule_GetState(module);
- rv = newXxoObject(module);
- if (rv == NULL)
- return NULL;
- return (PyObject *)rv;
+ return Xxo_new(state->Xxo_Type, NULL, NULL);
}
return -1;
}
- state->Xxo_Type = PyType_FromModuleAndSpec(m, &Xxo_Type_spec, NULL);
+ state->Xxo_Type = (PyTypeObject*)PyType_FromModuleAndSpec(
+ m, &Xxo_Type_spec, NULL);
if (state->Xxo_Type == NULL) {
return -1;
}
- if (PyModule_AddType(m, (PyTypeObject*)state->Xxo_Type) < 0) {
+ if (PyModule_AddType(m, state->Xxo_Type) < 0) {
return -1;
}
// added to the module dict.
// It does not inherit from "object" (PyObject_Type), but from "str"
// (PyUnincode_Type).
- PyObject *Str_Type = PyType_FromModuleAndSpec(
+ PyTypeObject *Str_Type = (PyTypeObject*)PyType_FromModuleAndSpec(
m, &Str_Type_spec, (PyObject *)&PyUnicode_Type);
if (Str_Type == NULL) {
return -1;
}
- if (PyModule_AddType(m, (PyTypeObject*)Str_Type) < 0) {
+ if (PyModule_AddType(m, Str_Type) < 0) {
+ Py_DECREF(Str_Type);
return -1;
}
Py_DECREF(Str_Type);
return 0;
}
-static PyModuleDef_Slot xx_slots[] = {
-
- /* exec function to initialize the module (called as part of import
- * after the object was added to sys.modules)
- */
- {Py_mod_exec, xx_modexec},
-
- /* Signal that this module supports being loaded in multiple interpreters
- * with separate GILs (global interpreter locks).
- * See "Isolating Extension Modules" on how to prepare a module for this:
- * https://docs.python.org/3/howto/isolating-extensions.html
- */
- {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
-
- /* Signal that this module does not rely on the GIL for its own needs.
- * Without this slot, free-threaded builds of CPython will enable
- * the GIL when this module is loaded.
- */
- {Py_mod_gil, Py_MOD_GIL_NOT_USED},
-
- {0, NULL}
-};
-
// Module finalization: modules that hold references in their module state
// need to implement the fullowing GC hooks. They're similar to the ones for
// types (see "Xxo finalization").
static int
xx_traverse(PyObject *module, visitproc visit, void *arg)
{
- xx_state *state = PyModule_GetState(module);
+ xx_state *state = PyModule_GetState_DuringGC(module);
+ if (state == NULL) {
+ return 0;
+ }
Py_VISIT(state->Xxo_Type);
Py_VISIT(state->Error_Type);
return 0;
xx_clear(PyObject *module)
{
xx_state *state = PyModule_GetState(module);
+ if (state == NULL) {
+ return 0;
+ }
Py_CLEAR(state->Xxo_Type);
Py_CLEAR(state->Error_Type);
return 0;
{
// allow xx_modexec to omit calling xx_clear on error
(void)xx_clear((PyObject *)module);
+
+ xx_state *state = PyModule_GetState(module);
+ if (state == NULL) {
+ return;
+ }
}
-static struct PyModuleDef xxmodule = {
- PyModuleDef_HEAD_INIT,
- .m_name = "xxlimited",
- .m_doc = module_doc,
- .m_size = sizeof(xx_state),
- .m_methods = xx_methods,
- .m_slots = xx_slots,
- .m_traverse = xx_traverse,
- .m_clear = xx_clear,
- .m_free = xx_free,
+// Information that CPython uses to prevent loading incompatible extenstions
+PyABIInfo_VAR(abi_info);
+
+static PySlot xx_slots[] = {
+ /* Basic metadata */
+ PySlot_STATIC_DATA(Py_mod_name, "xxlimited"),
+ PySlot_STATIC_DATA(Py_mod_doc, (void*)module_doc),
+ PySlot_DATA(Py_mod_abi, &abi_info),
+
+ /* The method table */
+ PySlot_STATIC_DATA(Py_mod_methods, xx_methods),
+
+ /* exec function to initialize the module (called as part of import
+ * after the object was added to sys.modules)
+ */
+ PySlot_FUNC(Py_mod_exec, xx_modexec),
+
+ /* Module state and associated functions */
+ PySlot_SIZE(Py_mod_state_size, sizeof(xx_state)),
+ PySlot_FUNC(Py_mod_state_traverse, xx_traverse),
+ PySlot_FUNC(Py_mod_state_clear, xx_clear),
+ PySlot_FUNC(Py_mod_state_free, xx_free),
+
+ /* Signal that this module supports being loaded in multiple interpreters
+ * with separate GILs (global interpreter locks).
+ * See "Isolating Extension Modules" on how to prepare a module for this:
+ * https://docs.python.org/3/howto/isolating-extensions.html
+ */
+ PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
+
+ /* Signal that this module does not rely on the GIL for its own needs.
+ * Without this slot, free-threaded builds of CPython will enable
+ * the GIL when this module is loaded.
+ */
+ PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
+
+ PySlot_END
};
-/* Export function for the module. *Must* be called PyInit_xx; usually it is
- * the only non-`static` object in a module definition.
+/* Export function for the module. *Must* be called PyModExport_xx; usually
+ * it is the only non-`static` object in a module definition.
*/
-PyMODINIT_FUNC
-PyInit_xxlimited(void)
+PyMODEXPORT_FUNC
+PyModExport_xxlimited(void)
{
- return PyModuleDef_Init(&xxmodule);
+ return xx_slots;
}
static PyModuleDef_Slot xx_slots[] = {
{Py_mod_exec, xx_modexec},
#ifdef Py_GIL_DISABLED
- // These definitions are in the limited API, but not until 3.13.
+ // In a free-threaded build, we don't use Limited API.
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
#endif
{0, NULL}
--- /dev/null
+/* Use this file as a template to start implementing a module that
+ also declares object types. All occurrences of 'Xxo' should be changed
+ to something reasonable for your objects. After that, all other
+ occurrences of 'xx' should be changed to something reasonable for your
+ module. If your module is named foo your source file should be named
+ foo.c or foomodule.c.
+
+ You will probably want to delete all references to 'x_attr' and add
+ your own types of attributes instead. Maybe you want to name your
+ local variables other than 'self'. If your object type is needed in
+ other files, you'll have to create a file "foobarobject.h"; see
+ floatobject.h for an example.
+
+ This module roughly corresponds to::
+
+ class Xxo:
+ """A class that explicitly stores attributes in an internal dict
+ (to simulate custom attribute handling).
+ """
+
+ def __init__(self):
+ # In the C class, "_x_attr" is not accessible from Python code
+ self._x_attr = {}
+ self._x_exports = 0
+
+ def __getattr__(self, name):
+ return self._x_attr[name]
+
+ def __setattr__(self, name, value):
+ self._x_attr[name] = value
+
+ def __delattr__(self, name):
+ del self._x_attr[name]
+
+ @property
+ def x_exports(self):
+ """Return the number of times an internal buffer is exported."""
+ # Each Xxo instance has a 10-byte buffer that can be
+ # accessed via the buffer interface (e.g. `memoryview`).
+ return self._x_exports
+
+ def demo(o, /):
+ if isinstance(o, str):
+ return o
+ elif isinstance(o, Xxo):
+ return o
+ else:
+ raise Error('argument must be str or Xxo')
+
+ class Error(Exception):
+ """Exception raised by the xxlimited module"""
+
+ def foo(i: int, j: int, /):
+ """Return the sum of i and j."""
+ # Unlike this pseudocode, the C function will *only* work with
+ # integers and perform C long int arithmetic
+ return i + j
+
+ def new():
+ return Xxo()
+
+ def Str(str):
+ # A trivial subclass of a built-in type
+ pass
+ */
+
+// Need limited C API version 3.13 for Py_mod_gil
+#include "pyconfig.h" // Py_GIL_DISABLED
+#ifndef Py_GIL_DISABLED
+# define Py_LIMITED_API 0x030d0000
+#endif
+
+#include "Python.h"
+#include <string.h>
+
+#define BUFSIZE 10
+
+// Module state
+typedef struct {
+ PyObject *Xxo_Type; // Xxo class
+ PyObject *Error_Type; // Error class
+} xx_state;
+
+
+/* Xxo objects */
+
+// Instance state
+typedef struct {
+ PyObject_HEAD
+ PyObject *x_attr; /* Attributes dictionary.
+ * May be NULL, which acts as an
+ * empty dict.
+ */
+ char x_buffer[BUFSIZE]; /* buffer for Py_buffer */
+ Py_ssize_t x_exports; /* how many buffer are exported */
+} XxoObject;
+
+#define XxoObject_CAST(op) ((XxoObject *)(op))
+// TODO: full support for type-checking was added in 3.14 (Py_tp_token)
+// #define XxoObject_Check(v) Py_IS_TYPE(v, Xxo_Type)
+
+static XxoObject *
+newXxoObject(PyObject *module)
+{
+ xx_state *state = PyModule_GetState(module);
+ if (state == NULL) {
+ return NULL;
+ }
+ XxoObject *self;
+ self = PyObject_GC_New(XxoObject, (PyTypeObject*)state->Xxo_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+ self->x_attr = NULL;
+ memset(self->x_buffer, 0, BUFSIZE);
+ self->x_exports = 0;
+ return self;
+}
+
+/* Xxo finalization.
+ *
+ * Types that store references to other PyObjects generally need to implement
+ * the GC slots: traverse, clear, dealloc, and (optionally) finalize.
+ */
+
+// traverse: Visit all references from an object, including its type
+static int
+Xxo_traverse(PyObject *op, visitproc visit, void *arg)
+{
+ // Visit the type
+ Py_VISIT(Py_TYPE(op));
+
+ // Visit the attribute dict
+ XxoObject *self = XxoObject_CAST(op);
+ Py_VISIT(self->x_attr);
+ return 0;
+}
+
+// clear: drop references in order to break all reference cycles
+static int
+Xxo_clear(PyObject *op)
+{
+ XxoObject *self = XxoObject_CAST(op);
+ Py_CLEAR(self->x_attr);
+ return 0;
+}
+
+// finalize: like clear, but should leave the object in a consistent state.
+// Equivalent to `__del__` in Python.
+static void
+Xxo_finalize(PyObject *op)
+{
+ XxoObject *self = XxoObject_CAST(op);
+ Py_CLEAR(self->x_attr);
+}
+
+// dealloc: drop all remaining references and free memory
+static void
+Xxo_dealloc(PyObject *self)
+{
+ PyObject_GC_UnTrack(self);
+ Xxo_finalize(self);
+ PyTypeObject *tp = Py_TYPE(self);
+ freefunc free = PyType_GetSlot(tp, Py_tp_free);
+ free(self);
+ Py_DECREF(tp);
+}
+
+
+/* Xxo attribute handling */
+
+// Get an attribute.
+static PyObject *
+Xxo_getattro(PyObject *op, PyObject *name)
+{
+ XxoObject *self = XxoObject_CAST(op);
+ if (self->x_attr != NULL) {
+ PyObject *v = PyDict_GetItemWithError(self->x_attr, name);
+ if (v != NULL) {
+ return Py_NewRef(v);
+ }
+ else if (PyErr_Occurred()) {
+ return NULL;
+ }
+ }
+ // Fall back to generic implementation (this handles special attributes,
+ // raising AttributeError, etc.)
+ return PyObject_GenericGetAttr(op, name);
+}
+
+// Set or delete an attribute.
+static int
+Xxo_setattro(PyObject *op, PyObject *name, PyObject *v)
+{
+ XxoObject *self = XxoObject_CAST(op);
+ if (self->x_attr == NULL) {
+ // prepare the attribute dict
+ self->x_attr = PyDict_New();
+ if (self->x_attr == NULL) {
+ return -1;
+ }
+ }
+ if (v == NULL) {
+ // delete an attribute
+ int rv = PyDict_DelItem(self->x_attr, name);
+ if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
+ PyErr_SetString(PyExc_AttributeError,
+ "delete non-existing Xxo attribute");
+ return -1;
+ }
+ return rv;
+ }
+ else {
+ // set an attribute
+ return PyDict_SetItem(self->x_attr, name, v);
+ }
+}
+
+/* Xxo methods: C functions plus a PyMethodDef array that lists them and
+ * specifies metadata.
+ */
+
+static PyObject *
+Xxo_demo(PyObject *op, PyTypeObject *defining_class,
+ PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ if (kwnames != NULL && PyObject_Length(kwnames)) {
+ PyErr_SetString(PyExc_TypeError, "demo() takes no keyword arguments");
+ return NULL;
+ }
+ if (nargs != 1) {
+ PyErr_SetString(PyExc_TypeError, "demo() takes exactly 1 argument");
+ return NULL;
+ }
+
+ PyObject *o = args[0];
+
+ /* Test if the argument is "str" */
+ if (PyUnicode_Check(o)) {
+ return Py_NewRef(o);
+ }
+
+ /* test if the argument is of the Xxo class */
+ if (PyObject_TypeCheck(o, defining_class)) {
+ return Py_NewRef(o);
+ }
+
+ return Py_NewRef(Py_None);
+}
+
+static PyMethodDef Xxo_methods[] = {
+ {"demo", _PyCFunction_CAST(Xxo_demo),
+ METH_METHOD | METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("demo(o) -> o")},
+ {NULL, NULL} /* sentinel */
+};
+
+/* Xxo buffer interface: C functions later referenced from PyType_Slot array.
+ * Other interfaces (e.g. for sequence-like or number-like types) are defined
+ * similarly.
+ */
+
+static int
+Xxo_getbuffer(PyObject *op, Py_buffer *view, int flags)
+{
+ XxoObject *self = XxoObject_CAST(op);
+ int res = PyBuffer_FillInfo(view, op,
+ (void *)self->x_buffer, BUFSIZE,
+ 0, flags);
+ if (res == 0) {
+ self->x_exports++;
+ }
+ return res;
+}
+
+static void
+Xxo_releasebuffer(PyObject *op, Py_buffer *Py_UNUSED(view))
+{
+ XxoObject *self = XxoObject_CAST(op);
+ self->x_exports--;
+}
+
+static PyObject *
+Xxo_get_x_exports(PyObject *op, void *Py_UNUSED(closure))
+{
+ XxoObject *self = XxoObject_CAST(op);
+ return PyLong_FromSsize_t(self->x_exports);
+}
+
+/* Xxo type definition */
+
+PyDoc_STRVAR(Xxo_doc,
+ "A class that explicitly stores attributes in an internal dict");
+
+static PyGetSetDef Xxo_getsetlist[] = {
+ {"x_exports", Xxo_get_x_exports, NULL, NULL},
+ {NULL},
+};
+
+
+static PyType_Slot Xxo_Type_slots[] = {
+ {Py_tp_doc, (char *)Xxo_doc},
+ {Py_tp_traverse, Xxo_traverse},
+ {Py_tp_clear, Xxo_clear},
+ {Py_tp_finalize, Xxo_finalize},
+ {Py_tp_dealloc, Xxo_dealloc},
+ {Py_tp_getattro, Xxo_getattro},
+ {Py_tp_setattro, Xxo_setattro},
+ {Py_tp_methods, Xxo_methods},
+ {Py_bf_getbuffer, Xxo_getbuffer},
+ {Py_bf_releasebuffer, Xxo_releasebuffer},
+ {Py_tp_getset, Xxo_getsetlist},
+ {0, 0}, /* sentinel */
+};
+
+static PyType_Spec Xxo_Type_spec = {
+ .name = "xxlimited_3_13.Xxo",
+ .basicsize = sizeof(XxoObject),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .slots = Xxo_Type_slots,
+};
+
+
+/* Str type definition*/
+
+static PyType_Slot Str_Type_slots[] = {
+ // slots array intentionally kept empty
+ {0, 0}, /* sentinel */
+};
+
+static PyType_Spec Str_Type_spec = {
+ .name = "xxlimited_3_13.Str",
+ .basicsize = 0,
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .slots = Str_Type_slots,
+};
+
+
+/* Function of two integers returning integer (with C "long int" arithmetic) */
+
+PyDoc_STRVAR(xx_foo_doc,
+"foo(i,j)\n\
+\n\
+Return the sum of i and j.");
+
+static PyObject *
+xx_foo(PyObject *module, PyObject *args)
+{
+ long i, j;
+ long res;
+ if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
+ return NULL;
+ res = i+j; /* XXX Do something here */
+ return PyLong_FromLong(res);
+}
+
+
+/* Function of no arguments returning new Xxo object */
+
+static PyObject *
+xx_new(PyObject *module, PyObject *Py_UNUSED(unused))
+{
+ XxoObject *rv;
+
+ rv = newXxoObject(module);
+ if (rv == NULL)
+ return NULL;
+ return (PyObject *)rv;
+}
+
+
+
+/* List of functions defined in the module */
+
+static PyMethodDef xx_methods[] = {
+ {"foo", xx_foo, METH_VARARGS,
+ xx_foo_doc},
+ {"new", xx_new, METH_NOARGS,
+ PyDoc_STR("new() -> new Xx object")},
+ {NULL, NULL} /* sentinel */
+};
+
+
+/* The module itself */
+
+PyDoc_STRVAR(module_doc,
+"This is a template module just for instruction.");
+
+static int
+xx_modexec(PyObject *m)
+{
+ xx_state *state = PyModule_GetState(m);
+
+ state->Error_Type = PyErr_NewException("xxlimited_3_13.Error", NULL, NULL);
+ if (state->Error_Type == NULL) {
+ return -1;
+ }
+ if (PyModule_AddType(m, (PyTypeObject*)state->Error_Type) < 0) {
+ return -1;
+ }
+
+ state->Xxo_Type = PyType_FromModuleAndSpec(m, &Xxo_Type_spec, NULL);
+ if (state->Xxo_Type == NULL) {
+ return -1;
+ }
+ if (PyModule_AddType(m, (PyTypeObject*)state->Xxo_Type) < 0) {
+ return -1;
+ }
+
+ // Add the Str type. It is not needed from C code, so it is only
+ // added to the module dict.
+ // It does not inherit from "object" (PyObject_Type), but from "str"
+ // (PyUnincode_Type).
+ PyObject *Str_Type = PyType_FromModuleAndSpec(
+ m, &Str_Type_spec, (PyObject *)&PyUnicode_Type);
+ if (Str_Type == NULL) {
+ return -1;
+ }
+ if (PyModule_AddType(m, (PyTypeObject*)Str_Type) < 0) {
+ return -1;
+ }
+ Py_DECREF(Str_Type);
+
+ return 0;
+}
+
+static PyModuleDef_Slot xx_slots[] = {
+
+ /* exec function to initialize the module (called as part of import
+ * after the object was added to sys.modules)
+ */
+ {Py_mod_exec, xx_modexec},
+
+ /* Signal that this module supports being loaded in multiple interpreters
+ * with separate GILs (global interpreter locks).
+ * See "Isolating Extension Modules" on how to prepare a module for this:
+ * https://docs.python.org/3/howto/isolating-extensions.html
+ */
+ {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
+
+ /* Signal that this module does not rely on the GIL for its own needs.
+ * Without this slot, free-threaded builds of CPython will enable
+ * the GIL when this module is loaded.
+ */
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+
+ {0, NULL}
+};
+
+// Module finalization: modules that hold references in their module state
+// need to implement the fullowing GC hooks. They're similar to the ones for
+// types (see "Xxo finalization").
+
+static int
+xx_traverse(PyObject *module, visitproc visit, void *arg)
+{
+ xx_state *state = PyModule_GetState(module);
+ Py_VISIT(state->Xxo_Type);
+ Py_VISIT(state->Error_Type);
+ return 0;
+}
+
+static int
+xx_clear(PyObject *module)
+{
+ xx_state *state = PyModule_GetState(module);
+ Py_CLEAR(state->Xxo_Type);
+ Py_CLEAR(state->Error_Type);
+ return 0;
+}
+
+static void
+xx_free(void *module)
+{
+ // allow xx_modexec to omit calling xx_clear on error
+ (void)xx_clear((PyObject *)module);
+}
+
+static struct PyModuleDef xxmodule = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "xxlimited_3_13",
+ .m_doc = module_doc,
+ .m_size = sizeof(xx_state),
+ .m_methods = xx_methods,
+ .m_slots = xx_slots,
+ .m_traverse = xx_traverse,
+ .m_clear = xx_clear,
+ .m_free = xx_free,
+};
+
+
+/* Export function for the module. *Must* be called PyInit_xx; usually it is
+ * the only non-`static` object in a module definition.
+ */
+
+PyMODINIT_FUNC
+PyInit_xxlimited_3_13(void)
+{
+ return PyModuleDef_Init(&xxmodule);
+}
from .support.pymanager import *
from .support.nuspec import *
-TEST_PYDS_ONLY = FileStemSet("xxlimited", "xxlimited_35", "_ctypes_test", "_test*")
+TEST_PYDS_ONLY = FileStemSet("xxlimited", "xxlimited_3_13", "xxlimited_35",
+ "_ctypes_test", "_test*")
TEST_DLLS_ONLY = set()
TEST_DIRS_ONLY = FileNameSet("test", "tests")
<TestModules Include="_ctypes_test;_testbuffer;_testcapi;_testlimitedcapi;_testinternalcapi;_testembed;_testimportmultiple;_testmultiphase;_testsinglephase;_testconsole;_testclinic;_testclinic_limited" />
<TestModules Include="xxlimited" Condition="'$(Configuration)' == 'Release'" />
<TestModules Include="xxlimited_35" Condition="'$(Configuration)' == 'Release'" />
+ <TestModules Include="xxlimited_3_13" Condition="'$(Configuration)' == 'Release'" />
<Projects Include="@(TestModules->'%(Identity).vcxproj')" Condition="$(IncludeTests)">
<!-- Disable parallel build for test modules -->
<BuildInParallel>false</BuildInParallel>
builds an example module that makes use of the PEP 384 Stable ABI,
see Modules\xxlimited.c
xxlimited_35
- ditto for testing the Python 3.5 stable ABI, see
- Modules\xxlimited_35.c
+xxlimited_3_13
+ ditto for testing older Limited API, see
+ Modules\xxlimited_*.c
The following sub-projects are for individual modules of the standard
library which are implemented in C; each one builds a DLL (renamed to
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|ARM">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|ARM64">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="PGInstrument|ARM">
+ <Configuration>PGInstrument</Configuration>
+ <Platform>ARM</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="PGInstrument|ARM64">
+ <Configuration>PGInstrument</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="PGInstrument|Win32">
+ <Configuration>PGInstrument</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="PGInstrument|x64">
+ <Configuration>PGInstrument</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="PGUpdate|ARM">
+ <Configuration>PGUpdate</Configuration>
+ <Platform>ARM</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="PGUpdate|ARM64">
+ <Configuration>PGUpdate</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="PGUpdate|Win32">
+ <Configuration>PGUpdate</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="PGUpdate|x64">
+ <Configuration>PGUpdate</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|ARM">
+ <Configuration>Release</Configuration>
+ <Platform>ARM</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|ARM64">
+ <Configuration>Release</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{fb868ea7-f93a-4d9b-be78-ca4e9ba14fff}</ProjectGuid>
+ <RootNamespace>xxlimited_3_13</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ </PropertyGroup>
+ <Import Project="python.props" />
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>NotSet</CharacterSet>
+ <SupportPGO>false</SupportPGO>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <TargetExt>$(PyStdlibPydExt)</TargetExt>
+ </PropertyGroup>
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="pyproject.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ <Link>
+ <AdditionalDependencies>wsock32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\Modules\xxlimited_3_13.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="python3dll.vcxproj">
+ <Project>{885d4898-d08d-4091-9c40-c700cfe3fc5a}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{5be27194-6530-452d-8d86-3767b991fa83}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\Modules\xxlimited_3_13.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
'test',
'xxlimited',
'xxlimited_35',
+ 'xxlimited_3_13',
'xxsubtype',
}
'_testclinic_limited.c',
'xxlimited.c',
'xxlimited_35.c',
+ 'xxlimited_3_13.c',
))
# C files in the fhe following directories must not be built with
'PyStructSequence_Field[]',
'PyStructSequence_Desc',
'PyABIInfo',
+ 'PySlot[]',
}
# XXX We should normalize all cases to a single name,
JIT_SHIM_BUILD_O
JIT_SHIM_O
JIT_STENCILS_H
+MODULE_XXLIMITED_3_13_FALSE
+MODULE_XXLIMITED_3_13_TRUE
MODULE_XXLIMITED_35_FALSE
MODULE_XXLIMITED_35_TRUE
MODULE_XXLIMITED_FALSE
py_cv_module_termios=n/a
py_cv_module_xxlimited=n/a
py_cv_module_xxlimited_35=n/a
+ py_cv_module_xxlimited_3_13=n/a
py_cv_module_=n/a
;; #(
printf "%s\n" "$py_cv_module_xxlimited_35" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdlib extension module xxlimited_3_13" >&5
+printf %s "checking for stdlib extension module xxlimited_3_13... " >&6; }
+ if test "$py_cv_module_xxlimited_3_13" != "n/a"
+then :
+
+ if test "$TEST_MODULES" = yes
+then :
+ if test "$ac_cv_func_dlopen" = yes
+then :
+ py_cv_module_xxlimited_3_13=yes
+else case e in #(
+ e) py_cv_module_xxlimited_3_13=missing ;;
+esac
+fi
+else case e in #(
+ e) py_cv_module_xxlimited_3_13=disabled ;;
+esac
+fi
+
+fi
+ as_fn_append MODULE_BLOCK "MODULE_XXLIMITED_3_13_STATE=$py_cv_module_xxlimited_3_13$as_nl"
+ if test "x$py_cv_module_xxlimited_3_13" = xyes
+then :
+
+
+
+
+fi
+ if test "$py_cv_module_xxlimited_3_13" = yes; then
+ MODULE_XXLIMITED_3_13_TRUE=
+ MODULE_XXLIMITED_3_13_FALSE='#'
+else
+ MODULE_XXLIMITED_3_13_TRUE='#'
+ MODULE_XXLIMITED_3_13_FALSE=
+fi
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $py_cv_module_xxlimited_3_13" >&5
+printf "%s\n" "$py_cv_module_xxlimited_3_13" >&6; }
+
+
# Determine JIT stencils header files based on target platform
JIT_STENCILS_H=""
JIT_SHIM_O=""
as_fn_error $? "conditional \"MODULE_XXLIMITED_35\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${MODULE_XXLIMITED_3_13_TRUE}" && test -z "${MODULE_XXLIMITED_3_13_FALSE}"; then
+ as_fn_error $? "conditional \"MODULE_XXLIMITED_3_13\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
: "${CONFIG_STATUS=./config.status}"
ac_write_fail=0
[termios],
[xxlimited],
[xxlimited_35],
+ [xxlimited_3_13],
)
],
[PY_STDLIB_MOD_SET_NA([_scproxy])]
dnl Emscripten does not support shared libraries yet.
PY_STDLIB_MOD([xxlimited], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes])
PY_STDLIB_MOD([xxlimited_35], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes])
+PY_STDLIB_MOD([xxlimited_3_13], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes])
# Determine JIT stencils header files based on target platform
JIT_STENCILS_H=""