update windows build to Python 3.7
This commit is contained in:
parent
73105fa71e
commit
ddc59ab92d
5761 changed files with 750298 additions and 213405 deletions
|
|
@ -1,11 +1,11 @@
|
|||
__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError',
|
||||
'FFIError']
|
||||
|
||||
from .api import FFI, CDefError, FFIError
|
||||
from .ffiplatform import VerificationError, VerificationMissing
|
||||
from .api import FFI
|
||||
from .error import CDefError, FFIError, VerificationError, VerificationMissing
|
||||
|
||||
__version__ = "1.5.0"
|
||||
__version_info__ = (1, 5, 0)
|
||||
__version__ = "1.11.5"
|
||||
__version_info__ = (1, 11, 5)
|
||||
|
||||
# The verifier module file names are based on the CRC32 of a string that
|
||||
# contains the following version number. It may be older than __version__
|
||||
|
|
|
|||
BIN
Lib/site-packages/cffi/__pycache__/__init__.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/__init__.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/api.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/api.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/backend_ctypes.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/backend_ctypes.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/cffi_opcode.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/cffi_opcode.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/commontypes.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/commontypes.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/cparser.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/cparser.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/error.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/error.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/ffiplatform.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/ffiplatform.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/lock.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/lock.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/model.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/model.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/recompiler.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/recompiler.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/setuptools_ext.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/setuptools_ext.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/vengine_cpy.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/vengine_cpy.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/vengine_gen.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/vengine_gen.cpython-37.pyc
Normal file
Binary file not shown.
BIN
Lib/site-packages/cffi/__pycache__/verifier.cpython-37.pyc
Normal file
BIN
Lib/site-packages/cffi/__pycache__/verifier.cpython-37.pyc
Normal file
Binary file not shown.
145
Lib/site-packages/cffi/_cffi_errors.h
Normal file
145
Lib/site-packages/cffi/_cffi_errors.h
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
#ifndef CFFI_MESSAGEBOX
|
||||
# ifdef _MSC_VER
|
||||
# define CFFI_MESSAGEBOX 1
|
||||
# else
|
||||
# define CFFI_MESSAGEBOX 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#if CFFI_MESSAGEBOX
|
||||
/* Windows only: logic to take the Python-CFFI embedding logic
|
||||
initialization errors and display them in a background thread
|
||||
with MessageBox. The idea is that if the whole program closes
|
||||
as a result of this problem, then likely it is already a console
|
||||
program and you can read the stderr output in the console too.
|
||||
If it is not a console program, then it will likely show its own
|
||||
dialog to complain, or generally not abruptly close, and for this
|
||||
case the background thread should stay alive.
|
||||
*/
|
||||
static void *volatile _cffi_bootstrap_text;
|
||||
|
||||
static PyObject *_cffi_start_error_capture(void)
|
||||
{
|
||||
PyObject *result = NULL;
|
||||
PyObject *x, *m, *bi;
|
||||
|
||||
if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text,
|
||||
(void *)1, NULL) != NULL)
|
||||
return (PyObject *)1;
|
||||
|
||||
m = PyImport_AddModule("_cffi_error_capture");
|
||||
if (m == NULL)
|
||||
goto error;
|
||||
|
||||
result = PyModule_GetDict(m);
|
||||
if (result == NULL)
|
||||
goto error;
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
bi = PyImport_ImportModule("builtins");
|
||||
#else
|
||||
bi = PyImport_ImportModule("__builtin__");
|
||||
#endif
|
||||
if (bi == NULL)
|
||||
goto error;
|
||||
PyDict_SetItemString(result, "__builtins__", bi);
|
||||
Py_DECREF(bi);
|
||||
|
||||
x = PyRun_String(
|
||||
"import sys\n"
|
||||
"class FileLike:\n"
|
||||
" def write(self, x):\n"
|
||||
" of.write(x)\n"
|
||||
" self.buf += x\n"
|
||||
"fl = FileLike()\n"
|
||||
"fl.buf = ''\n"
|
||||
"of = sys.stderr\n"
|
||||
"sys.stderr = fl\n"
|
||||
"def done():\n"
|
||||
" sys.stderr = of\n"
|
||||
" return fl.buf\n", /* make sure the returned value stays alive */
|
||||
Py_file_input,
|
||||
result, result);
|
||||
Py_XDECREF(x);
|
||||
|
||||
error:
|
||||
if (PyErr_Occurred())
|
||||
{
|
||||
PyErr_WriteUnraisable(Py_None);
|
||||
PyErr_Clear();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma comment(lib, "user32.lib")
|
||||
|
||||
static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored)
|
||||
{
|
||||
Sleep(666); /* may be interrupted if the whole process is closing */
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text,
|
||||
L"Python-CFFI error",
|
||||
MB_OK | MB_ICONERROR);
|
||||
#else
|
||||
MessageBoxA(NULL, (char *)_cffi_bootstrap_text,
|
||||
"Python-CFFI error",
|
||||
MB_OK | MB_ICONERROR);
|
||||
#endif
|
||||
_cffi_bootstrap_text = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _cffi_stop_error_capture(PyObject *ecap)
|
||||
{
|
||||
PyObject *s;
|
||||
void *text;
|
||||
|
||||
if (ecap == (PyObject *)1)
|
||||
return;
|
||||
|
||||
if (ecap == NULL)
|
||||
goto error;
|
||||
|
||||
s = PyRun_String("done()", Py_eval_input, ecap, ecap);
|
||||
if (s == NULL)
|
||||
goto error;
|
||||
|
||||
/* Show a dialog box, but in a background thread, and
|
||||
never show multiple dialog boxes at once. */
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
text = PyUnicode_AsWideCharString(s, NULL);
|
||||
#else
|
||||
text = PyString_AsString(s);
|
||||
#endif
|
||||
|
||||
_cffi_bootstrap_text = text;
|
||||
|
||||
if (text != NULL)
|
||||
{
|
||||
HANDLE h;
|
||||
h = CreateThread(NULL, 0, _cffi_bootstrap_dialog,
|
||||
NULL, 0, NULL);
|
||||
if (h != NULL)
|
||||
CloseHandle(h);
|
||||
}
|
||||
/* decref the string, but it should stay alive as 'fl.buf'
|
||||
in the small module above. It will really be freed only if
|
||||
we later get another similar error. So it's a leak of at
|
||||
most one copy of the small module. That's fine for this
|
||||
situation which is usually a "fatal error" anyway. */
|
||||
Py_DECREF(s);
|
||||
PyErr_Clear();
|
||||
return;
|
||||
|
||||
error:
|
||||
_cffi_bootstrap_text = NULL;
|
||||
PyErr_Clear();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static PyObject *_cffi_start_error_capture(void) { return NULL; }
|
||||
static void _cffi_stop_error_capture(PyObject *ecap) { }
|
||||
|
||||
#endif
|
||||
|
|
@ -1,4 +1,30 @@
|
|||
#define _CFFI_
|
||||
|
||||
/* We try to define Py_LIMITED_API before including Python.h.
|
||||
|
||||
Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and
|
||||
Py_REF_DEBUG are not defined. This is a best-effort approximation:
|
||||
we can learn about Py_DEBUG from pyconfig.h, but it is unclear if
|
||||
the same works for the other two macros. Py_DEBUG implies them,
|
||||
but not the other way around.
|
||||
|
||||
Issue #350 is still open: on Windows, the code here causes it to link
|
||||
with PYTHON36.DLL (for example) instead of PYTHON3.DLL. A fix was
|
||||
attempted in 164e526a5515 and 14ce6985e1c3, but reverted: virtualenv
|
||||
does not make PYTHON3.DLL available, and so the "correctly" compiled
|
||||
version would not run inside a virtualenv. We will re-apply the fix
|
||||
after virtualenv has been fixed for some time. For explanation, see
|
||||
issue #355. For a workaround if you want PYTHON3.DLL and don't worry
|
||||
about virtualenv, see issue #350. See also 'py_limited_api' in
|
||||
setuptools_ext.py.
|
||||
*/
|
||||
#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API)
|
||||
# include <pyconfig.h>
|
||||
# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG)
|
||||
# define Py_LIMITED_API
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <Python.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -42,7 +68,9 @@ extern "C" {
|
|||
# include <stdint.h>
|
||||
# endif
|
||||
# if _MSC_VER < 1800 /* MSVC < 2013 */
|
||||
typedef unsigned char _Bool;
|
||||
# ifndef __cplusplus
|
||||
typedef unsigned char _Bool;
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
# include <stdint.h>
|
||||
|
|
@ -57,6 +85,12 @@ extern "C" {
|
|||
# define _CFFI_UNUSED_FN /* nothing */
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
# ifndef _Bool
|
||||
typedef bool _Bool; /* semi-hackish: C++ has no _Bool; bool is builtin */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/********** CPython-specific section **********/
|
||||
#ifndef PYPY_VERSION
|
||||
|
||||
|
|
@ -71,6 +105,7 @@ extern "C" {
|
|||
#define _cffi_from_c_ulong PyLong_FromUnsignedLong
|
||||
#define _cffi_from_c_longlong PyLong_FromLongLong
|
||||
#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong
|
||||
#define _cffi_from_c__Bool PyBool_FromLong
|
||||
|
||||
#define _cffi_to_c_double PyFloat_AsDouble
|
||||
#define _cffi_to_c_float PyFloat_AsDouble
|
||||
|
|
@ -117,9 +152,9 @@ extern "C" {
|
|||
#define _cffi_to_c_char \
|
||||
((int(*)(PyObject *))_cffi_exports[9])
|
||||
#define _cffi_from_c_pointer \
|
||||
((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10])
|
||||
((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10])
|
||||
#define _cffi_to_c_pointer \
|
||||
((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11])
|
||||
((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11])
|
||||
#define _cffi_get_struct_layout \
|
||||
not used any more
|
||||
#define _cffi_restore_errno \
|
||||
|
|
@ -129,35 +164,40 @@ extern "C" {
|
|||
#define _cffi_from_c_char \
|
||||
((PyObject *(*)(char))_cffi_exports[15])
|
||||
#define _cffi_from_c_deref \
|
||||
((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16])
|
||||
((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16])
|
||||
#define _cffi_to_c \
|
||||
((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17])
|
||||
((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17])
|
||||
#define _cffi_from_c_struct \
|
||||
((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18])
|
||||
((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18])
|
||||
#define _cffi_to_c_wchar_t \
|
||||
((wchar_t(*)(PyObject *))_cffi_exports[19])
|
||||
((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19])
|
||||
#define _cffi_from_c_wchar_t \
|
||||
((PyObject *(*)(wchar_t))_cffi_exports[20])
|
||||
((PyObject *(*)(_cffi_wchar_t))_cffi_exports[20])
|
||||
#define _cffi_to_c_long_double \
|
||||
((long double(*)(PyObject *))_cffi_exports[21])
|
||||
#define _cffi_to_c__Bool \
|
||||
((_Bool(*)(PyObject *))_cffi_exports[22])
|
||||
#define _cffi_prepare_pointer_call_argument \
|
||||
((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23])
|
||||
((Py_ssize_t(*)(struct _cffi_ctypedescr *, \
|
||||
PyObject *, char **))_cffi_exports[23])
|
||||
#define _cffi_convert_array_from_object \
|
||||
((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24])
|
||||
((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24])
|
||||
#define _CFFI_CPIDX 25
|
||||
#define _cffi_call_python \
|
||||
((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX])
|
||||
#define _CFFI_NUM_EXPORTS 26
|
||||
#define _cffi_to_c_wchar3216_t \
|
||||
((int(*)(PyObject *))_cffi_exports[26])
|
||||
#define _cffi_from_c_wchar3216_t \
|
||||
((PyObject *(*)(int))_cffi_exports[27])
|
||||
#define _CFFI_NUM_EXPORTS 28
|
||||
|
||||
typedef struct _ctypedescr CTypeDescrObject;
|
||||
struct _cffi_ctypedescr;
|
||||
|
||||
static void *_cffi_exports[_CFFI_NUM_EXPORTS];
|
||||
|
||||
#define _cffi_type(index) ( \
|
||||
assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \
|
||||
(CTypeDescrObject *)_cffi_types[index])
|
||||
(struct _cffi_ctypedescr *)_cffi_types[index])
|
||||
|
||||
static PyObject *_cffi_init(const char *module_name, Py_ssize_t version,
|
||||
const struct _cffi_type_context_s *ctx)
|
||||
|
|
@ -190,20 +230,46 @@ static PyObject *_cffi_init(const char *module_name, Py_ssize_t version,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
_CFFI_UNUSED_FN
|
||||
static PyObject **_cffi_unpack_args(PyObject *args_tuple, Py_ssize_t expected,
|
||||
const char *fnname)
|
||||
|
||||
#ifdef HAVE_WCHAR_H
|
||||
typedef wchar_t _cffi_wchar_t;
|
||||
#else
|
||||
typedef uint16_t _cffi_wchar_t; /* same random pick as _cffi_backend.c */
|
||||
#endif
|
||||
|
||||
_CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o)
|
||||
{
|
||||
if (PyTuple_GET_SIZE(args_tuple) != expected) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.150s() takes exactly %zd arguments (%zd given)",
|
||||
fnname, expected, PyTuple_GET_SIZE(args_tuple));
|
||||
return NULL;
|
||||
}
|
||||
return &PyTuple_GET_ITEM(args_tuple, 0); /* pointer to the first item,
|
||||
the others follow */
|
||||
if (sizeof(_cffi_wchar_t) == 2)
|
||||
return (uint16_t)_cffi_to_c_wchar_t(o);
|
||||
else
|
||||
return (uint16_t)_cffi_to_c_wchar3216_t(o);
|
||||
}
|
||||
|
||||
_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x)
|
||||
{
|
||||
if (sizeof(_cffi_wchar_t) == 2)
|
||||
return _cffi_from_c_wchar_t((_cffi_wchar_t)x);
|
||||
else
|
||||
return _cffi_from_c_wchar3216_t((int)x);
|
||||
}
|
||||
|
||||
_CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o)
|
||||
{
|
||||
if (sizeof(_cffi_wchar_t) == 4)
|
||||
return (int)_cffi_to_c_wchar_t(o);
|
||||
else
|
||||
return (int)_cffi_to_c_wchar3216_t(o);
|
||||
}
|
||||
|
||||
_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(int x)
|
||||
{
|
||||
if (sizeof(_cffi_wchar_t) == 4)
|
||||
return _cffi_from_c_wchar_t((_cffi_wchar_t)x);
|
||||
else
|
||||
return _cffi_from_c_wchar3216_t(x);
|
||||
}
|
||||
|
||||
|
||||
/********** end CPython-specific section **********/
|
||||
#else
|
||||
_CFFI_UNUSED_FN
|
||||
|
|
@ -231,6 +297,12 @@ static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *);
|
|||
((got_nonpos) == (expected <= 0) && \
|
||||
(got) == (unsigned long long)expected)
|
||||
|
||||
#ifdef MS_WIN32
|
||||
# define _cffi_stdcall __stdcall
|
||||
#else
|
||||
# define _cffi_stdcall /* nothing */
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
|
||||
/***** Support code for embedding *****/
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define CFFI_DLLEXPORT __declspec(dllexport)
|
||||
#elif defined(__GNUC__)
|
||||
# define CFFI_DLLEXPORT __attribute__((visibility("default")))
|
||||
|
|
@ -109,6 +114,8 @@ static void _cffi_release_reentrant_mutex(void)
|
|||
/********** CPython-specific section **********/
|
||||
#ifndef PYPY_VERSION
|
||||
|
||||
#include "_cffi_errors.h"
|
||||
|
||||
|
||||
#define _cffi_call_python_org _cffi_exports[_CFFI_CPIDX]
|
||||
|
||||
|
|
@ -139,32 +146,6 @@ static int _cffi_initialize_python(void)
|
|||
PyGILState_STATE state;
|
||||
PyObject *pycode=NULL, *global_dict=NULL, *x;
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
/* see comments in _cffi_carefully_make_gil() about the
|
||||
Python2/Python3 difference
|
||||
*/
|
||||
#else
|
||||
/* Acquire the GIL. We have no threadstate here. If Python is
|
||||
already initialized, it is possible that there is already one
|
||||
existing for this thread, but it is not made current now.
|
||||
*/
|
||||
PyEval_AcquireLock();
|
||||
|
||||
_cffi_py_initialize();
|
||||
|
||||
/* The Py_InitializeEx() sometimes made a threadstate for us, but
|
||||
not always. Indeed Py_InitializeEx() could be called and do
|
||||
nothing. So do we have a threadstate, or not? We don't know,
|
||||
but we can replace it with NULL in all cases.
|
||||
*/
|
||||
(void)PyThreadState_Swap(NULL);
|
||||
|
||||
/* Now we can release the GIL and re-acquire immediately using the
|
||||
logic of PyGILState(), which handles making or installing the
|
||||
correct threadstate.
|
||||
*/
|
||||
PyEval_ReleaseLock();
|
||||
#endif
|
||||
state = PyGILState_Ensure();
|
||||
|
||||
/* Call the initxxx() function from the present module. It will
|
||||
|
|
@ -220,8 +201,16 @@ static int _cffi_initialize_python(void)
|
|||
/* Print as much information as potentially useful.
|
||||
Debugging load-time failures with embedding is not fun
|
||||
*/
|
||||
PyObject *ecap;
|
||||
PyObject *exception, *v, *tb, *f, *modules, *mod;
|
||||
PyErr_Fetch(&exception, &v, &tb);
|
||||
ecap = _cffi_start_error_capture();
|
||||
f = PySys_GetObject((char *)"stderr");
|
||||
if (f != NULL && f != Py_None) {
|
||||
PyFile_WriteString(
|
||||
"Failed to initialize the Python-CFFI embedding logic:\n\n", f);
|
||||
}
|
||||
|
||||
if (exception != NULL) {
|
||||
PyErr_NormalizeException(&exception, &v, &tb);
|
||||
PyErr_Display(exception, v, tb);
|
||||
|
|
@ -230,10 +219,9 @@ static int _cffi_initialize_python(void)
|
|||
Py_XDECREF(v);
|
||||
Py_XDECREF(tb);
|
||||
|
||||
f = PySys_GetObject((char *)"stderr");
|
||||
if (f != NULL && f != Py_None) {
|
||||
PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME
|
||||
"\ncompiled with cffi version: 1.5.0"
|
||||
"\ncompiled with cffi version: 1.11.5"
|
||||
"\n_cffi_backend module: ", f);
|
||||
modules = PyImport_GetModuleDict();
|
||||
mod = PyDict_GetItemString(modules, "_cffi_backend");
|
||||
|
|
@ -249,6 +237,7 @@ static int _cffi_initialize_python(void)
|
|||
PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0);
|
||||
PyFile_WriteString("\n\n", f);
|
||||
}
|
||||
_cffi_stop_error_capture(ecap);
|
||||
}
|
||||
result = -1;
|
||||
goto done;
|
||||
|
|
@ -263,16 +252,14 @@ static int _cffi_carefully_make_gil(void)
|
|||
that we don't hold the GIL before (if it exists), and we don't
|
||||
hold it afterwards.
|
||||
|
||||
What it really does is completely different in Python 2 and
|
||||
Python 3.
|
||||
(What it really does used to be completely different in Python 2
|
||||
and Python 3, with the Python 2 solution avoiding the spin-lock
|
||||
around the Py_InitializeEx() call. However, after recent changes
|
||||
to CPython 2.7 (issue #358) it no longer works. So we use the
|
||||
Python 3 solution everywhere.)
|
||||
|
||||
Python 2
|
||||
========
|
||||
|
||||
Initialize the GIL, without initializing the rest of Python,
|
||||
by calling PyEval_InitThreads().
|
||||
|
||||
PyEval_InitThreads() must not be called concurrently at all.
|
||||
This initializes Python by calling Py_InitializeEx().
|
||||
Important: this must not be called concurrently at all.
|
||||
So we use a global variable as a simple spin lock. This global
|
||||
variable must be from 'libpythonX.Y.so', not from this
|
||||
cffi-based extension module, because it must be shared from
|
||||
|
|
@ -282,18 +269,6 @@ static int _cffi_carefully_make_gil(void)
|
|||
string "ENDMARKER". We change it temporarily to point to the
|
||||
next character in that string. (Yes, I know it's REALLY
|
||||
obscure.)
|
||||
|
||||
Python 3
|
||||
========
|
||||
|
||||
In Python 3, PyEval_InitThreads() cannot be called before
|
||||
Py_InitializeEx() any more. So this function calls
|
||||
Py_InitializeEx() first. It uses the same obscure logic to
|
||||
make sure we never call it concurrently.
|
||||
|
||||
Arguably, this is less good on the spinlock, because
|
||||
Py_InitializeEx() takes much longer to run than
|
||||
PyEval_InitThreads(). But I didn't find a way around it.
|
||||
*/
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
|
|
@ -317,8 +292,7 @@ static int _cffi_carefully_make_gil(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
/* Python 3: call Py_InitializeEx() */
|
||||
/* call Py_InitializeEx() */
|
||||
{
|
||||
PyGILState_STATE state = PyGILState_UNLOCKED;
|
||||
if (!Py_IsInitialized())
|
||||
|
|
@ -329,17 +303,6 @@ static int _cffi_carefully_make_gil(void)
|
|||
PyEval_InitThreads();
|
||||
PyGILState_Release(state);
|
||||
}
|
||||
#else
|
||||
/* Python 2: call PyEval_InitThreads() */
|
||||
# ifdef WITH_THREAD
|
||||
if (!PyEval_ThreadsInitialized()) {
|
||||
PyEval_InitThreads(); /* makes the GIL */
|
||||
PyEval_ReleaseLock(); /* then release it */
|
||||
}
|
||||
/* else: there is already a GIL, but we still needed to do the
|
||||
spinlock dance to make sure that we see it as fully ready */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
/* release the lock */
|
||||
|
|
@ -366,7 +329,7 @@ static struct _cffi_pypy_init_s {
|
|||
const char *code;
|
||||
} _cffi_pypy_init = {
|
||||
_CFFI_MODULE_NAME,
|
||||
_CFFI_PYTHON_STARTUP_FUNC,
|
||||
(void(*)(const void *[]))_CFFI_PYTHON_STARTUP_FUNC,
|
||||
_CFFI_PYTHON_STARTUP_CODE,
|
||||
};
|
||||
|
||||
|
|
@ -515,3 +478,7 @@ static int cffi_start_python(void)
|
|||
#undef cffi_compare_and_swap
|
||||
#undef cffi_write_barrier
|
||||
#undef cffi_read_barrier
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import sys, types
|
||||
from .lock import allocate_lock
|
||||
from .error import CDefError
|
||||
from . import model
|
||||
|
||||
try:
|
||||
callable
|
||||
|
|
@ -15,17 +17,6 @@ except NameError:
|
|||
basestring = str
|
||||
|
||||
|
||||
class FFIError(Exception):
|
||||
pass
|
||||
|
||||
class CDefError(Exception):
|
||||
def __str__(self):
|
||||
try:
|
||||
line = 'line %d: ' % (self.args[1].coord.line,)
|
||||
except (AttributeError, TypeError, IndexError):
|
||||
line = ''
|
||||
return '%s%s' % (line, self.args[0])
|
||||
|
||||
|
||||
class FFI(object):
|
||||
r'''
|
||||
|
|
@ -49,18 +40,27 @@ class FFI(object):
|
|||
"""Create an FFI instance. The 'backend' argument is used to
|
||||
select a non-default backend, mostly for tests.
|
||||
"""
|
||||
from . import cparser, model
|
||||
if backend is None:
|
||||
# You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with
|
||||
# _cffi_backend.so compiled.
|
||||
import _cffi_backend as backend
|
||||
from . import __version__
|
||||
assert backend.__version__ == __version__, \
|
||||
"version mismatch, %s != %s" % (backend.__version__, __version__)
|
||||
if backend.__version__ != __version__:
|
||||
# bad version! Try to be as explicit as possible.
|
||||
if hasattr(backend, '__file__'):
|
||||
# CPython
|
||||
raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r. The two versions should be equal; check your installation." % (
|
||||
__version__, __file__,
|
||||
backend.__version__, backend.__file__))
|
||||
else:
|
||||
# PyPy
|
||||
raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. This interpreter comes with a built-in '_cffi_backend' module, which is version %s. The two versions should be equal; check your installation." % (
|
||||
__version__, __file__, backend.__version__))
|
||||
# (If you insist you can also try to pass the option
|
||||
# 'backend=backend_ctypes.CTypesBackend()', but don't
|
||||
# rely on it! It's probably not going to work well.)
|
||||
|
||||
from . import cparser
|
||||
self._backend = backend
|
||||
self._lock = allocate_lock()
|
||||
self._parser = cparser.Parser()
|
||||
|
|
@ -75,9 +75,10 @@ class FFI(object):
|
|||
self._init_once_cache = {}
|
||||
self._cdef_version = None
|
||||
self._embedding = None
|
||||
self._typecache = model.get_typecache(backend)
|
||||
if hasattr(backend, 'set_ffi'):
|
||||
backend.set_ffi(self)
|
||||
for name in backend.__dict__:
|
||||
for name in list(backend.__dict__):
|
||||
if name.startswith('RTLD_'):
|
||||
setattr(self, name, getattr(backend, name))
|
||||
#
|
||||
|
|
@ -93,6 +94,7 @@ class FFI(object):
|
|||
# ctypes backend: attach these constants to the instance
|
||||
self.NULL = self.cast(self.BVoidP, 0)
|
||||
self.CData, self.CType = backend._get_types()
|
||||
self.buffer = backend.buffer
|
||||
|
||||
def cdef(self, csource, override=False, packed=False):
|
||||
"""Parse the given C source. This registers all declared functions,
|
||||
|
|
@ -141,6 +143,13 @@ class FFI(object):
|
|||
self._libraries.append(lib)
|
||||
return lib
|
||||
|
||||
def dlclose(self, lib):
|
||||
"""Close a library obtained with ffi.dlopen(). After this call,
|
||||
access to functions or variables from the library will fail
|
||||
(possibly with a segmentation fault).
|
||||
"""
|
||||
type(lib).__cffi_close__(lib)
|
||||
|
||||
def _typeof_locked(self, cdecl):
|
||||
# call me with the lock!
|
||||
key = cdecl
|
||||
|
|
@ -212,7 +221,7 @@ class FFI(object):
|
|||
|
||||
def offsetof(self, cdecl, *fields_or_indexes):
|
||||
"""Return the offset of the named field inside the given
|
||||
structure or array, which must be given as a C type name.
|
||||
structure or array, which must be given as a C type name.
|
||||
You can give several field names in case of nested structures.
|
||||
You can also give numeric values which correspond to array
|
||||
items, in case of an array type.
|
||||
|
|
@ -299,24 +308,41 @@ class FFI(object):
|
|||
"""
|
||||
return self._backend.string(cdata, maxlen)
|
||||
|
||||
def buffer(self, cdata, size=-1):
|
||||
"""Return a read-write buffer object that references the raw C data
|
||||
pointed to by the given 'cdata'. The 'cdata' must be a pointer or
|
||||
an array. Can be passed to functions expecting a buffer, or directly
|
||||
manipulated with:
|
||||
def unpack(self, cdata, length):
|
||||
"""Unpack an array of C data of the given length,
|
||||
returning a Python string/unicode/list.
|
||||
|
||||
buf[:] get a copy of it in a regular string, or
|
||||
buf[idx] as a single character
|
||||
buf[:] = ...
|
||||
buf[idx] = ... change the content
|
||||
If 'cdata' is a pointer to 'char', returns a byte string.
|
||||
It does not stop at the first null. This is equivalent to:
|
||||
ffi.buffer(cdata, length)[:]
|
||||
|
||||
If 'cdata' is a pointer to 'wchar_t', returns a unicode string.
|
||||
'length' is measured in wchar_t's; it is not the size in bytes.
|
||||
|
||||
If 'cdata' is a pointer to anything else, returns a list of
|
||||
'length' items. This is a faster equivalent to:
|
||||
[cdata[i] for i in range(length)]
|
||||
"""
|
||||
return self._backend.buffer(cdata, size)
|
||||
return self._backend.unpack(cdata, length)
|
||||
|
||||
#def buffer(self, cdata, size=-1):
|
||||
# """Return a read-write buffer object that references the raw C data
|
||||
# pointed to by the given 'cdata'. The 'cdata' must be a pointer or
|
||||
# an array. Can be passed to functions expecting a buffer, or directly
|
||||
# manipulated with:
|
||||
#
|
||||
# buf[:] get a copy of it in a regular string, or
|
||||
# buf[idx] as a single character
|
||||
# buf[:] = ...
|
||||
# buf[idx] = ... change the content
|
||||
# """
|
||||
# note that 'buffer' is a type, set on this instance by __init__
|
||||
|
||||
def from_buffer(self, python_buffer):
|
||||
"""Return a <cdata 'char[]'> that points to the data of the
|
||||
given Python object, which must support the buffer interface.
|
||||
Note that this is not meant to be used on the built-in types str,
|
||||
unicode, or bytearray (you can build 'char[]' arrays explicitly)
|
||||
Note that this is not meant to be used on the built-in types
|
||||
str or unicode (you can build 'char[]' arrays explicitly)
|
||||
but only on objects containing large quantities of raw data
|
||||
in some other format, like 'array.array' or numpy arrays.
|
||||
"""
|
||||
|
|
@ -375,25 +401,17 @@ class FFI(object):
|
|||
replace_with = ' ' + replace_with
|
||||
return self._backend.getcname(cdecl, replace_with)
|
||||
|
||||
def gc(self, cdata, destructor):
|
||||
def gc(self, cdata, destructor, size=0):
|
||||
"""Return a new cdata object that points to the same
|
||||
data. Later, when this new cdata object is garbage-collected,
|
||||
'destructor(old_cdata_object)' will be called.
|
||||
|
||||
The optional 'size' gives an estimate of the size, used to
|
||||
trigger the garbage collection more eagerly. So far only used
|
||||
on PyPy. It tells the GC that the returned object keeps alive
|
||||
roughly 'size' bytes of external memory.
|
||||
"""
|
||||
try:
|
||||
gcp = self._backend.gcp
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
return gcp(cdata, destructor)
|
||||
#
|
||||
with self._lock:
|
||||
try:
|
||||
gc_weakrefs = self.gc_weakrefs
|
||||
except AttributeError:
|
||||
from .gc_weakref import GcWeakrefs
|
||||
gc_weakrefs = self.gc_weakrefs = GcWeakrefs(self)
|
||||
return gc_weakrefs.build(cdata, destructor)
|
||||
return self._backend.gcp(cdata, destructor, size)
|
||||
|
||||
def _get_cached_btype(self, type):
|
||||
assert self._lock.acquire(False) is False
|
||||
|
|
@ -448,7 +466,6 @@ class FFI(object):
|
|||
return self._backend.getwinerror(code)
|
||||
|
||||
def _pointer_to(self, ctype):
|
||||
from . import model
|
||||
with self._lock:
|
||||
return model.pointer_cache(self, ctype)
|
||||
|
||||
|
|
@ -458,7 +475,12 @@ class FFI(object):
|
|||
field or array item in the structure or array, recursively in
|
||||
case of nested structures.
|
||||
"""
|
||||
ctype = self._backend.typeof(cdata)
|
||||
try:
|
||||
ctype = self._backend.typeof(cdata)
|
||||
except TypeError:
|
||||
if '__addressof__' in type(cdata).__dict__:
|
||||
return type(cdata).__addressof__(cdata, *fields_or_indexes)
|
||||
raise
|
||||
if fields_or_indexes:
|
||||
ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes)
|
||||
else:
|
||||
|
|
@ -550,17 +572,40 @@ class FFI(object):
|
|||
lst.append(value)
|
||||
#
|
||||
if '__pypy__' in sys.builtin_module_names:
|
||||
import os
|
||||
if sys.platform == "win32":
|
||||
# we need 'libpypy-c.lib'. Current distributions of
|
||||
# pypy (>= 4.1) contain it as 'libs/python27.lib'.
|
||||
pythonlib = "python27"
|
||||
if hasattr(sys, 'prefix'):
|
||||
ensure('library_dirs', os.path.join(sys.prefix, 'libs'))
|
||||
else:
|
||||
# we need 'libpypy-c.{so,dylib}', which should be by
|
||||
# default located in 'sys.prefix/bin' for installed
|
||||
# systems.
|
||||
if sys.version_info < (3,):
|
||||
pythonlib = "pypy-c"
|
||||
else:
|
||||
pythonlib = "pypy3-c"
|
||||
if hasattr(sys, 'prefix'):
|
||||
ensure('library_dirs', os.path.join(sys.prefix, 'bin'))
|
||||
# On uninstalled pypy's, the libpypy-c is typically found in
|
||||
# .../pypy/goal/.
|
||||
if hasattr(sys, 'prefix'):
|
||||
import os
|
||||
ensure('library_dirs', os.path.join(sys.prefix, 'bin'))
|
||||
pythonlib = "pypy-c"
|
||||
ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal'))
|
||||
else:
|
||||
if sys.platform == "win32":
|
||||
template = "python%d%d"
|
||||
if sys.flags.debug:
|
||||
template = template + '_d'
|
||||
if hasattr(sys, 'gettotalrefcount'):
|
||||
template += '_d'
|
||||
else:
|
||||
try:
|
||||
import sysconfig
|
||||
except ImportError: # 2.6
|
||||
from distutils import sysconfig
|
||||
template = "python%d.%d"
|
||||
if sysconfig.get_config_var('DEBUG_EXT'):
|
||||
template += sysconfig.get_config_var('DEBUG_EXT')
|
||||
pythonlib = (template %
|
||||
(sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
|
||||
if hasattr(sys, 'abiflags'):
|
||||
|
|
@ -570,11 +615,15 @@ class FFI(object):
|
|||
ensure('extra_link_args', '/MANIFEST')
|
||||
|
||||
def set_source(self, module_name, source, source_extension='.c', **kwds):
|
||||
import os
|
||||
if hasattr(self, '_assigned_source'):
|
||||
raise ValueError("set_source() cannot be called several times "
|
||||
"per ffi object")
|
||||
if not isinstance(module_name, basestring):
|
||||
raise TypeError("'module_name' must be a string")
|
||||
if os.sep in module_name or (os.altsep and os.altsep in module_name):
|
||||
raise ValueError("'module_name' must not contain '/': use a dotted "
|
||||
"name to make a 'package.module' location")
|
||||
self._assigned_source = (str(module_name), source,
|
||||
source_extension, kwds)
|
||||
|
||||
|
|
@ -628,7 +677,7 @@ class FFI(object):
|
|||
recompile(self, module_name, source,
|
||||
c_file=filename, call_c_compiler=False, **kwds)
|
||||
|
||||
def compile(self, tmpdir='.', verbose=0, target=None):
|
||||
def compile(self, tmpdir='.', verbose=0, target=None, debug=None):
|
||||
"""The 'target' argument gives the final file name of the
|
||||
compiled DLL. Use '*' to force distutils' choice, suitable for
|
||||
regular CPython C API modules. Use a file name ending in '.*'
|
||||
|
|
@ -645,7 +694,7 @@ class FFI(object):
|
|||
module_name, source, source_extension, kwds = self._assigned_source
|
||||
return recompile(self, module_name, source, tmpdir=tmpdir,
|
||||
target=target, source_extension=source_extension,
|
||||
compiler_verbose=verbose, **kwds)
|
||||
compiler_verbose=verbose, debug=debug, **kwds)
|
||||
|
||||
def init_once(self, func, tag):
|
||||
# Read _init_once_cache[tag], which is either (False, lock) if
|
||||
|
|
@ -697,26 +746,58 @@ class FFI(object):
|
|||
#
|
||||
self._embedding = pysource
|
||||
|
||||
def def_extern(self, *args, **kwds):
|
||||
raise ValueError("ffi.def_extern() is only available on API-mode FFI "
|
||||
"objects")
|
||||
|
||||
def list_types(self):
|
||||
"""Returns the user type names known to this FFI instance.
|
||||
This returns a tuple containing three lists of names:
|
||||
(typedef_names, names_of_structs, names_of_unions)
|
||||
"""
|
||||
typedefs = []
|
||||
structs = []
|
||||
unions = []
|
||||
for key in self._parser._declarations:
|
||||
if key.startswith('typedef '):
|
||||
typedefs.append(key[8:])
|
||||
elif key.startswith('struct '):
|
||||
structs.append(key[7:])
|
||||
elif key.startswith('union '):
|
||||
unions.append(key[6:])
|
||||
typedefs.sort()
|
||||
structs.sort()
|
||||
unions.sort()
|
||||
return (typedefs, structs, unions)
|
||||
|
||||
|
||||
def _load_backend_lib(backend, name, flags):
|
||||
import os
|
||||
if name is None:
|
||||
if sys.platform != "win32":
|
||||
return backend.load_library(None, flags)
|
||||
name = "c" # Windows: load_library(None) fails, but this works
|
||||
# (backward compatibility hack only)
|
||||
try:
|
||||
if '.' not in name and '/' not in name:
|
||||
raise OSError("library not found: %r" % (name,))
|
||||
return backend.load_library(name, flags)
|
||||
except OSError:
|
||||
import ctypes.util
|
||||
path = ctypes.util.find_library(name)
|
||||
if path is None:
|
||||
raise # propagate the original OSError
|
||||
return backend.load_library(path, flags)
|
||||
# on Python 2 (backward compatibility hack only)
|
||||
first_error = None
|
||||
if '.' in name or '/' in name or os.sep in name:
|
||||
try:
|
||||
return backend.load_library(name, flags)
|
||||
except OSError as e:
|
||||
first_error = e
|
||||
import ctypes.util
|
||||
path = ctypes.util.find_library(name)
|
||||
if path is None:
|
||||
if name == "c" and sys.platform == "win32" and sys.version_info >= (3,):
|
||||
raise OSError("dlopen(None) cannot work on Windows for Python 3 "
|
||||
"(see http://bugs.python.org/issue23606)")
|
||||
msg = ("ctypes.util.find_library() did not manage "
|
||||
"to locate a library called %r" % (name,))
|
||||
if first_error is not None:
|
||||
msg = "%s. Additionally, %s" % (first_error, msg)
|
||||
raise OSError(msg)
|
||||
return backend.load_library(path, flags)
|
||||
|
||||
def _make_ffi_library(ffi, libname, flags):
|
||||
import os
|
||||
backend = ffi._backend
|
||||
backendlib = _load_backend_lib(backend, libname, flags)
|
||||
#
|
||||
|
|
@ -724,10 +805,7 @@ def _make_ffi_library(ffi, libname, flags):
|
|||
key = 'function ' + name
|
||||
tp, _ = ffi._parser._declarations[key]
|
||||
BType = ffi._get_cached_btype(tp)
|
||||
try:
|
||||
value = backendlib.load_function(BType, name)
|
||||
except KeyError as e:
|
||||
raise AttributeError('%s: %s' % (name, e))
|
||||
value = backendlib.load_function(BType, name)
|
||||
library.__dict__[name] = value
|
||||
#
|
||||
def accessor_variable(name):
|
||||
|
|
@ -740,6 +818,21 @@ def _make_ffi_library(ffi, libname, flags):
|
|||
lambda self: read_variable(BType, name),
|
||||
lambda self, value: write_variable(BType, name, value)))
|
||||
#
|
||||
def addressof_var(name):
|
||||
try:
|
||||
return addr_variables[name]
|
||||
except KeyError:
|
||||
with ffi._lock:
|
||||
if name not in addr_variables:
|
||||
key = 'variable ' + name
|
||||
tp, _ = ffi._parser._declarations[key]
|
||||
BType = ffi._get_cached_btype(tp)
|
||||
if BType.kind != 'array':
|
||||
BType = model.pointer_cache(ffi, BType)
|
||||
p = backendlib.load_function(BType, name)
|
||||
addr_variables[name] = p
|
||||
return addr_variables[name]
|
||||
#
|
||||
def accessor_constant(name):
|
||||
raise NotImplementedError("non-integer constant '%s' cannot be "
|
||||
"accessed from a dlopen() library" % (name,))
|
||||
|
|
@ -749,12 +842,12 @@ def _make_ffi_library(ffi, libname, flags):
|
|||
#
|
||||
accessors = {}
|
||||
accessors_version = [False]
|
||||
addr_variables = {}
|
||||
#
|
||||
def update_accessors():
|
||||
if accessors_version[0] is ffi._cdef_version:
|
||||
return
|
||||
#
|
||||
from . import model
|
||||
for key, (tp, _) in ffi._parser._declarations.items():
|
||||
if not isinstance(tp, model.EnumType):
|
||||
tag, name = key.split(' ', 1)
|
||||
|
|
@ -800,6 +893,21 @@ def _make_ffi_library(ffi, libname, flags):
|
|||
with ffi._lock:
|
||||
update_accessors()
|
||||
return accessors.keys()
|
||||
def __addressof__(self, name):
|
||||
if name in library.__dict__:
|
||||
return library.__dict__[name]
|
||||
if name in FFILibrary.__dict__:
|
||||
return addressof_var(name)
|
||||
make_accessor(name)
|
||||
if name in library.__dict__:
|
||||
return library.__dict__[name]
|
||||
if name in FFILibrary.__dict__:
|
||||
return addressof_var(name)
|
||||
raise AttributeError("cffi library has no function or "
|
||||
"global variable named '%s'" % (name,))
|
||||
def __cffi_close__(self):
|
||||
backendlib.close_lib()
|
||||
self.__dict__.clear()
|
||||
#
|
||||
if libname is not None:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -112,11 +112,20 @@ class CTypesData(object):
|
|||
def _make_cmp(name):
|
||||
cmpfunc = getattr(operator, name)
|
||||
def cmp(self, other):
|
||||
if isinstance(other, CTypesData):
|
||||
v_is_ptr = not isinstance(self, CTypesGenericPrimitive)
|
||||
w_is_ptr = (isinstance(other, CTypesData) and
|
||||
not isinstance(other, CTypesGenericPrimitive))
|
||||
if v_is_ptr and w_is_ptr:
|
||||
return cmpfunc(self._convert_to_address(None),
|
||||
other._convert_to_address(None))
|
||||
else:
|
||||
elif v_is_ptr or w_is_ptr:
|
||||
return NotImplemented
|
||||
else:
|
||||
if isinstance(self, CTypesGenericPrimitive):
|
||||
self = self._value
|
||||
if isinstance(other, CTypesGenericPrimitive):
|
||||
other = other._value
|
||||
return cmpfunc(self, other)
|
||||
cmp.func_name = name
|
||||
return cmp
|
||||
|
||||
|
|
@ -128,7 +137,7 @@ class CTypesData(object):
|
|||
__ge__ = _make_cmp('__ge__')
|
||||
|
||||
def __hash__(self):
|
||||
return hash(type(self)) ^ hash(self._convert_to_address(None))
|
||||
return hash(self._convert_to_address(None))
|
||||
|
||||
def _to_string(self, maxlen):
|
||||
raise TypeError("string(): %r" % (self,))
|
||||
|
|
@ -137,14 +146,8 @@ class CTypesData(object):
|
|||
class CTypesGenericPrimitive(CTypesData):
|
||||
__slots__ = []
|
||||
|
||||
def __eq__(self, other):
|
||||
return self is other
|
||||
|
||||
def __ne__(self, other):
|
||||
return self is not other
|
||||
|
||||
def __hash__(self):
|
||||
return object.__hash__(self)
|
||||
return hash(self._value)
|
||||
|
||||
def _get_own_repr(self):
|
||||
return repr(self._from_ctypes(self._value))
|
||||
|
|
@ -205,9 +208,7 @@ class CTypesGenericPtr(CTypesData):
|
|||
|
||||
def __nonzero__(self):
|
||||
return bool(self._address)
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self._address)
|
||||
__bool__ = __nonzero__
|
||||
|
||||
@classmethod
|
||||
def _to_ctypes(cls, value):
|
||||
|
|
@ -460,6 +461,12 @@ class CTypesBackend(object):
|
|||
return x._value
|
||||
raise TypeError("character expected, got %s" %
|
||||
type(x).__name__)
|
||||
def __nonzero__(self):
|
||||
return ord(self._value) != 0
|
||||
else:
|
||||
def __nonzero__(self):
|
||||
return self._value != 0
|
||||
__bool__ = __nonzero__
|
||||
|
||||
if kind == 'float':
|
||||
@staticmethod
|
||||
|
|
@ -993,6 +1000,45 @@ class CTypesBackend(object):
|
|||
assert onerror is None # XXX not implemented
|
||||
return BType(source, error)
|
||||
|
||||
_weakref_cache_ref = None
|
||||
|
||||
def gcp(self, cdata, destructor, size=0):
|
||||
if self._weakref_cache_ref is None:
|
||||
import weakref
|
||||
class MyRef(weakref.ref):
|
||||
def __eq__(self, other):
|
||||
myref = self()
|
||||
return self is other or (
|
||||
myref is not None and myref is other())
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
def __hash__(self):
|
||||
try:
|
||||
return self._hash
|
||||
except AttributeError:
|
||||
self._hash = hash(self())
|
||||
return self._hash
|
||||
self._weakref_cache_ref = {}, MyRef
|
||||
weak_cache, MyRef = self._weakref_cache_ref
|
||||
|
||||
if destructor is None:
|
||||
try:
|
||||
del weak_cache[MyRef(cdata)]
|
||||
except KeyError:
|
||||
raise TypeError("Can remove destructor only on a object "
|
||||
"previously returned by ffi.gc()")
|
||||
return None
|
||||
|
||||
def remove(k):
|
||||
cdata, destructor = weak_cache.pop(k, (None, None))
|
||||
if destructor is not None:
|
||||
destructor(cdata)
|
||||
|
||||
new_cdata = self.cast(self.typeof(cdata), cdata)
|
||||
assert new_cdata is not cdata
|
||||
weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor)
|
||||
return new_cdata
|
||||
|
||||
typeof = type
|
||||
|
||||
def getcname(self, BType, replace_with):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from .error import VerificationError
|
||||
|
||||
class CffiOp(object):
|
||||
def __init__(self, op, arg):
|
||||
|
|
@ -19,7 +20,6 @@ class CffiOp(object):
|
|||
% (self.arg,))
|
||||
return format_four_bytes(value)
|
||||
if isinstance(self.arg, str):
|
||||
from .ffiplatform import VerificationError
|
||||
raise VerificationError("cannot emit to Python: %r" % (self.arg,))
|
||||
return format_four_bytes((self.arg << 8) | self.op)
|
||||
|
||||
|
|
@ -105,8 +105,12 @@ PRIM_INT_FAST64 = 44
|
|||
PRIM_UINT_FAST64 = 45
|
||||
PRIM_INTMAX = 46
|
||||
PRIM_UINTMAX = 47
|
||||
PRIM_FLOATCOMPLEX = 48
|
||||
PRIM_DOUBLECOMPLEX = 49
|
||||
PRIM_CHAR16 = 50
|
||||
PRIM_CHAR32 = 51
|
||||
|
||||
_NUM_PRIM = 48
|
||||
_NUM_PRIM = 52
|
||||
_UNKNOWN_PRIM = -1
|
||||
_UNKNOWN_FLOAT_PRIM = -2
|
||||
_UNKNOWN_LONG_DOUBLE = -3
|
||||
|
|
@ -128,8 +132,12 @@ PRIMITIVE_TO_INDEX = {
|
|||
'float': PRIM_FLOAT,
|
||||
'double': PRIM_DOUBLE,
|
||||
'long double': PRIM_LONGDOUBLE,
|
||||
'float _Complex': PRIM_FLOATCOMPLEX,
|
||||
'double _Complex': PRIM_DOUBLECOMPLEX,
|
||||
'_Bool': PRIM_BOOL,
|
||||
'wchar_t': PRIM_WCHAR,
|
||||
'char16_t': PRIM_CHAR16,
|
||||
'char32_t': PRIM_CHAR32,
|
||||
'int8_t': PRIM_INT8,
|
||||
'uint8_t': PRIM_UINT8,
|
||||
'int16_t': PRIM_INT16,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import sys
|
||||
from . import api, model
|
||||
from . import model
|
||||
from .error import FFIError
|
||||
|
||||
|
||||
COMMON_TYPES = {}
|
||||
|
|
@ -31,12 +32,15 @@ def resolve_common_type(parser, commontype):
|
|||
elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
|
||||
result, quals = model.PrimitiveType(cdecl), 0
|
||||
elif cdecl == 'set-unicode-needed':
|
||||
raise api.FFIError("The Windows type %r is only available after "
|
||||
"you call ffi.set_unicode()" % (commontype,))
|
||||
raise FFIError("The Windows type %r is only available after "
|
||||
"you call ffi.set_unicode()" % (commontype,))
|
||||
else:
|
||||
if commontype == cdecl:
|
||||
raise api.FFIError("Unsupported type: %r. Please file a bug "
|
||||
"if you think it should be." % (commontype,))
|
||||
raise FFIError(
|
||||
"Unsupported type: %r. Please look at "
|
||||
"http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations "
|
||||
"and file an issue if you think this type should really "
|
||||
"be supported." % (commontype,))
|
||||
result, quals = parser.parse_type_and_quals(cdecl) # recursive
|
||||
|
||||
assert isinstance(result, model.BaseTypeByIdentity)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from . import api, model
|
||||
from . import model
|
||||
from .commontypes import COMMON_TYPES, resolve_common_type
|
||||
from .error import FFIError, CDefError
|
||||
try:
|
||||
from . import _pycparser as pycparser
|
||||
except ImportError:
|
||||
|
|
@ -15,6 +16,7 @@ try:
|
|||
except ImportError:
|
||||
lock = None
|
||||
|
||||
CDEF_SOURCE_STRING = "<cdef source string>"
|
||||
_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$",
|
||||
re.DOTALL | re.MULTILINE)
|
||||
_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)"
|
||||
|
|
@ -29,9 +31,13 @@ _r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE)
|
|||
_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b")
|
||||
_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b")
|
||||
_r_cdecl = re.compile(r"\b__cdecl\b")
|
||||
_r_extern_python = re.compile(r'\bextern\s*"Python"\s*.')
|
||||
_r_extern_python = re.compile(r'\bextern\s*"'
|
||||
r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.')
|
||||
_r_star_const_space = re.compile( # matches "* const "
|
||||
r"[*]\s*((const|volatile|restrict)\b\s*)+")
|
||||
_r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+"
|
||||
r"\.\.\.")
|
||||
_r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.")
|
||||
|
||||
def _get_parser():
|
||||
global _parser_cache
|
||||
|
|
@ -88,6 +94,12 @@ def _preprocess_extern_python(csource):
|
|||
# void __cffi_extern_python_start;
|
||||
# int foo(int);
|
||||
# void __cffi_extern_python_stop;
|
||||
#
|
||||
# input: `extern "Python+C" int foo(int);`
|
||||
# output:
|
||||
# void __cffi_extern_python_plus_c_start;
|
||||
# int foo(int);
|
||||
# void __cffi_extern_python_stop;
|
||||
parts = []
|
||||
while True:
|
||||
match = _r_extern_python.search(csource)
|
||||
|
|
@ -98,12 +110,15 @@ def _preprocess_extern_python(csource):
|
|||
#print ''.join(parts)+csource
|
||||
#print '=>'
|
||||
parts.append(csource[:match.start()])
|
||||
parts.append('void __cffi_extern_python_start; ')
|
||||
if 'C' in match.group(1):
|
||||
parts.append('void __cffi_extern_python_plus_c_start; ')
|
||||
else:
|
||||
parts.append('void __cffi_extern_python_start; ')
|
||||
if csource[endpos] == '{':
|
||||
# grouping variant
|
||||
closing = csource.find('}', endpos)
|
||||
if closing < 0:
|
||||
raise api.CDefError("'extern \"Python\" {': no '}' found")
|
||||
raise CDefError("'extern \"Python\" {': no '}' found")
|
||||
if csource.find('{', endpos + 1, closing) >= 0:
|
||||
raise NotImplementedError("cannot use { } inside a block "
|
||||
"'extern \"Python\" { ... }'")
|
||||
|
|
@ -113,7 +128,7 @@ def _preprocess_extern_python(csource):
|
|||
# non-grouping variant
|
||||
semicolon = csource.find(';', endpos)
|
||||
if semicolon < 0:
|
||||
raise api.CDefError("'extern \"Python\": no ';' found")
|
||||
raise CDefError("'extern \"Python\": no ';' found")
|
||||
parts.append(csource[endpos:semicolon+1])
|
||||
csource = csource[semicolon+1:]
|
||||
parts.append(' void __cffi_extern_python_stop;')
|
||||
|
|
@ -169,6 +184,10 @@ def _preprocess(csource):
|
|||
assert csource[p:p+3] == '...'
|
||||
csource = '%s __dotdotdot%d__ %s' % (csource[:p], number,
|
||||
csource[p+3:])
|
||||
# Replace "int ..." or "unsigned long int..." with "__dotdotdotint__"
|
||||
csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource)
|
||||
# Replace "float ..." or "double..." with "__dotdotdotfloat__"
|
||||
csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource)
|
||||
# Replace all remaining "..." with the same name, "__dotdotdot__",
|
||||
# which is declared with a typedef for the purpose of C parsing.
|
||||
return csource.replace('...', ' __dotdotdot__ '), macros
|
||||
|
|
@ -220,7 +239,7 @@ class Parser(object):
|
|||
self._included_declarations = set()
|
||||
self._anonymous_counter = 0
|
||||
self._structnode2type = weakref.WeakKeyDictionary()
|
||||
self._options = None
|
||||
self._options = {}
|
||||
self._int_constants = {}
|
||||
self._recomplete = []
|
||||
self._uses_new_feature = None
|
||||
|
|
@ -240,14 +259,21 @@ class Parser(object):
|
|||
ctn.discard(name)
|
||||
typenames += sorted(ctn)
|
||||
#
|
||||
csourcelines = ['typedef int %s;' % typename for typename in typenames]
|
||||
csourcelines.append('typedef int __dotdotdot__;')
|
||||
csourcelines = []
|
||||
csourcelines.append('# 1 "<cdef automatic initialization code>"')
|
||||
for typename in typenames:
|
||||
csourcelines.append('typedef int %s;' % typename)
|
||||
csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,'
|
||||
' __dotdotdot__;')
|
||||
# this forces pycparser to consider the following in the file
|
||||
# called <cdef source string> from line 1
|
||||
csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,))
|
||||
csourcelines.append(csource)
|
||||
csource = '\n'.join(csourcelines)
|
||||
fullcsource = '\n'.join(csourcelines)
|
||||
if lock is not None:
|
||||
lock.acquire() # pycparser is not thread-safe...
|
||||
try:
|
||||
ast = _get_parser().parse(csource)
|
||||
ast = _get_parser().parse(fullcsource)
|
||||
except pycparser.c_parser.ParseError as e:
|
||||
self.convert_pycparser_error(e, csource)
|
||||
finally:
|
||||
|
|
@ -257,17 +283,17 @@ class Parser(object):
|
|||
return ast, macros, csource
|
||||
|
||||
def _convert_pycparser_error(self, e, csource):
|
||||
# xxx look for ":NUM:" at the start of str(e) and try to interpret
|
||||
# it as a line number
|
||||
# xxx look for "<cdef source string>:NUM:" at the start of str(e)
|
||||
# and interpret that as a line number. This will not work if
|
||||
# the user gives explicit ``# NUM "FILE"`` directives.
|
||||
line = None
|
||||
msg = str(e)
|
||||
if msg.startswith(':') and ':' in msg[1:]:
|
||||
linenum = msg[1:msg.find(':',1)]
|
||||
if linenum.isdigit():
|
||||
linenum = int(linenum, 10)
|
||||
csourcelines = csource.splitlines()
|
||||
if 1 <= linenum <= len(csourcelines):
|
||||
line = csourcelines[linenum-1]
|
||||
match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg)
|
||||
if match:
|
||||
linenum = int(match.group(1), 10)
|
||||
csourcelines = csource.splitlines()
|
||||
if 1 <= linenum <= len(csourcelines):
|
||||
line = csourcelines[linenum-1]
|
||||
return line
|
||||
|
||||
def convert_pycparser_error(self, e, csource):
|
||||
|
|
@ -278,7 +304,7 @@ class Parser(object):
|
|||
msg = 'cannot parse "%s"\n%s' % (line.strip(), msg)
|
||||
else:
|
||||
msg = 'parse error\n%s' % (msg,)
|
||||
raise api.CDefError(msg)
|
||||
raise CDefError(msg)
|
||||
|
||||
def parse(self, csource, override=False, packed=False, dllexport=False):
|
||||
prev_options = self._options
|
||||
|
|
@ -300,33 +326,45 @@ class Parser(object):
|
|||
for decl in iterator:
|
||||
if decl.name == '__dotdotdot__':
|
||||
break
|
||||
else:
|
||||
assert 0
|
||||
current_decl = None
|
||||
#
|
||||
try:
|
||||
self._inside_extern_python = False
|
||||
self._inside_extern_python = '__cffi_extern_python_stop'
|
||||
for decl in iterator:
|
||||
current_decl = decl
|
||||
if isinstance(decl, pycparser.c_ast.Decl):
|
||||
self._parse_decl(decl)
|
||||
elif isinstance(decl, pycparser.c_ast.Typedef):
|
||||
if not decl.name:
|
||||
raise api.CDefError("typedef does not declare any name",
|
||||
decl)
|
||||
raise CDefError("typedef does not declare any name",
|
||||
decl)
|
||||
quals = 0
|
||||
if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType)
|
||||
and decl.type.type.names[-1] == '__dotdotdot__'):
|
||||
if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and
|
||||
decl.type.type.names[-1].startswith('__dotdotdot')):
|
||||
realtype = self._get_unknown_type(decl)
|
||||
elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and
|
||||
isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and
|
||||
isinstance(decl.type.type.type,
|
||||
pycparser.c_ast.IdentifierType) and
|
||||
decl.type.type.type.names == ['__dotdotdot__']):
|
||||
realtype = model.unknown_ptr_type(decl.name)
|
||||
decl.type.type.type.names[-1].startswith('__dotdotdot')):
|
||||
realtype = self._get_unknown_ptr_type(decl)
|
||||
else:
|
||||
realtype, quals = self._get_type_and_quals(
|
||||
decl.type, name=decl.name)
|
||||
decl.type, name=decl.name, partial_length_ok=True)
|
||||
self._declare('typedef ' + decl.name, realtype, quals=quals)
|
||||
elif decl.__class__.__name__ == 'Pragma':
|
||||
pass # skip pragma, only in pycparser 2.15
|
||||
else:
|
||||
raise api.CDefError("unrecognized construct", decl)
|
||||
except api.FFIError as e:
|
||||
raise CDefError("unexpected <%s>: this construct is valid "
|
||||
"C but not valid in cdef()" %
|
||||
decl.__class__.__name__, decl)
|
||||
except CDefError as e:
|
||||
if len(e.args) == 1:
|
||||
e.args = e.args + (current_decl,)
|
||||
raise
|
||||
except FFIError as e:
|
||||
msg = self._convert_pycparser_error(e, csource)
|
||||
if msg:
|
||||
e.args = (e.args[0] + "\n *** Err: %s" % msg,)
|
||||
|
|
@ -336,7 +374,7 @@ class Parser(object):
|
|||
if key in self._int_constants:
|
||||
if self._int_constants[key] == val:
|
||||
return # ignore identical double declarations
|
||||
raise api.FFIError(
|
||||
raise FFIError(
|
||||
"multiple declarations of constant: %s" % (key,))
|
||||
self._int_constants[key] = val
|
||||
|
||||
|
|
@ -363,7 +401,7 @@ class Parser(object):
|
|||
elif value == '...':
|
||||
self._declare('macro ' + key, value)
|
||||
else:
|
||||
raise api.CDefError(
|
||||
raise CDefError(
|
||||
'only supports one of the following syntax:\n'
|
||||
' #define %s ... (literally dot-dot-dot)\n'
|
||||
' #define %s NUMBER (with NUMBER an integer'
|
||||
|
|
@ -374,10 +412,12 @@ class Parser(object):
|
|||
|
||||
def _declare_function(self, tp, quals, decl):
|
||||
tp = self._get_type_pointer(tp, quals)
|
||||
if self._options['dllexport']:
|
||||
if self._options.get('dllexport'):
|
||||
tag = 'dllexport_python '
|
||||
elif self._inside_extern_python:
|
||||
elif self._inside_extern_python == '__cffi_extern_python_start':
|
||||
tag = 'extern_python '
|
||||
elif self._inside_extern_python == '__cffi_extern_python_plus_c_start':
|
||||
tag = 'extern_python_plus_c '
|
||||
else:
|
||||
tag = 'function '
|
||||
self._declare(tag + decl.name, tp)
|
||||
|
|
@ -396,8 +436,8 @@ class Parser(object):
|
|||
elif isinstance(node, pycparser.c_ast.Enum):
|
||||
self._get_struct_union_enum_type('enum', node)
|
||||
elif not decl.name:
|
||||
raise api.CDefError("construct does not declare any variable",
|
||||
decl)
|
||||
raise CDefError("construct does not declare any variable",
|
||||
decl)
|
||||
#
|
||||
if decl.name:
|
||||
tp, quals = self._get_type_and_quals(node,
|
||||
|
|
@ -421,12 +461,10 @@ class Parser(object):
|
|||
# hack: `extern "Python"` in the C source is replaced
|
||||
# with "void __cffi_extern_python_start;" and
|
||||
# "void __cffi_extern_python_stop;"
|
||||
self._inside_extern_python = not self._inside_extern_python
|
||||
assert self._inside_extern_python == (
|
||||
decl.name == '__cffi_extern_python_start')
|
||||
self._inside_extern_python = decl.name
|
||||
else:
|
||||
if self._inside_extern_python:
|
||||
raise api.CDefError(
|
||||
if self._inside_extern_python !='__cffi_extern_python_stop':
|
||||
raise CDefError(
|
||||
"cannot declare constants or "
|
||||
"variables with 'extern \"Python\"'")
|
||||
if (quals & model.Q_CONST) and not tp.is_array_type:
|
||||
|
|
@ -442,7 +480,7 @@ class Parser(object):
|
|||
assert not macros
|
||||
exprnode = ast.ext[-1].type.args.params[0]
|
||||
if isinstance(exprnode, pycparser.c_ast.ID):
|
||||
raise api.CDefError("unknown identifier '%s'" % (exprnode.name,))
|
||||
raise CDefError("unknown identifier '%s'" % (exprnode.name,))
|
||||
return self._get_type_and_quals(exprnode.type)
|
||||
|
||||
def _declare(self, name, obj, included=False, quals=0):
|
||||
|
|
@ -450,8 +488,8 @@ class Parser(object):
|
|||
prevobj, prevquals = self._declarations[name]
|
||||
if prevobj is obj and prevquals == quals:
|
||||
return
|
||||
if not self._options['override']:
|
||||
raise api.FFIError(
|
||||
if not self._options.get('override'):
|
||||
raise FFIError(
|
||||
"multiple declarations of %s (for interactive usage, "
|
||||
"try cdef(xx, override=True))" % (name,))
|
||||
assert '__dotdotdot__' not in name.split()
|
||||
|
|
@ -539,7 +577,7 @@ class Parser(object):
|
|||
if ident == 'void':
|
||||
return model.void_type, quals
|
||||
if ident == '__dotdotdot__':
|
||||
raise api.FFIError(':%d: bad usage of "..."' %
|
||||
raise FFIError(':%d: bad usage of "..."' %
|
||||
typenode.coord.line)
|
||||
tp0, quals0 = resolve_common_type(self, ident)
|
||||
return tp0, (quals | quals0)
|
||||
|
|
@ -571,14 +609,14 @@ class Parser(object):
|
|||
return self._get_struct_union_enum_type('union', typenode, name,
|
||||
nested=True), 0
|
||||
#
|
||||
raise api.FFIError(":%d: bad or unsupported type declaration" %
|
||||
raise FFIError(":%d: bad or unsupported type declaration" %
|
||||
typenode.coord.line)
|
||||
|
||||
def _parse_function_type(self, typenode, funcname=None):
|
||||
params = list(getattr(typenode.args, 'params', []))
|
||||
for i, arg in enumerate(params):
|
||||
if not hasattr(arg, 'type'):
|
||||
raise api.CDefError("%s arg %d: unknown type '%s'"
|
||||
raise CDefError("%s arg %d: unknown type '%s'"
|
||||
" (if you meant to use the old C syntax of giving"
|
||||
" untyped arguments, it is not supported)"
|
||||
% (funcname or 'in expression', i + 1,
|
||||
|
|
@ -592,7 +630,7 @@ class Parser(object):
|
|||
if ellipsis:
|
||||
params.pop()
|
||||
if not params:
|
||||
raise api.CDefError(
|
||||
raise CDefError(
|
||||
"%s: a function with only '(...)' as argument"
|
||||
" is not correct C" % (funcname or 'in expression'))
|
||||
args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type))
|
||||
|
|
@ -693,7 +731,7 @@ class Parser(object):
|
|||
return tp
|
||||
#
|
||||
if tp.fldnames is not None:
|
||||
raise api.CDefError("duplicate declaration of struct %s" % name)
|
||||
raise CDefError("duplicate declaration of struct %s" % name)
|
||||
fldnames = []
|
||||
fldtypes = []
|
||||
fldbitsize = []
|
||||
|
|
@ -729,7 +767,7 @@ class Parser(object):
|
|||
if isinstance(tp, model.StructType) and tp.partial:
|
||||
raise NotImplementedError("%s: using both bitfields and '...;'"
|
||||
% (tp,))
|
||||
tp.packed = self._options['packed']
|
||||
tp.packed = self._options.get('packed')
|
||||
if tp.completed: # must be re-completed: it is not opaque any more
|
||||
tp.completed = 0
|
||||
self._recomplete.append(tp)
|
||||
|
|
@ -737,7 +775,7 @@ class Parser(object):
|
|||
|
||||
def _make_partial(self, tp, nested):
|
||||
if not isinstance(tp, model.StructOrUnion):
|
||||
raise api.CDefError("%s cannot be partial" % (tp,))
|
||||
raise CDefError("%s cannot be partial" % (tp,))
|
||||
if not tp.has_c_name() and not nested:
|
||||
raise NotImplementedError("%s is partial but has no C name" %(tp,))
|
||||
tp.partial = True
|
||||
|
|
@ -757,7 +795,7 @@ class Parser(object):
|
|||
len(s) == 3 or (len(s) == 4 and s[1] == "\\")):
|
||||
return ord(s[-2])
|
||||
else:
|
||||
raise api.CDefError("invalid constant %r" % (s,))
|
||||
raise CDefError("invalid constant %r" % (s,))
|
||||
#
|
||||
if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and
|
||||
exprnode.op == '+'):
|
||||
|
|
@ -771,14 +809,27 @@ class Parser(object):
|
|||
exprnode.name in self._int_constants):
|
||||
return self._int_constants[exprnode.name]
|
||||
#
|
||||
if partial_length_ok:
|
||||
if (isinstance(exprnode, pycparser.c_ast.ID) and
|
||||
if (isinstance(exprnode, pycparser.c_ast.ID) and
|
||||
exprnode.name == '__dotdotdotarray__'):
|
||||
if partial_length_ok:
|
||||
self._partial_length = True
|
||||
return '...'
|
||||
raise FFIError(":%d: unsupported '[...]' here, cannot derive "
|
||||
"the actual array length in this context"
|
||||
% exprnode.coord.line)
|
||||
#
|
||||
raise api.FFIError(":%d: unsupported expression: expected a "
|
||||
"simple numeric constant" % exprnode.coord.line)
|
||||
if (isinstance(exprnode, pycparser.c_ast.BinaryOp) and
|
||||
exprnode.op == '+'):
|
||||
return (self._parse_constant(exprnode.left) +
|
||||
self._parse_constant(exprnode.right))
|
||||
#
|
||||
if (isinstance(exprnode, pycparser.c_ast.BinaryOp) and
|
||||
exprnode.op == '-'):
|
||||
return (self._parse_constant(exprnode.left) -
|
||||
self._parse_constant(exprnode.right))
|
||||
#
|
||||
raise FFIError(":%d: unsupported expression: expected a "
|
||||
"simple numeric constant" % exprnode.coord.line)
|
||||
|
||||
def _build_enum_type(self, explicit_name, decls):
|
||||
if decls is not None:
|
||||
|
|
@ -816,24 +867,25 @@ class Parser(object):
|
|||
|
||||
def _get_unknown_type(self, decl):
|
||||
typenames = decl.type.type.names
|
||||
assert typenames[-1] == '__dotdotdot__'
|
||||
if len(typenames) == 1:
|
||||
if typenames == ['__dotdotdot__']:
|
||||
return model.unknown_type(decl.name)
|
||||
|
||||
if (typenames[:-1] == ['float'] or
|
||||
typenames[:-1] == ['double']):
|
||||
# not for 'long double' so far
|
||||
result = model.UnknownFloatType(decl.name)
|
||||
else:
|
||||
for t in typenames[:-1]:
|
||||
if t not in ['int', 'short', 'long', 'signed',
|
||||
'unsigned', 'char']:
|
||||
raise api.FFIError(':%d: bad usage of "..."' %
|
||||
decl.coord.line)
|
||||
result = model.UnknownIntegerType(decl.name)
|
||||
if typenames == ['__dotdotdotint__']:
|
||||
if self._uses_new_feature is None:
|
||||
self._uses_new_feature = "'typedef int... %s'" % decl.name
|
||||
return model.UnknownIntegerType(decl.name)
|
||||
|
||||
if self._uses_new_feature is None:
|
||||
self._uses_new_feature = "'typedef %s... %s'" % (
|
||||
' '.join(typenames[:-1]), decl.name)
|
||||
if typenames == ['__dotdotdotfloat__']:
|
||||
# note: not for 'long double' so far
|
||||
if self._uses_new_feature is None:
|
||||
self._uses_new_feature = "'typedef float... %s'" % decl.name
|
||||
return model.UnknownFloatType(decl.name)
|
||||
|
||||
return result
|
||||
raise FFIError(':%d: unsupported usage of "..." in typedef'
|
||||
% decl.coord.line)
|
||||
|
||||
def _get_unknown_ptr_type(self, decl):
|
||||
if decl.type.type.type.names == ['__dotdotdot__']:
|
||||
return model.unknown_ptr_type(decl.name)
|
||||
raise FFIError(':%d: unsupported usage of "..." in typedef'
|
||||
% decl.coord.line)
|
||||
|
|
|
|||
23
Lib/site-packages/cffi/error.py
Normal file
23
Lib/site-packages/cffi/error.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
class FFIError(Exception):
|
||||
pass
|
||||
|
||||
class CDefError(Exception):
|
||||
def __str__(self):
|
||||
try:
|
||||
current_decl = self.args[1]
|
||||
filename = current_decl.coord.file
|
||||
linenum = current_decl.coord.line
|
||||
prefix = '%s:%d: ' % (filename, linenum)
|
||||
except (AttributeError, TypeError, IndexError):
|
||||
prefix = ''
|
||||
return '%s%s' % (prefix, self.args[0])
|
||||
|
||||
class VerificationError(Exception):
|
||||
""" An error raised when verification fails
|
||||
"""
|
||||
|
||||
class VerificationMissing(Exception):
|
||||
""" An error raised when incomplete structures are passed into
|
||||
cdef, but no verification has been done
|
||||
"""
|
||||
|
|
@ -1,32 +1,25 @@
|
|||
import sys, os
|
||||
|
||||
|
||||
class VerificationError(Exception):
|
||||
""" An error raised when verification fails
|
||||
"""
|
||||
|
||||
class VerificationMissing(Exception):
|
||||
""" An error raised when incomplete structures are passed into
|
||||
cdef, but no verification has been done
|
||||
"""
|
||||
from .error import VerificationError
|
||||
|
||||
|
||||
LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs',
|
||||
'extra_objects', 'depends']
|
||||
|
||||
def get_extension(srcfilename, modname, sources=(), **kwds):
|
||||
_hack_at_distutils()
|
||||
from distutils.core import Extension
|
||||
allsources = [srcfilename]
|
||||
for src in sources:
|
||||
allsources.append(os.path.normpath(src))
|
||||
return Extension(name=modname, sources=allsources, **kwds)
|
||||
|
||||
def compile(tmpdir, ext, compiler_verbose=0):
|
||||
def compile(tmpdir, ext, compiler_verbose=0, debug=None):
|
||||
"""Compile a C extension module using distutils."""
|
||||
|
||||
_hack_at_distutils()
|
||||
saved_environ = os.environ.copy()
|
||||
try:
|
||||
outputfilename = _build(tmpdir, ext, compiler_verbose)
|
||||
outputfilename = _build(tmpdir, ext, compiler_verbose, debug)
|
||||
outputfilename = os.path.abspath(outputfilename)
|
||||
finally:
|
||||
# workaround for a distutils bugs where some env vars can
|
||||
|
|
@ -36,7 +29,7 @@ def compile(tmpdir, ext, compiler_verbose=0):
|
|||
os.environ[key] = value
|
||||
return outputfilename
|
||||
|
||||
def _build(tmpdir, ext, compiler_verbose=0):
|
||||
def _build(tmpdir, ext, compiler_verbose=0, debug=None):
|
||||
# XXX compact but horrible :-(
|
||||
from distutils.core import Distribution
|
||||
import distutils.errors, distutils.log
|
||||
|
|
@ -44,6 +37,9 @@ def _build(tmpdir, ext, compiler_verbose=0):
|
|||
dist = Distribution({'ext_modules': [ext]})
|
||||
dist.parse_config_files()
|
||||
options = dist.get_option_dict('build_ext')
|
||||
if debug is None:
|
||||
debug = sys.flags.debug
|
||||
options['debug'] = ('ffiplatform', debug)
|
||||
options['force'] = ('ffiplatform', True)
|
||||
options['build_lib'] = ('ffiplatform', tmpdir)
|
||||
options['build_temp'] = ('ffiplatform', tmpdir)
|
||||
|
|
@ -119,3 +115,13 @@ def flatten(x):
|
|||
f = cStringIO.StringIO()
|
||||
_flatten(x, f)
|
||||
return f.getvalue()
|
||||
|
||||
def _hack_at_distutils():
|
||||
# Windows-only workaround for some configurations: see
|
||||
# https://bugs.python.org/issue23246 (Python 2.7 with
|
||||
# a specific MS compiler suite download)
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
import setuptools # for side-effects, patches distutils
|
||||
except ImportError:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
from weakref import ref
|
||||
|
||||
|
||||
class GcWeakrefs(object):
|
||||
def __init__(self, ffi):
|
||||
self.ffi = ffi
|
||||
self.data = {}
|
||||
|
||||
def build(self, cdata, destructor):
|
||||
# make a new cdata of the same type as the original one
|
||||
new_cdata = self.ffi.cast(self.ffi._backend.typeof(cdata), cdata)
|
||||
#
|
||||
def remove(key):
|
||||
# careful, this function is not protected by any lock
|
||||
old_key = self.data.pop(index)
|
||||
assert old_key is key
|
||||
destructor(cdata)
|
||||
#
|
||||
key = ref(new_cdata, remove)
|
||||
index = object()
|
||||
self.data[index] = key
|
||||
return new_cdata
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import types, sys
|
||||
import types
|
||||
import weakref
|
||||
|
||||
from .lock import allocate_lock
|
||||
|
||||
from .error import CDefError, VerificationError, VerificationMissing
|
||||
|
||||
# type qualifiers
|
||||
Q_CONST = 0x01
|
||||
|
|
@ -39,7 +39,6 @@ class BaseTypeByIdentity(object):
|
|||
replace_with = qualify(quals, replace_with)
|
||||
result = result.replace('&', replace_with)
|
||||
if '$' in result:
|
||||
from .ffiplatform import VerificationError
|
||||
raise VerificationError(
|
||||
"cannot generate '%s' in %s: unknown type name"
|
||||
% (self._get_c_name(), context))
|
||||
|
|
@ -96,7 +95,8 @@ void_type = VoidType()
|
|||
|
||||
|
||||
class BasePrimitiveType(BaseType):
|
||||
pass
|
||||
def is_complex_type(self):
|
||||
return False
|
||||
|
||||
|
||||
class PrimitiveType(BasePrimitiveType):
|
||||
|
|
@ -117,9 +117,13 @@ class PrimitiveType(BasePrimitiveType):
|
|||
'float': 'f',
|
||||
'double': 'f',
|
||||
'long double': 'f',
|
||||
'float _Complex': 'j',
|
||||
'double _Complex': 'j',
|
||||
'_Bool': 'i',
|
||||
# the following types are not primitive in the C sense
|
||||
'wchar_t': 'c',
|
||||
'char16_t': 'c',
|
||||
'char32_t': 'c',
|
||||
'int8_t': 'i',
|
||||
'uint8_t': 'i',
|
||||
'int16_t': 'i',
|
||||
|
|
@ -164,6 +168,8 @@ class PrimitiveType(BasePrimitiveType):
|
|||
return self.ALL_PRIMITIVE_TYPES[self.name] == 'i'
|
||||
def is_float_type(self):
|
||||
return self.ALL_PRIMITIVE_TYPES[self.name] == 'f'
|
||||
def is_complex_type(self):
|
||||
return self.ALL_PRIMITIVE_TYPES[self.name] == 'j'
|
||||
|
||||
def build_backend_type(self, ffi, finishlist):
|
||||
return global_cache(self, ffi, 'new_primitive_type', self.name)
|
||||
|
|
@ -223,9 +229,8 @@ class RawFunctionType(BaseFunctionType):
|
|||
is_raw_function = True
|
||||
|
||||
def build_backend_type(self, ffi, finishlist):
|
||||
from . import api
|
||||
raise api.CDefError("cannot render the type %r: it is a function "
|
||||
"type, not a pointer-to-function type" % (self,))
|
||||
raise CDefError("cannot render the type %r: it is a function "
|
||||
"type, not a pointer-to-function type" % (self,))
|
||||
|
||||
def as_function_pointer(self):
|
||||
return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi)
|
||||
|
|
@ -307,9 +312,8 @@ class ArrayType(BaseType):
|
|||
|
||||
def build_backend_type(self, ffi, finishlist):
|
||||
if self.length == '...':
|
||||
from . import api
|
||||
raise api.CDefError("cannot render the type %r: unknown length" %
|
||||
(self,))
|
||||
raise CDefError("cannot render the type %r: unknown length" %
|
||||
(self,))
|
||||
self.item.get_cached_btype(ffi, finishlist) # force the item BType
|
||||
BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist)
|
||||
return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length)
|
||||
|
|
@ -348,21 +352,20 @@ class StructOrUnion(StructOrUnionOrEnum):
|
|||
self.fldquals = fldquals
|
||||
self.build_c_name_with_marker()
|
||||
|
||||
def has_anonymous_struct_fields(self):
|
||||
if self.fldtypes is None:
|
||||
return False
|
||||
for name, type in zip(self.fldnames, self.fldtypes):
|
||||
if name == '' and isinstance(type, StructOrUnion):
|
||||
return True
|
||||
return False
|
||||
def anonymous_struct_fields(self):
|
||||
if self.fldtypes is not None:
|
||||
for name, type in zip(self.fldnames, self.fldtypes):
|
||||
if name == '' and isinstance(type, StructOrUnion):
|
||||
yield type
|
||||
|
||||
def enumfields(self):
|
||||
def enumfields(self, expand_anonymous_struct_union=True):
|
||||
fldquals = self.fldquals
|
||||
if fldquals is None:
|
||||
fldquals = (0,) * len(self.fldnames)
|
||||
for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes,
|
||||
self.fldbitsize, fldquals):
|
||||
if name == '' and isinstance(type, StructOrUnion):
|
||||
if (name == '' and isinstance(type, StructOrUnion)
|
||||
and expand_anonymous_struct_union):
|
||||
# nested anonymous struct/union
|
||||
for result in type.enumfields():
|
||||
yield result
|
||||
|
|
@ -455,13 +458,11 @@ class StructOrUnion(StructOrUnionOrEnum):
|
|||
self.completed = 2
|
||||
|
||||
def _verification_error(self, msg):
|
||||
from .ffiplatform import VerificationError
|
||||
raise VerificationError(msg)
|
||||
|
||||
def check_not_partial(self):
|
||||
if self.partial and self.fixedlayout is None:
|
||||
from . import ffiplatform
|
||||
raise ffiplatform.VerificationMissing(self._get_c_name())
|
||||
raise VerificationMissing(self._get_c_name())
|
||||
|
||||
def build_backend_type(self, ffi, finishlist):
|
||||
self.check_not_partial()
|
||||
|
|
@ -499,8 +500,7 @@ class EnumType(StructOrUnionOrEnum):
|
|||
|
||||
def check_not_partial(self):
|
||||
if self.partial and not self.partial_resolved:
|
||||
from . import ffiplatform
|
||||
raise ffiplatform.VerificationMissing(self._get_c_name())
|
||||
raise VerificationMissing(self._get_c_name())
|
||||
|
||||
def build_backend_type(self, ffi, finishlist):
|
||||
self.check_not_partial()
|
||||
|
|
@ -514,15 +514,20 @@ class EnumType(StructOrUnionOrEnum):
|
|||
if self.baseinttype is not None:
|
||||
return self.baseinttype.get_cached_btype(ffi, finishlist)
|
||||
#
|
||||
from . import api
|
||||
if self.enumvalues:
|
||||
smallest_value = min(self.enumvalues)
|
||||
largest_value = max(self.enumvalues)
|
||||
else:
|
||||
import warnings
|
||||
warnings.warn("%r has no values explicitly defined; next version "
|
||||
"will refuse to guess which integer type it is "
|
||||
"meant to be (unsigned/signed, int/long)"
|
||||
try:
|
||||
# XXX! The goal is to ensure that the warnings.warn()
|
||||
# will not suppress the warning. We want to get it
|
||||
# several times if we reach this point several times.
|
||||
__warningregistry__.clear()
|
||||
except NameError:
|
||||
pass
|
||||
warnings.warn("%r has no values explicitly defined; "
|
||||
"guessing that it is equivalent to 'unsigned int'"
|
||||
% self._get_c_name())
|
||||
smallest_value = largest_value = 0
|
||||
if smallest_value < 0: # needs a signed type
|
||||
|
|
@ -543,8 +548,8 @@ class EnumType(StructOrUnionOrEnum):
|
|||
if (smallest_value >= ((-1) << (8*size2-1)) and
|
||||
largest_value < (1 << (8*size2-sign))):
|
||||
return btype2
|
||||
raise api.CDefError("%s values don't all fit into either 'long' "
|
||||
"or 'unsigned long'" % self._get_c_name())
|
||||
raise CDefError("%s values don't all fit into either 'long' "
|
||||
"or 'unsigned long'" % self._get_c_name())
|
||||
|
||||
def unknown_type(name, structname=None):
|
||||
if structname is None:
|
||||
|
|
@ -562,22 +567,26 @@ def unknown_ptr_type(name, structname=None):
|
|||
|
||||
|
||||
global_lock = allocate_lock()
|
||||
_typecache_cffi_backend = weakref.WeakValueDictionary()
|
||||
|
||||
def get_typecache(backend):
|
||||
# returns _typecache_cffi_backend if backend is the _cffi_backend
|
||||
# module, or type(backend).__typecache if backend is an instance of
|
||||
# CTypesBackend (or some FakeBackend class during tests)
|
||||
if isinstance(backend, types.ModuleType):
|
||||
return _typecache_cffi_backend
|
||||
with global_lock:
|
||||
if not hasattr(type(backend), '__typecache'):
|
||||
type(backend).__typecache = weakref.WeakValueDictionary()
|
||||
return type(backend).__typecache
|
||||
|
||||
def global_cache(srctype, ffi, funcname, *args, **kwds):
|
||||
key = kwds.pop('key', (funcname, args))
|
||||
assert not kwds
|
||||
try:
|
||||
return ffi._backend.__typecache[key]
|
||||
return ffi._typecache[key]
|
||||
except KeyError:
|
||||
pass
|
||||
except AttributeError:
|
||||
# initialize the __typecache attribute, either at the module level
|
||||
# if ffi._backend is a module, or at the class level if ffi._backend
|
||||
# is some instance.
|
||||
if isinstance(ffi._backend, types.ModuleType):
|
||||
ffi._backend.__typecache = weakref.WeakValueDictionary()
|
||||
else:
|
||||
type(ffi._backend).__typecache = weakref.WeakValueDictionary()
|
||||
try:
|
||||
res = getattr(ffi._backend, funcname)(*args)
|
||||
except NotImplementedError as e:
|
||||
|
|
@ -585,7 +594,7 @@ def global_cache(srctype, ffi, funcname, *args, **kwds):
|
|||
# note that setdefault() on WeakValueDictionary is not atomic
|
||||
# and contains a rare bug (http://bugs.python.org/issue19542);
|
||||
# we have to use a lock and do it ourselves
|
||||
cache = ffi._backend.__typecache
|
||||
cache = ffi._typecache
|
||||
with global_lock:
|
||||
res1 = cache.get(key)
|
||||
if res1 is None:
|
||||
|
|
|
|||
|
|
@ -79,8 +79,12 @@ typedef void *_cffi_opcode_t;
|
|||
#define _CFFI_PRIM_UINT_FAST64 45
|
||||
#define _CFFI_PRIM_INTMAX 46
|
||||
#define _CFFI_PRIM_UINTMAX 47
|
||||
#define _CFFI_PRIM_FLOATCOMPLEX 48
|
||||
#define _CFFI_PRIM_DOUBLECOMPLEX 49
|
||||
#define _CFFI_PRIM_CHAR16 50
|
||||
#define _CFFI_PRIM_CHAR32 51
|
||||
|
||||
#define _CFFI__NUM_PRIM 48
|
||||
#define _CFFI__NUM_PRIM 52
|
||||
#define _CFFI__UNKNOWN_PRIM (-1)
|
||||
#define _CFFI__UNKNOWN_FLOAT_PRIM (-2)
|
||||
#define _CFFI__UNKNOWN_LONG_DOUBLE (-3)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import os, sys, io
|
||||
from . import ffiplatform, model
|
||||
from .error import VerificationError
|
||||
from .cffi_opcode import *
|
||||
|
||||
VERSION = "0x2601"
|
||||
VERSION_EMBEDDED = "0x2701"
|
||||
VERSION_BASE = 0x2601
|
||||
VERSION_EMBEDDED = 0x2701
|
||||
VERSION_CHAR16CHAR32 = 0x2801
|
||||
|
||||
|
||||
class GlobalExpr:
|
||||
|
|
@ -125,6 +127,10 @@ class Recompiler:
|
|||
self.ffi = ffi
|
||||
self.module_name = module_name
|
||||
self.target_is_python = target_is_python
|
||||
self._version = VERSION_BASE
|
||||
|
||||
def needs_version(self, ver):
|
||||
self._version = max(self._version, ver)
|
||||
|
||||
def collect_type_table(self):
|
||||
self._typesdict = {}
|
||||
|
|
@ -211,7 +217,7 @@ class Recompiler:
|
|||
method = getattr(self, '_generate_cpy_%s_%s' % (kind,
|
||||
step_name))
|
||||
except AttributeError:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"not implemented in recompile(): %r" % name)
|
||||
try:
|
||||
self._current_quals = quals
|
||||
|
|
@ -275,6 +281,8 @@ class Recompiler:
|
|||
def write_c_source_to_f(self, f, preamble):
|
||||
self._f = f
|
||||
prnt = self._prnt
|
||||
if self.ffi._embedding is not None:
|
||||
prnt('#define _CFFI_USE_EMBEDDING')
|
||||
#
|
||||
# first the '#include' (actually done by inlining the file's content)
|
||||
lines = self._rel_readlines('_cffi_include.h')
|
||||
|
|
@ -287,8 +295,9 @@ class Recompiler:
|
|||
base_module_name = self.module_name.split('.')[-1]
|
||||
if self.ffi._embedding is not None:
|
||||
prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,))
|
||||
prnt('#define _CFFI_PYTHON_STARTUP_CODE %s' %
|
||||
(self._string_literal(self.ffi._embedding),))
|
||||
prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {')
|
||||
self._print_string_literal_in_array(self.ffi._embedding)
|
||||
prnt('0 };')
|
||||
prnt('#ifdef PYPY_VERSION')
|
||||
prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % (
|
||||
base_module_name,))
|
||||
|
|
@ -300,10 +309,10 @@ class Recompiler:
|
|||
base_module_name,))
|
||||
prnt('#endif')
|
||||
lines = self._rel_readlines('_embedding.h')
|
||||
i = lines.index('#include "_cffi_errors.h"\n')
|
||||
lines[i:i+1] = self._rel_readlines('_cffi_errors.h')
|
||||
prnt(''.join(lines))
|
||||
version = VERSION_EMBEDDED
|
||||
else:
|
||||
version = VERSION
|
||||
self.needs_version(VERSION_EMBEDDED)
|
||||
#
|
||||
# then paste the C source given by the user, verbatim.
|
||||
prnt('/************************************************************/')
|
||||
|
|
@ -352,12 +361,12 @@ class Recompiler:
|
|||
included_module_name, included_source = (
|
||||
ffi_to_include._assigned_source[:2])
|
||||
except AttributeError:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"ffi object %r includes %r, but the latter has not "
|
||||
"been prepared with set_source()" % (
|
||||
self.ffi, ffi_to_include,))
|
||||
if included_source is None:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"not implemented yet: ffi.include() of a Python-based "
|
||||
"ffi inside a C-based ffi")
|
||||
prnt(' "%s",' % (included_module_name,))
|
||||
|
|
@ -389,6 +398,10 @@ class Recompiler:
|
|||
prnt()
|
||||
#
|
||||
# the init function
|
||||
prnt('#ifdef __GNUC__')
|
||||
prnt('# pragma GCC visibility push(default) /* for -fvisibility= */')
|
||||
prnt('#endif')
|
||||
prnt()
|
||||
prnt('#ifdef PYPY_VERSION')
|
||||
prnt('PyMODINIT_FUNC')
|
||||
prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,))
|
||||
|
|
@ -398,8 +411,11 @@ class Recompiler:
|
|||
prnt(' _cffi_call_python_org = '
|
||||
'(void(*)(struct _cffi_externpy_s *, char *))p[1];')
|
||||
prnt(' }')
|
||||
prnt(' p[0] = (const void *)%s;' % version)
|
||||
prnt(' p[0] = (const void *)0x%x;' % self._version)
|
||||
prnt(' p[1] = &_cffi_type_context;')
|
||||
prnt('#if PY_MAJOR_VERSION >= 3')
|
||||
prnt(' return NULL;')
|
||||
prnt('#endif')
|
||||
prnt('}')
|
||||
# on Windows, distutils insists on putting init_cffi_xyz in
|
||||
# 'export_symbols', so instead of fighting it, just give up and
|
||||
|
|
@ -416,17 +432,22 @@ class Recompiler:
|
|||
prnt('PyMODINIT_FUNC')
|
||||
prnt('PyInit_%s(void)' % (base_module_name,))
|
||||
prnt('{')
|
||||
prnt(' return _cffi_init("%s", %s, &_cffi_type_context);' % (
|
||||
self.module_name, version))
|
||||
prnt(' return _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
|
||||
self.module_name, self._version))
|
||||
prnt('}')
|
||||
prnt('#else')
|
||||
prnt('PyMODINIT_FUNC')
|
||||
prnt('init%s(void)' % (base_module_name,))
|
||||
prnt('{')
|
||||
prnt(' _cffi_init("%s", %s, &_cffi_type_context);' % (
|
||||
self.module_name, version))
|
||||
prnt(' _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
|
||||
self.module_name, self._version))
|
||||
prnt('}')
|
||||
prnt('#endif')
|
||||
prnt()
|
||||
prnt('#ifdef __GNUC__')
|
||||
prnt('# pragma GCC visibility pop')
|
||||
prnt('#endif')
|
||||
self._version = None
|
||||
|
||||
def _to_py(self, x):
|
||||
if isinstance(x, str):
|
||||
|
|
@ -454,18 +475,19 @@ class Recompiler:
|
|||
included_module_name, included_source = (
|
||||
ffi_to_include._assigned_source[:2])
|
||||
except AttributeError:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"ffi object %r includes %r, but the latter has not "
|
||||
"been prepared with set_source()" % (
|
||||
self.ffi, ffi_to_include,))
|
||||
if included_source is not None:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"not implemented yet: ffi.include() of a C-based "
|
||||
"ffi inside a Python-based ffi")
|
||||
prnt('from %s import ffi as _ffi%d' % (included_module_name, i))
|
||||
prnt()
|
||||
prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,))
|
||||
prnt(" _version = %s," % (VERSION,))
|
||||
prnt(" _version = 0x%x," % (self._version,))
|
||||
self._version = None
|
||||
#
|
||||
# the '_types' keyword argument
|
||||
self.cffi_types = tuple(self.cffi_types) # don't change any more
|
||||
|
|
@ -495,7 +517,7 @@ class Recompiler:
|
|||
|
||||
def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
|
||||
extraarg = ''
|
||||
if isinstance(tp, model.BasePrimitiveType):
|
||||
if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type():
|
||||
if tp.is_integer_type() and tp.name != '_Bool':
|
||||
converter = '_cffi_to_c_int'
|
||||
extraarg = ', %s' % tp.name
|
||||
|
|
@ -504,8 +526,11 @@ class Recompiler:
|
|||
# double' here, and _cffi_to_c_double would loose precision
|
||||
converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),)
|
||||
else:
|
||||
converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''),
|
||||
cname = tp.get_c_name('')
|
||||
converter = '(%s)_cffi_to_c_%s' % (cname,
|
||||
tp.name.replace(' ', '_'))
|
||||
if cname in ('char16_t', 'char32_t'):
|
||||
self.needs_version(VERSION_CHAR16CHAR32)
|
||||
errvalue = '-1'
|
||||
#
|
||||
elif isinstance(tp, model.PointerType):
|
||||
|
|
@ -513,8 +538,10 @@ class Recompiler:
|
|||
tovar, errcode)
|
||||
return
|
||||
#
|
||||
elif isinstance(tp, (model.StructOrUnion, model.EnumType)):
|
||||
# a struct (not a struct pointer) as a function argument
|
||||
elif (isinstance(tp, model.StructOrUnionOrEnum) or
|
||||
isinstance(tp, model.BasePrimitiveType)):
|
||||
# a struct (not a struct pointer) as a function argument;
|
||||
# or, a complex (the same code works)
|
||||
self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
|
||||
% (tovar, self._gettypenum(tp), fromvar))
|
||||
self._prnt(' %s;' % errcode)
|
||||
|
|
@ -555,12 +582,15 @@ class Recompiler:
|
|||
|
||||
def _convert_expr_from_c(self, tp, var, context):
|
||||
if isinstance(tp, model.BasePrimitiveType):
|
||||
if tp.is_integer_type():
|
||||
if tp.is_integer_type() and tp.name != '_Bool':
|
||||
return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
|
||||
elif isinstance(tp, model.UnknownFloatType):
|
||||
return '_cffi_from_c_double(%s)' % (var,)
|
||||
elif tp.name != 'long double':
|
||||
return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
|
||||
elif tp.name != 'long double' and not tp.is_complex_type():
|
||||
cname = tp.name.replace(' ', '_')
|
||||
if cname in ('char16_t', 'char32_t'):
|
||||
self.needs_version(VERSION_CHAR16CHAR32)
|
||||
return '_cffi_from_c_%s(%s)' % (cname, var)
|
||||
else:
|
||||
return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
|
||||
var, self._gettypenum(tp))
|
||||
|
|
@ -570,7 +600,7 @@ class Recompiler:
|
|||
elif isinstance(tp, model.ArrayType):
|
||||
return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
|
||||
var, self._gettypenum(model.PointerType(tp.item)))
|
||||
elif isinstance(tp, model.StructType):
|
||||
elif isinstance(tp, model.StructOrUnion):
|
||||
if tp.fldnames is None:
|
||||
raise TypeError("'%s' is used as %s, but is opaque" % (
|
||||
tp._get_c_name(), context))
|
||||
|
|
@ -585,8 +615,11 @@ class Recompiler:
|
|||
# ----------
|
||||
# typedefs
|
||||
|
||||
def _typedef_type(self, tp, name):
|
||||
return self._global_type(tp, "(*(%s *)0)" % (name,))
|
||||
|
||||
def _generate_cpy_typedef_collecttype(self, tp, name):
|
||||
self._do_collect_type(tp)
|
||||
self._do_collect_type(self._typedef_type(tp, name))
|
||||
|
||||
def _generate_cpy_typedef_decl(self, tp, name):
|
||||
pass
|
||||
|
|
@ -596,6 +629,7 @@ class Recompiler:
|
|||
self._lsts["typename"].append(TypenameExpr(name, type_index))
|
||||
|
||||
def _generate_cpy_typedef_ctx(self, tp, name):
|
||||
tp = self._typedef_type(tp, name)
|
||||
self._typedef_ctx(tp, name)
|
||||
if getattr(tp, "origin", None) == "unknown_type":
|
||||
self._struct_ctx(tp, tp.name, approxname=None)
|
||||
|
|
@ -683,13 +717,11 @@ class Recompiler:
|
|||
rng = range(len(tp.args))
|
||||
for i in rng:
|
||||
prnt(' PyObject *arg%d;' % i)
|
||||
prnt(' PyObject **aa;')
|
||||
prnt()
|
||||
prnt(' aa = _cffi_unpack_args(args, %d, "%s");' % (len(rng), name))
|
||||
prnt(' if (aa == NULL)')
|
||||
prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % (
|
||||
name, len(rng), len(rng),
|
||||
', '.join(['&arg%d' % i for i in rng])))
|
||||
prnt(' return NULL;')
|
||||
for i in rng:
|
||||
prnt(' arg%d = aa[%d];' % (i, i))
|
||||
prnt()
|
||||
#
|
||||
for i, type in enumerate(tp.args):
|
||||
|
|
@ -721,21 +753,26 @@ class Recompiler:
|
|||
#
|
||||
# the PyPy version: need to replace struct/union arguments with
|
||||
# pointers, and if the result is a struct/union, insert a first
|
||||
# arg that is a pointer to the result.
|
||||
# arg that is a pointer to the result. We also do that for
|
||||
# complex args and return type.
|
||||
def need_indirection(type):
|
||||
return (isinstance(type, model.StructOrUnion) or
|
||||
(isinstance(type, model.PrimitiveType) and
|
||||
type.is_complex_type()))
|
||||
difference = False
|
||||
arguments = []
|
||||
call_arguments = []
|
||||
context = 'argument of %s' % name
|
||||
for i, type in enumerate(tp.args):
|
||||
indirection = ''
|
||||
if isinstance(type, model.StructOrUnion):
|
||||
if need_indirection(type):
|
||||
indirection = '*'
|
||||
difference = True
|
||||
arg = type.get_c_name(' %sx%d' % (indirection, i), context)
|
||||
arguments.append(arg)
|
||||
call_arguments.append('%sx%d' % (indirection, i))
|
||||
tp_result = tp.result
|
||||
if isinstance(tp_result, model.StructOrUnion):
|
||||
if need_indirection(tp_result):
|
||||
context = 'result of %s' % name
|
||||
arg = tp_result.get_c_name(' *result', context)
|
||||
arguments.insert(0, arg)
|
||||
|
|
@ -799,6 +836,10 @@ class Recompiler:
|
|||
|
||||
def _struct_collecttype(self, tp):
|
||||
self._do_collect_type(tp)
|
||||
if self.target_is_python:
|
||||
# also requires nested anon struct/unions in ABI mode, recursively
|
||||
for fldtype in tp.anonymous_struct_fields():
|
||||
self._struct_collecttype(fldtype)
|
||||
|
||||
def _struct_decl(self, tp, cname, approxname):
|
||||
if tp.fldtypes is None:
|
||||
|
|
@ -814,7 +855,7 @@ class Recompiler:
|
|||
try:
|
||||
if ftype.is_integer_type() or fbitsize >= 0:
|
||||
# accept all integers, but complain on float or double
|
||||
prnt(" (void)((p->%s) << 1); /* check that '%s.%s' is "
|
||||
prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is "
|
||||
"an integer */" % (fname, cname, fname))
|
||||
continue
|
||||
# only accept exactly the type declared, except that '[]'
|
||||
|
|
@ -827,7 +868,7 @@ class Recompiler:
|
|||
prnt(' { %s = &p->%s; (void)tmp; }' % (
|
||||
ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
|
||||
fname))
|
||||
except ffiplatform.VerificationError as e:
|
||||
except VerificationError as e:
|
||||
prnt(' /* %s */' % str(e)) # cannot verify it, ignore
|
||||
prnt('}')
|
||||
prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname))
|
||||
|
|
@ -847,7 +888,7 @@ class Recompiler:
|
|||
named_ptr not in self.ffi._parser._included_declarations)):
|
||||
if tp.fldtypes is None:
|
||||
pass # opaque
|
||||
elif tp.partial or tp.has_anonymous_struct_fields():
|
||||
elif tp.partial or any(tp.anonymous_struct_fields()):
|
||||
pass # field layout obtained silently from the C compiler
|
||||
else:
|
||||
flags.append("_CFFI_F_CHECK_FIELDS")
|
||||
|
|
@ -859,9 +900,12 @@ class Recompiler:
|
|||
flags = '|'.join(flags) or '0'
|
||||
c_fields = []
|
||||
if reason_for_not_expanding is None:
|
||||
enumfields = list(tp.enumfields())
|
||||
expand_anonymous_struct_union = not self.target_is_python
|
||||
enumfields = list(tp.enumfields(expand_anonymous_struct_union))
|
||||
for fldname, fldtype, fbitsize, fqual in enumfields:
|
||||
fldtype = self._field_type(tp, fldname, fldtype)
|
||||
self._check_not_opaque(fldtype,
|
||||
"field '%s.%s'" % (tp.name, fldname))
|
||||
# cname is None for _add_missing_struct_unions() only
|
||||
op = OP_NOOP
|
||||
if fbitsize >= 0:
|
||||
|
|
@ -911,6 +955,13 @@ class Recompiler:
|
|||
first_field_index, c_fields))
|
||||
self._seen_struct_unions.add(tp)
|
||||
|
||||
def _check_not_opaque(self, tp, location):
|
||||
while isinstance(tp, model.ArrayType):
|
||||
tp = tp.item
|
||||
if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None:
|
||||
raise TypeError(
|
||||
"%s is of an opaque type (not declared in cdef())" % location)
|
||||
|
||||
def _add_missing_struct_unions(self):
|
||||
# not very nice, but some struct declarations might be missing
|
||||
# because they don't have any known C name. Check that they are
|
||||
|
|
@ -981,7 +1032,7 @@ class Recompiler:
|
|||
def _generate_cpy_const(self, is_int, name, tp=None, category='const',
|
||||
check_value=None):
|
||||
if (category, name) in self._seen_constants:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"duplicate declaration of %s '%s'" % (category, name))
|
||||
self._seen_constants.add((category, name))
|
||||
#
|
||||
|
|
@ -991,7 +1042,7 @@ class Recompiler:
|
|||
prnt('static int %s(unsigned long long *o)' % funcname)
|
||||
prnt('{')
|
||||
prnt(' int n = (%s) <= 0;' % (name,))
|
||||
prnt(' *o = (unsigned long long)((%s) << 0);'
|
||||
prnt(' *o = (unsigned long long)((%s) | 0);'
|
||||
' /* check that %s is an integer */' % (name, name))
|
||||
if check_value is not None:
|
||||
if check_value > 0:
|
||||
|
|
@ -1080,7 +1131,7 @@ class Recompiler:
|
|||
def _generate_cpy_macro_ctx(self, tp, name):
|
||||
if tp == '...':
|
||||
if self.target_is_python:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"cannot use the syntax '...' in '#define %s ...' when "
|
||||
"using the ABI mode" % (name,))
|
||||
check_value = None
|
||||
|
|
@ -1145,11 +1196,11 @@ class Recompiler:
|
|||
def _generate_cpy_extern_python_collecttype(self, tp, name):
|
||||
assert isinstance(tp, model.FunctionPtrType)
|
||||
self._do_collect_type(tp)
|
||||
_generate_cpy_dllexport_python_collecttype = \
|
||||
_generate_cpy_extern_python_plus_c_collecttype = \
|
||||
_generate_cpy_extern_python_collecttype
|
||||
|
||||
def _generate_cpy_dllexport_python_collecttype(self, tp, name):
|
||||
self._generate_cpy_extern_python_collecttype(tp, name)
|
||||
|
||||
def _generate_cpy_extern_python_decl(self, tp, name, dllexport=False):
|
||||
def _extern_python_decl(self, tp, name, tag_and_space):
|
||||
prnt = self._prnt
|
||||
if isinstance(tp.result, model.VoidType):
|
||||
size_of_result = '0'
|
||||
|
|
@ -1158,7 +1209,7 @@ class Recompiler:
|
|||
size_of_result = '(int)sizeof(%s)' % (
|
||||
tp.result.get_c_name('', context),)
|
||||
prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name)
|
||||
prnt(' { "%s", %s };' % (name, size_of_result))
|
||||
prnt(' { "%s.%s", %s };' % (self.module_name, name, size_of_result))
|
||||
prnt()
|
||||
#
|
||||
arguments = []
|
||||
|
|
@ -1170,6 +1221,8 @@ class Recompiler:
|
|||
repr_arguments = ', '.join(arguments)
|
||||
repr_arguments = repr_arguments or 'void'
|
||||
name_and_arguments = '%s(%s)' % (name, repr_arguments)
|
||||
if tp.abi == "__stdcall":
|
||||
name_and_arguments = '_cffi_stdcall ' + name_and_arguments
|
||||
#
|
||||
def may_need_128_bits(tp):
|
||||
return (isinstance(tp, model.PrimitiveType) and
|
||||
|
|
@ -1182,11 +1235,7 @@ class Recompiler:
|
|||
size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % (
|
||||
tp.result.get_c_name(''), size_of_a,
|
||||
tp.result.get_c_name(''), size_of_a)
|
||||
if dllexport:
|
||||
tag = 'CFFI_DLLEXPORT'
|
||||
else:
|
||||
tag = 'static'
|
||||
prnt('%s %s' % (tag, tp.result.get_c_name(name_and_arguments)))
|
||||
prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments)))
|
||||
prnt('{')
|
||||
prnt(' char a[%s];' % size_of_a)
|
||||
prnt(' char *p = a;')
|
||||
|
|
@ -1204,12 +1253,18 @@ class Recompiler:
|
|||
prnt()
|
||||
self._num_externpy += 1
|
||||
|
||||
def _generate_cpy_extern_python_decl(self, tp, name):
|
||||
self._extern_python_decl(tp, name, 'static ')
|
||||
|
||||
def _generate_cpy_dllexport_python_decl(self, tp, name):
|
||||
self._generate_cpy_extern_python_decl(tp, name, dllexport=True)
|
||||
self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ')
|
||||
|
||||
def _generate_cpy_extern_python_plus_c_decl(self, tp, name):
|
||||
self._extern_python_decl(tp, name, '')
|
||||
|
||||
def _generate_cpy_extern_python_ctx(self, tp, name):
|
||||
if self.target_is_python:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"cannot use 'extern \"Python\"' in the ABI mode")
|
||||
if tp.ellipsis:
|
||||
raise NotImplementedError("a vararg function is extern \"Python\"")
|
||||
|
|
@ -1218,20 +1273,22 @@ class Recompiler:
|
|||
self._lsts["global"].append(
|
||||
GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name))
|
||||
|
||||
def _generate_cpy_dllexport_python_ctx(self, tp, name):
|
||||
self._generate_cpy_extern_python_ctx(tp, name)
|
||||
_generate_cpy_dllexport_python_ctx = \
|
||||
_generate_cpy_extern_python_plus_c_ctx = \
|
||||
_generate_cpy_extern_python_ctx
|
||||
|
||||
def _string_literal(self, s):
|
||||
def _char_repr(c):
|
||||
# escape with a '\' the characters '\', '"' or (for trigraphs) '?'
|
||||
if c in '\\"?': return '\\' + c
|
||||
if ' ' <= c < '\x7F': return c
|
||||
if c == '\n': return '\\n'
|
||||
return '\\%03o' % ord(c)
|
||||
lines = []
|
||||
def _print_string_literal_in_array(self, s):
|
||||
prnt = self._prnt
|
||||
prnt('// # NB. this is not a string because of a size limit in MSVC')
|
||||
for line in s.splitlines(True):
|
||||
lines.append('"%s"' % ''.join([_char_repr(c) for c in line]))
|
||||
return ' \\\n'.join(lines)
|
||||
prnt(('// ' + line).rstrip())
|
||||
printed_line = ''
|
||||
for c in line:
|
||||
if len(printed_line) >= 76:
|
||||
prnt(printed_line)
|
||||
printed_line = ''
|
||||
printed_line += '%d,' % (ord(c),)
|
||||
prnt(printed_line)
|
||||
|
||||
# ----------
|
||||
# emitting the opcodes for individual types
|
||||
|
|
@ -1245,7 +1302,7 @@ class Recompiler:
|
|||
|
||||
def _emit_bytecode_UnknownIntegerType(self, tp, index):
|
||||
s = ('_cffi_prim_int(sizeof(%s), (\n'
|
||||
' ((%s)-1) << 0 /* check that %s is an integer type */\n'
|
||||
' ((%s)-1) | 0 /* check that %s is an integer type */\n'
|
||||
' ) <= 0)' % (tp.name, tp.name, tp.name))
|
||||
self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
|
||||
|
||||
|
|
@ -1289,7 +1346,7 @@ class Recompiler:
|
|||
if tp.length is None:
|
||||
self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index)
|
||||
elif tp.length == '...':
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"type %s badly placed: the '...' array length can only be "
|
||||
"used on global arrays or on fields of structures" % (
|
||||
str(tp).replace('/*...*/', '...'),))
|
||||
|
|
@ -1317,7 +1374,9 @@ else:
|
|||
s = s.encode('ascii')
|
||||
super(NativeIO, self).write(s)
|
||||
|
||||
def _make_c_or_py_source(ffi, module_name, preamble, target_file):
|
||||
def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose):
|
||||
if verbose:
|
||||
print("generating %s" % (target_file,))
|
||||
recompiler = Recompiler(ffi, module_name,
|
||||
target_is_python=(preamble is None))
|
||||
recompiler.collect_type_table()
|
||||
|
|
@ -1329,6 +1388,8 @@ def _make_c_or_py_source(ffi, module_name, preamble, target_file):
|
|||
with open(target_file, 'r') as f1:
|
||||
if f1.read(len(output) + 1) != output:
|
||||
raise IOError
|
||||
if verbose:
|
||||
print("(already up-to-date)")
|
||||
return False # already up-to-date
|
||||
except IOError:
|
||||
tmp_file = '%s.~%d' % (target_file, os.getpid())
|
||||
|
|
@ -1341,12 +1402,14 @@ def _make_c_or_py_source(ffi, module_name, preamble, target_file):
|
|||
os.rename(tmp_file, target_file)
|
||||
return True
|
||||
|
||||
def make_c_source(ffi, module_name, preamble, target_c_file):
|
||||
def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False):
|
||||
assert preamble is not None
|
||||
return _make_c_or_py_source(ffi, module_name, preamble, target_c_file)
|
||||
return _make_c_or_py_source(ffi, module_name, preamble, target_c_file,
|
||||
verbose)
|
||||
|
||||
def make_py_source(ffi, module_name, target_py_file):
|
||||
return _make_c_or_py_source(ffi, module_name, None, target_py_file)
|
||||
def make_py_source(ffi, module_name, target_py_file, verbose=False):
|
||||
return _make_c_or_py_source(ffi, module_name, None, target_py_file,
|
||||
verbose)
|
||||
|
||||
def _modname_to_file(outputdir, modname, extension):
|
||||
parts = modname.split('.')
|
||||
|
|
@ -1411,7 +1474,7 @@ def _patch_for_target(patchlist, target):
|
|||
|
||||
def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
|
||||
c_file=None, source_extension='.c', extradir=None,
|
||||
compiler_verbose=1, target=None, **kwds):
|
||||
compiler_verbose=1, target=None, debug=None, **kwds):
|
||||
if not isinstance(module_name, str):
|
||||
module_name = module_name.encode('ascii')
|
||||
if ffi._windows_unicode:
|
||||
|
|
@ -1436,7 +1499,8 @@ def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
|
|||
target = '*'
|
||||
#
|
||||
ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds)
|
||||
updated = make_c_source(ffi, module_name, preamble, c_file)
|
||||
updated = make_c_source(ffi, module_name, preamble, c_file,
|
||||
verbose=compiler_verbose)
|
||||
if call_c_compiler:
|
||||
patchlist = []
|
||||
cwd = os.getcwd()
|
||||
|
|
@ -1445,8 +1509,15 @@ def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
|
|||
_patch_for_embedding(patchlist)
|
||||
if target != '*':
|
||||
_patch_for_target(patchlist, target)
|
||||
if compiler_verbose:
|
||||
if tmpdir == '.':
|
||||
msg = 'the current directory is'
|
||||
else:
|
||||
msg = 'setting the current directory to'
|
||||
print('%s %r' % (msg, os.path.abspath(tmpdir)))
|
||||
os.chdir(tmpdir)
|
||||
outputfilename = ffiplatform.compile('.', ext, compiler_verbose)
|
||||
outputfilename = ffiplatform.compile('.', ext,
|
||||
compiler_verbose, debug)
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
_unpatch_meths(patchlist)
|
||||
|
|
@ -1456,7 +1527,8 @@ def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
|
|||
else:
|
||||
if c_file is None:
|
||||
c_file, _ = _modname_to_file(tmpdir, module_name, '.py')
|
||||
updated = make_py_source(ffi, module_name, c_file)
|
||||
updated = make_py_source(ffi, module_name, c_file,
|
||||
verbose=compiler_verbose)
|
||||
if call_c_compiler:
|
||||
return c_file
|
||||
else:
|
||||
|
|
@ -1482,4 +1554,7 @@ def _verify(ffi, module_name, preamble, *args, **kwds):
|
|||
def typeof_disabled(*args, **kwds):
|
||||
raise NotImplementedError
|
||||
ffi._typeof = typeof_disabled
|
||||
for name in dir(ffi):
|
||||
if not name.startswith('_') and not hasattr(module.ffi, name):
|
||||
setattr(ffi, name, NotImplemented)
|
||||
return module.lib
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
basestring
|
||||
|
|
@ -69,16 +70,47 @@ def add_cffi_module(dist, mod_spec):
|
|||
else:
|
||||
_add_c_module(dist, ffi, module_name, source, source_extension, kwds)
|
||||
|
||||
def _set_py_limited_api(Extension, kwds):
|
||||
"""
|
||||
Add py_limited_api to kwds if setuptools >= 26 is in use.
|
||||
Do not alter the setting if it already exists.
|
||||
Setuptools takes care of ignoring the flag on Python 2 and PyPy.
|
||||
|
||||
CPython itself should ignore the flag in a debugging version
|
||||
(by not listing .abi3.so in the extensions it supports), but
|
||||
it doesn't so far, creating troubles. That's why we check
|
||||
for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent
|
||||
of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401)
|
||||
|
||||
On Windows, it's better not to use py_limited_api until issue #355
|
||||
can be resolved (by having virtualenv copy PYTHON3.DLL). See also
|
||||
the start of _cffi_include.h.
|
||||
"""
|
||||
if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount')
|
||||
and sys.platform != 'win32'):
|
||||
import setuptools
|
||||
try:
|
||||
setuptools_major_version = int(setuptools.__version__.partition('.')[0])
|
||||
if setuptools_major_version >= 26:
|
||||
kwds['py_limited_api'] = True
|
||||
except ValueError: # certain development versions of setuptools
|
||||
# If we don't know the version number of setuptools, we
|
||||
# try to set 'py_limited_api' anyway. At worst, we get a
|
||||
# warning.
|
||||
kwds['py_limited_api'] = True
|
||||
return kwds
|
||||
|
||||
def _add_c_module(dist, ffi, module_name, source, source_extension, kwds):
|
||||
from distutils.core import Extension
|
||||
from distutils.command.build_ext import build_ext
|
||||
# We are a setuptools extension. Need this build_ext for py_limited_api.
|
||||
from setuptools.command.build_ext import build_ext
|
||||
from distutils.dir_util import mkpath
|
||||
from distutils import log
|
||||
from cffi import recompiler
|
||||
|
||||
allsources = ['$PLACEHOLDER']
|
||||
allsources.extend(kwds.pop('sources', []))
|
||||
kwds = _set_py_limited_api(Extension, kwds)
|
||||
ext = Extension(name=module_name, sources=allsources, **kwds)
|
||||
|
||||
def make_mod(tmpdir, pre_run=None):
|
||||
|
|
@ -116,8 +148,8 @@ def _add_c_module(dist, ffi, module_name, source, source_extension, kwds):
|
|||
|
||||
def _add_py_module(dist, ffi, module_name):
|
||||
from distutils.dir_util import mkpath
|
||||
from distutils.command.build_py import build_py
|
||||
from distutils.command.build_ext import build_ext
|
||||
from setuptools.command.build_py import build_py
|
||||
from setuptools.command.build_ext import build_ext
|
||||
from distutils import log
|
||||
from cffi import recompiler
|
||||
|
||||
|
|
@ -137,6 +169,17 @@ def _add_py_module(dist, ffi, module_name):
|
|||
generate_mod(os.path.join(self.build_lib, *module_path))
|
||||
dist.cmdclass['build_py'] = build_py_make_mod
|
||||
|
||||
# distutils and setuptools have no notion I could find of a
|
||||
# generated python module. If we don't add module_name to
|
||||
# dist.py_modules, then things mostly work but there are some
|
||||
# combination of options (--root and --record) that will miss
|
||||
# the module. So we add it here, which gives a few apparently
|
||||
# harmless warnings about not finding the file outside the
|
||||
# build directory.
|
||||
if dist.py_modules is None:
|
||||
dist.py_modules = []
|
||||
dist.py_modules.append(module_name)
|
||||
|
||||
# the following is only for "build_ext -i"
|
||||
base_class_2 = dist.cmdclass.get('build_ext', build_ext)
|
||||
class build_ext_make_mod(base_class_2):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
#
|
||||
# DEPRECATED: implementation for ffi.verify()
|
||||
#
|
||||
import sys, imp
|
||||
from . import model, ffiplatform
|
||||
from . import model
|
||||
from .error import VerificationError
|
||||
|
||||
|
||||
class VCPythonEngine(object):
|
||||
|
|
@ -152,7 +156,7 @@ class VCPythonEngine(object):
|
|||
self.verifier.modulefilename)
|
||||
except ImportError as e:
|
||||
error = "importing %r: %s" % (self.verifier.modulefilename, e)
|
||||
raise ffiplatform.VerificationError(error)
|
||||
raise VerificationError(error)
|
||||
finally:
|
||||
if hasattr(sys, "setdlopenflags"):
|
||||
sys.setdlopenflags(previous_flags)
|
||||
|
|
@ -182,7 +186,7 @@ class VCPythonEngine(object):
|
|||
def __dir__(self):
|
||||
return FFILibrary._cffi_dir + list(self.__dict__)
|
||||
library = FFILibrary()
|
||||
if module._cffi_setup(lst, ffiplatform.VerificationError, library):
|
||||
if module._cffi_setup(lst, VerificationError, library):
|
||||
import warnings
|
||||
warnings.warn("reimporting %r might overwrite older definitions"
|
||||
% (self.verifier.get_module_name()))
|
||||
|
|
@ -209,7 +213,7 @@ class VCPythonEngine(object):
|
|||
method = getattr(self, '_generate_cpy_%s_%s' % (kind,
|
||||
step_name))
|
||||
except AttributeError:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"not implemented in verify(): %r" % name)
|
||||
try:
|
||||
method(tp, realname)
|
||||
|
|
@ -292,7 +296,7 @@ class VCPythonEngine(object):
|
|||
|
||||
def _convert_expr_from_c(self, tp, var, context):
|
||||
if isinstance(tp, model.PrimitiveType):
|
||||
if tp.is_integer_type():
|
||||
if tp.is_integer_type() and tp.name != '_Bool':
|
||||
return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
|
||||
elif tp.name != 'long double':
|
||||
return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
|
||||
|
|
@ -305,7 +309,7 @@ class VCPythonEngine(object):
|
|||
elif isinstance(tp, model.ArrayType):
|
||||
return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
|
||||
var, self._gettypenum(model.PointerType(tp.item)))
|
||||
elif isinstance(tp, model.StructType):
|
||||
elif isinstance(tp, model.StructOrUnion):
|
||||
if tp.fldnames is None:
|
||||
raise TypeError("'%s' is used as %s, but is opaque" % (
|
||||
tp._get_c_name(), context))
|
||||
|
|
@ -482,7 +486,7 @@ class VCPythonEngine(object):
|
|||
prnt(' { %s = &p->%s; (void)tmp; }' % (
|
||||
ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
|
||||
fname))
|
||||
except ffiplatform.VerificationError as e:
|
||||
except VerificationError as e:
|
||||
prnt(' /* %s */' % str(e)) # cannot verify it, ignore
|
||||
prnt('}')
|
||||
prnt('static PyObject *')
|
||||
|
|
@ -547,7 +551,7 @@ class VCPythonEngine(object):
|
|||
# check that the layout sizes and offsets match the real ones
|
||||
def check(realvalue, expectedvalue, msg):
|
||||
if realvalue != expectedvalue:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"%s (we have %d, but C compiler says %d)"
|
||||
% (msg, expectedvalue, realvalue))
|
||||
ffi = self.ffi
|
||||
|
|
@ -768,7 +772,7 @@ class VCPythonEngine(object):
|
|||
BItemType = self.ffi._get_cached_btype(tp.item)
|
||||
length, rest = divmod(size, self.ffi.sizeof(BItemType))
|
||||
if rest != 0:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"bad size: %r does not seem to be an array of %s" %
|
||||
(name, tp.item))
|
||||
tp = tp.resolve_length(length)
|
||||
|
|
@ -804,7 +808,8 @@ cffimod_header = r'''
|
|||
#include <stddef.h>
|
||||
|
||||
/* this block of #ifs should be kept exactly identical between
|
||||
c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py */
|
||||
c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
|
||||
and cffi/_cffi_include.h */
|
||||
#if defined(_MSC_VER)
|
||||
# include <malloc.h> /* for alloca() */
|
||||
# if _MSC_VER < 1600 /* MSVC < 2010 */
|
||||
|
|
@ -838,11 +843,13 @@ cffimod_header = r'''
|
|||
# include <stdint.h>
|
||||
# endif
|
||||
# if _MSC_VER < 1800 /* MSVC < 2013 */
|
||||
typedef unsigned char _Bool;
|
||||
# ifndef __cplusplus
|
||||
typedef unsigned char _Bool;
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
# include <stdint.h>
|
||||
# if (defined (__SVR4) && defined (__sun)) || defined(_AIX)
|
||||
# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
|
||||
# include <alloca.h>
|
||||
# endif
|
||||
#endif
|
||||
|
|
@ -865,6 +872,7 @@ cffimod_header = r'''
|
|||
#define _cffi_from_c_ulong PyLong_FromUnsignedLong
|
||||
#define _cffi_from_c_longlong PyLong_FromLongLong
|
||||
#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong
|
||||
#define _cffi_from_c__Bool PyBool_FromLong
|
||||
|
||||
#define _cffi_to_c_double PyFloat_AsDouble
|
||||
#define _cffi_to_c_float PyFloat_AsDouble
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
#
|
||||
# DEPRECATED: implementation for ffi.verify()
|
||||
#
|
||||
import sys, os
|
||||
import types
|
||||
|
||||
from . import model, ffiplatform
|
||||
from . import model
|
||||
from .error import VerificationError
|
||||
|
||||
|
||||
class VGenericEngine(object):
|
||||
|
|
@ -99,7 +103,7 @@ class VGenericEngine(object):
|
|||
method = getattr(self, '_generate_gen_%s_%s' % (kind,
|
||||
step_name))
|
||||
except AttributeError:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"not implemented in verify(): %r" % name)
|
||||
try:
|
||||
method(tp, realname)
|
||||
|
|
@ -278,7 +282,7 @@ class VGenericEngine(object):
|
|||
prnt(' { %s = &p->%s; (void)tmp; }' % (
|
||||
ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
|
||||
fname))
|
||||
except ffiplatform.VerificationError as e:
|
||||
except VerificationError as e:
|
||||
prnt(' /* %s */' % str(e)) # cannot verify it, ignore
|
||||
prnt('}')
|
||||
self.export_symbols.append(layoutfuncname)
|
||||
|
|
@ -341,7 +345,7 @@ class VGenericEngine(object):
|
|||
# check that the layout sizes and offsets match the real ones
|
||||
def check(realvalue, expectedvalue, msg):
|
||||
if realvalue != expectedvalue:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"%s (we have %d, but C compiler says %d)"
|
||||
% (msg, expectedvalue, realvalue))
|
||||
ffi = self.ffi
|
||||
|
|
@ -495,7 +499,7 @@ class VGenericEngine(object):
|
|||
error = self.ffi.string(p)
|
||||
if sys.version_info >= (3,):
|
||||
error = str(error, 'utf-8')
|
||||
raise ffiplatform.VerificationError(error)
|
||||
raise VerificationError(error)
|
||||
|
||||
def _enum_funcname(self, prefix, name):
|
||||
# "$enum_$1" => "___D_enum____D_1"
|
||||
|
|
@ -588,7 +592,7 @@ class VGenericEngine(object):
|
|||
BItemType = self.ffi._get_cached_btype(tp.item)
|
||||
length, rest = divmod(size, self.ffi.sizeof(BItemType))
|
||||
if rest != 0:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"bad size: %r does not seem to be an array of %s" %
|
||||
(name, tp.item))
|
||||
tp = tp.resolve_length(length)
|
||||
|
|
@ -623,7 +627,8 @@ cffimod_header = r'''
|
|||
#include <sys/types.h> /* XXX for ssize_t on some platforms */
|
||||
|
||||
/* this block of #ifs should be kept exactly identical between
|
||||
c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py */
|
||||
c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
|
||||
and cffi/_cffi_include.h */
|
||||
#if defined(_MSC_VER)
|
||||
# include <malloc.h> /* for alloca() */
|
||||
# if _MSC_VER < 1600 /* MSVC < 2010 */
|
||||
|
|
@ -657,11 +662,13 @@ cffimod_header = r'''
|
|||
# include <stdint.h>
|
||||
# endif
|
||||
# if _MSC_VER < 1800 /* MSVC < 2013 */
|
||||
typedef unsigned char _Bool;
|
||||
# ifndef __cplusplus
|
||||
typedef unsigned char _Bool;
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
# include <stdint.h>
|
||||
# if (defined (__SVR4) && defined (__sun)) || defined(_AIX)
|
||||
# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
|
||||
# include <alloca.h>
|
||||
# endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
#
|
||||
# DEPRECATED: implementation for ffi.verify()
|
||||
#
|
||||
import sys, os, binascii, shutil, io
|
||||
from . import __version_verifier_modules__
|
||||
from . import ffiplatform
|
||||
from .error import VerificationError
|
||||
|
||||
if sys.version_info >= (3, 3):
|
||||
import importlib.machinery
|
||||
|
|
@ -22,16 +26,6 @@ else:
|
|||
s = s.encode('ascii')
|
||||
super(NativeIO, self).write(s)
|
||||
|
||||
def _hack_at_distutils():
|
||||
# Windows-only workaround for some configurations: see
|
||||
# https://bugs.python.org/issue23246 (Python 2.7 with
|
||||
# a specific MS compiler suite download)
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
import setuptools # for side-effects, patches distutils
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class Verifier(object):
|
||||
|
||||
|
|
@ -39,7 +33,7 @@ class Verifier(object):
|
|||
ext_package=None, tag='', force_generic_engine=False,
|
||||
source_extension='.c', flags=None, relative_to=None, **kwds):
|
||||
if ffi._parser._uses_new_feature:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"feature not supported with ffi.verify(), but only "
|
||||
"with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,))
|
||||
self.ffi = ffi
|
||||
|
|
@ -80,7 +74,7 @@ class Verifier(object):
|
|||
which can be tweaked beforehand."""
|
||||
with self.ffi._lock:
|
||||
if self._has_source and file is None:
|
||||
raise ffiplatform.VerificationError(
|
||||
raise VerificationError(
|
||||
"source code already written")
|
||||
self._write_source(file)
|
||||
|
||||
|
|
@ -89,7 +83,7 @@ class Verifier(object):
|
|||
This produces a dynamic link library in 'self.modulefilename'."""
|
||||
with self.ffi._lock:
|
||||
if self._has_module:
|
||||
raise ffiplatform.VerificationError("module already compiled")
|
||||
raise VerificationError("module already compiled")
|
||||
if not self._has_source:
|
||||
self._write_source()
|
||||
self._compile_module()
|
||||
|
|
@ -122,7 +116,7 @@ class Verifier(object):
|
|||
return basename
|
||||
|
||||
def get_extension(self):
|
||||
_hack_at_distutils() # backward compatibility hack
|
||||
ffiplatform._hack_at_distutils() # backward compatibility hack
|
||||
if not self._has_source:
|
||||
with self.ffi._lock:
|
||||
if not self._has_source:
|
||||
|
|
@ -307,7 +301,6 @@ def _get_so_suffixes():
|
|||
return suffixes
|
||||
|
||||
def _ensure_dir(filename):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(filename))
|
||||
except OSError:
|
||||
pass
|
||||
dirname = os.path.dirname(filename)
|
||||
if dirname and not os.path.isdir(dirname):
|
||||
os.makedirs(dirname)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue