Skip to content

Commit 8346ec8

Browse files
pythonCAPI stub routines fully tested on Windows.
1 parent 84cbd24 commit 8346ec8

File tree

3 files changed

+80
-37
lines changed

3 files changed

+80
-37
lines changed

pythonBuffer.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
catch (...) \
3434
{ \
3535
PyErr_SetString(PyExc_RuntimeError, "Unknown exception"); \
36-
return ret; \
36+
return ret; \
3737
}
3838

3939
namespace classdesc
@@ -212,7 +212,7 @@ namespace classdesc
212212
get() const {return PyFloat_AsDouble(pyObject);}
213213
// template <> bool get<bool>() const {return PyLong_AsLong(pyObject);}
214214
template <class T> typename enable_if<is_string<T>, T>::T
215-
get() const {return PyUnicode_AsUTF8(pyObject);}
215+
get() const {return PyUnicode_AsUTF8AndSize(pyObject,nullptr);}
216216

217217
template <class T> typename enable_if<is_same<T,json_pack_t>, T>::T
218218
get() const {
@@ -245,7 +245,7 @@ namespace classdesc
245245
{
246246
PyObjectRef keyValue=PySequence_GetItem(keyValues, i);
247247
PyObjectRef keyRef(PyObject_Str(PySequence_GetItem(keyValue,0)));
248-
string keyStr=PyUnicode_AsUTF8(keyRef);
248+
string keyStr=PyUnicode_AsUTF8AndSize(keyRef,nullptr);
249249
obj[keyStr]=PythonBuffer(PySequence_GetItem(keyValue,1)).get<json_pack_t>();
250250
}
251251
}
@@ -256,7 +256,7 @@ namespace classdesc
256256
{
257257
auto key=PySequence_GetItem(pyObject, i);
258258
PyObjectRef keyRef(PyObject_Str(key));
259-
string keyStr=PyUnicode_AsUTF8(keyRef);
259+
string keyStr=PyUnicode_AsUTF8AndSize(keyRef,nullptr);
260260
obj[keyStr]=PythonBuffer(PyObject_GetAttr(pyObject, key)).get<json_pack_t>();
261261
}
262262
}
@@ -281,7 +281,7 @@ namespace classdesc
281281

282282
std::string str() const {
283283
PyObjectRef pyStr(PyObject_Str(pyObject));
284-
return PyUnicode_AsUTF8(pyStr);
284+
return PyUnicode_AsUTF8AndSize(pyStr,nullptr);
285285
}
286286
private:
287287
PyObject* pyObject=Py_None; // note - this needs to be INCREF'd in constructors, not immortal before 3.12
@@ -568,7 +568,7 @@ namespace classdesc
568568
try
569569
{
570570
auto cppWrapper=static_cast<CppWrapper*>(self);
571-
auto i=cppWrapper->methods.find(PyUnicode_AsUTF8(attr));
571+
auto i=cppWrapper->methods.find(PyUnicode_AsUTF8AndSize(attr,nullptr));
572572
if (i!=cppWrapper->methods.end())
573573
{
574574
Py_INCREF(i->second);
@@ -577,7 +577,7 @@ namespace classdesc
577577
else
578578
{
579579
auto methods=cppWrapper->command->list();
580-
auto attribute=methods.find(string(".")+PyUnicode_AsUTF8(attr));
580+
auto attribute=methods.find(string(".")+PyUnicode_AsUTF8AndSize(attr,nullptr));
581581
if (attribute!=methods.end())
582582
return CppWrapper::create(attribute->second, false);
583583
}
@@ -588,7 +588,7 @@ namespace classdesc
588588
static int setAttro(PyObject* self, PyObject* name, PyObject* attr)
589589
{
590590
auto cppWrapper=static_cast<CppWrapper*>(self);
591-
auto key=PyUnicode_AsUTF8(name);
591+
auto key=PyUnicode_AsUTF8AndSize(name,nullptr);
592592
if (attr)
593593
{
594594
cppWrapper->methods[key]=PyObjectRef(attr);
@@ -756,7 +756,7 @@ namespace classdesc
756756
PyErr_SetString(PyExc_RuntimeError, "First argument not a CppWrapper");
757757
return nullptr;
758758
}
759-
auto name=PyUnicode_AsUTF8(PySequence_GetItem(args,1));
759+
auto name=PyUnicode_AsUTF8AndSize(PySequence_GetItem(args,1),nullptr);
760760
if (!name) {
761761
PyErr_SetString(PyExc_RuntimeError, "Second argument not a string");
762762
return nullptr;
@@ -804,10 +804,10 @@ namespace {
804804
nullptr, \
805805
nullptr \
806806
}; \
807-
\
808807
using namespace classdesc; \
809808
registries()[#name]=&registry; \
810809
pythonModule=PyModule_Create(&module_##name); \
810+
if (PyErr_Occurred()) PyErr_Print(); \
811811
if (pythonModule) initModule(pythonModule,registry); \
812812
return pythonModule; \
813813
}

pythonCAPI.cc

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,55 @@
1212
#include <windows.h>
1313
#include <iostream>
1414

15-
static HINSTANCE pythonExe=GetModuleHandle(nullptr);
15+
static HINSTANCE pythonExe=GetModuleHandle("python3");
16+
17+
// On Windows, python3 refers to the stable ABI. checkSymbol provides
18+
// an important check that all symbols referenced here are available
19+
// in the stable ABI. This check should be performed prior to release.
20+
static void checkSymbol(const char* name)
21+
{
22+
auto symbol=GetProcAddress(pythonExe, name);
23+
if (!symbol)
24+
std::cerr<<name<<" not found in module"<<std::endl;
25+
}
26+
#define CHECK_SYMBOL(name) static int checkSymbol_##name=(checkSymbol(#name),1);
1627

1728
extern "C"
1829
{
1930

20-
#define APIFN(return,name,arg_decls,args) \
21-
return name arg_decls \
22-
{ \
31+
#define APIFN(returnType,name,arg_decls,args) \
32+
CHECK_SYMBOL(name) \
33+
returnType name arg_decls \
34+
{ \
35+
if (!pythonExe) puts("python exe not found"); \
2336
static auto symbol=(decltype(name)*)GetProcAddress(pythonExe, #name); \
24-
return symbol? symbol args: 0; \
25-
} \
37+
if (!symbol) puts("failed to load: "#name); \
38+
return symbol? symbol args: 0; \
39+
}
2640

2741
#define VOID_APIFN(name,arg_decls,args) \
42+
CHECK_SYMBOL(name) \
2843
void name arg_decls \
2944
{ \
3045
static auto symbol=(decltype(name)*)GetProcAddress(pythonExe, #name); \
46+
if (!symbol) puts("failed to load: "#name); \
3147
if (symbol) symbol args; \
32-
} \
48+
}
49+
50+
#define APIVARPTR(type, name) \
51+
type* name=(type*)GetProcAddress(pythonExe, #name); \
52+
CHECK_SYMBOL(name)
53+
54+
APIVARPTR(PyTypeObject, PyBool_Type);
55+
APIVARPTR(PyTypeObject, PyFloat_Type);
56+
APIVARPTR(PyObject, PyExc_RuntimeError);
3357

3458
VOID_APIFN(_Py_Dealloc,(PyObject*o),(o));
3559
APIFN(PyObject*, PyErr_Occurred, (), ());
36-
VOID_APIFN(PyErr_Print,()());
60+
VOID_APIFN(PyErr_Print,(),());
3761
VOID_APIFN(PyErr_SetString,(PyObject* o,const char* s),(o,s));
3862

63+
APIFN(PyObject*, Py_GetConstantBorrowed,(unsigned x),(x));
3964
APIFN(int, PyType_IsSubtype,(PyTypeObject* o1, PyTypeObject* o2),(o1,o2));
4065
APIFN(unsigned long, PyType_GetFlags,(PyTypeObject* o),(o));
4166
APIFN(PyObject*, PyLong_FromLong,(long x),(x));
@@ -58,7 +83,7 @@ extern "C"
5883
APIFN(PyObject*, PySequence_GetItem, (PyObject* o, ssize_t i), (o,i));
5984

6085
APIFN(PyObject*, PyUnicode_FromString, (const char* s), (s));
61-
APIFN(char*, PyUnicode_AsUTF8, (PyObject* s), (s));
86+
APIFN(char*, PyUnicode_AsUTF8AndSize, (PyObject* s,Py_ssize_t* sz), (s,sz));
6287

6388
APIFN(PyObject*, PyDict_New, (), ());
6489
APIFN(int, PyDict_SetItemString, (PyObject* d, const char* k, PyObject* v), (d,k,v));

pythonCAPI.h

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,28 @@
99
// Declares functions usded from the Python C API
1010
#ifndef CLASSDESC_PYTHON_CAPI_H
1111
#define CLASSDESC_PYTHON_CAPI_H
12+
#include <cstddef>
13+
#include <stdio.h>
1214

1315
// TODO - is this field used on Windows?
1416
//#define _PyObject_HEAD_EXTRA
1517

1618
#define PYTHON_API_VERSION 1013
19+
20+
// Windows uses stable API, so the *Struct symbols are not available
21+
#ifdef _WIN32
22+
#define Py_False Py_GetConstantBorrowed(1)
23+
#define Py_True Py_GetConstantBorrowed(2)
24+
#define Py_None Py_GetConstantBorrowed(0)
25+
#else
1726
#define Py_False ((PyObject *) &_Py_FalseStruct)
1827
#define Py_True ((PyObject *) &_Py_TrueStruct)
1928
#define Py_None ((PyObject *) &_Py_NoneStruct)
29+
#endif
2030
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
2131
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
2232
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
33+
2334
#define METH_VARARGS 0x0001
2435
#define METH_NOARGS 0x0004
2536
#define METH_O 0x0008
@@ -29,11 +40,11 @@
2940
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
3041
#define PyObject_TypeCheck(ob, tp) \
3142
(Py_TYPE(ob) == (tp) || PyType_IsSubtype(Py_TYPE(ob), (tp)))
32-
#define PyBool_Check(x) (Py_TYPE(x) == &PyBool_Type)
43+
#define PyBool_Check(x) (Py_TYPE(x) == PyBool_Type)
44+
#define PyFloat_Check(op) PyObject_TypeCheck(op, PyFloat_Type)
3345
#define PyLong_Check(op) PyType_FastSubclass(Py_TYPE(op), 1UL << 24)
34-
#define PyFloat_Check(op) PyObject_TypeCheck(op, &PyFloat_Type)
35-
#define PyUnicode_Check(op) PyType_FastSubclass(Py_TYPE(op), 1UL << 28)
3646
#define PyList_Check(op) PyType_FastSubclass(Py_TYPE(op), 1UL << 25)
47+
#define PyUnicode_Check(op) PyType_FastSubclass(Py_TYPE(op), 1UL << 28)
3748

3849
#define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt)
3950
#define Py_INCREF(op) (((PyObject *)(op))->ob_refcnt++)
@@ -77,24 +88,27 @@
7788
#define PyModule_Create(module) \
7889
PyModule_Create2(module, PYTHON_API_VERSION)
7990

91+
// Windows in particular doesn't define ssize_t
92+
using Py_ssize_t=std::ptrdiff_t;
93+
8094
struct PyTypeObject;
8195

8296
struct PyObject
8397
{
8498
//_PyObject_HEAD_EXTRA
85-
ssize_t ob_refcnt;
99+
Py_ssize_t ob_refcnt;
86100
PyTypeObject *ob_type;
87101
};
88102

89103
struct PyVarObject
90104
{
91105
PyObject ob_base;
92-
ssize_t ob_size; /* Number of items in variable part */
106+
Py_ssize_t ob_size; /* Number of items in variable part */
93107
};
94108

95109
typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
96110
typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
97-
typedef ssize_t (*lenfunc)(PyObject *);
111+
typedef Py_ssize_t (*lenfunc)(PyObject *);
98112
typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *);
99113
typedef void (*freefunc)(void *);
100114
typedef void (*destructor)(PyObject *);
@@ -104,15 +118,15 @@ typedef PyObject *(*getattrofunc)(PyObject *, PyObject *);
104118
typedef int (*setattrfunc)(PyObject *, char *, PyObject *);
105119
typedef int (*setattrofunc)(PyObject *, PyObject *, PyObject *);
106120
typedef PyObject *(*reprfunc)(PyObject *);
107-
typedef ssize_t (*hashfunc)(PyObject *);
121+
typedef Py_ssize_t (*hashfunc)(PyObject *);
108122
typedef PyObject *(*richcmpfunc) (PyObject *, PyObject *, int);
109123
typedef PyObject *(*getiterfunc) (PyObject *);
110124
typedef PyObject *(*iternextfunc) (PyObject *);
111125
typedef PyObject *(*descrgetfunc) (PyObject *, PyObject *, PyObject *);
112126
typedef int (*descrsetfunc) (PyObject *, PyObject *, PyObject *);
113127
typedef int (*initproc)(PyObject *, PyObject *, PyObject *);
114128
typedef PyObject *(*newfunc)(struct _typeobject *, PyObject *, PyObject *);
115-
typedef PyObject *(*allocfunc)(struct _typeobject *, ssize_t);
129+
typedef PyObject *(*allocfunc)(struct _typeobject *, Py_ssize_t);
116130
typedef int (*visitproc)(PyObject *, void *);
117131
typedef int (*traverseproc)(PyObject *, visitproc, void *);
118132
typedef int (*inquiry)(PyObject *);
@@ -129,7 +143,7 @@ struct PyTypeObject
129143
{
130144
PyVarObject ob_base;
131145
const char *tp_name; /* For printing, in format "<module>.<name>" */
132-
ssize_t tp_basicsize, tp_itemsize; /* For allocation */
146+
Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
133147

134148
/* Methods to implement standard operations */
135149

@@ -175,7 +189,7 @@ struct PyTypeObject
175189
richcmpfunc tp_richcompare;
176190

177191
/* weak reference enabler */
178-
ssize_t tp_weaklistoffset;
192+
Py_ssize_t tp_weaklistoffset;
179193

180194
/* Iterators */
181195
getiterfunc tp_iter;
@@ -189,7 +203,7 @@ struct PyTypeObject
189203
PyObject *tp_dict;
190204
descrgetfunc tp_descr_get;
191205
descrsetfunc tp_descr_set;
192-
ssize_t tp_dictoffset;
206+
Py_ssize_t tp_dictoffset;
193207
initproc tp_init;
194208
allocfunc tp_alloc;
195209
newfunc tp_new;
@@ -226,36 +240,40 @@ struct PyMappingMethods {
226240
struct PyModuleDef_Base {
227241
PyObject ob_base;
228242
PyObject* (*m_init)(void);
229-
ssize_t m_index;
243+
Py_ssize_t m_index;
230244
PyObject* m_copy;
231245
};
232246

233247
struct PyModuleDef {
234248
PyModuleDef_Base m_base;
235249
const char* m_name;
236250
const char* m_doc;
237-
ssize_t m_size;
251+
Py_ssize_t m_size;
238252
PyMethodDef *m_methods;
239253
struct PyModuleDef_Slot* m_slots;
240254
traverseproc m_traverse;
241255
inquiry m_clear;
242256
freefunc m_free;
243257
};
244258

259+
#ifndef _WIN32
245260
extern PyObject _Py_FalseStruct, _Py_TrueStruct, _Py_NoneStruct;
246-
extern PyTypeObject PyBool_Type, PyFloat_Type;
247-
extern PyObject* PyExc_RuntimeError;
248-
249-
using Py_ssize_t=ssize_t;
261+
#endif
250262

251263
extern "C" {
252264
void _Py_Dealloc(PyObject*);
253265
PyObject* PyErr_Occurred();
254266
void PyErr_Print();
255267
void PyErr_SetString(PyObject*,const char*);
268+
269+
270+
extern PyTypeObject* PyBool_Type;
271+
extern PyTypeObject* PyFloat_Type;
272+
extern PyObject* PyExc_RuntimeError;
256273

257274
int PyType_IsSubtype(PyTypeObject*, PyTypeObject*);
258275
unsigned long PyType_GetFlags(PyTypeObject*);
276+
PyObject* Py_GetConstantBorrowed(unsigned);
259277
PyObject* PyLong_FromLong(long);
260278
PyObject* PyFloat_FromDouble(double);
261279
long long PyLong_AsLongLong(PyObject*);
@@ -276,7 +294,7 @@ extern "C" {
276294
PyObject* PySequence_GetItem(PyObject*, ssize_t i);
277295

278296
PyObject* PyUnicode_FromString(const char*);
279-
char* PyUnicode_AsUTF8(PyObject*);
297+
char* PyUnicode_AsUTF8AndSize(PyObject*,Py_ssize_t*);
280298

281299
PyObject* PyDict_New();
282300
int PyDict_SetItemString(PyObject* dp, const char* key, PyObject* item);

0 commit comments

Comments
 (0)