Author: Armin Rigo <[email protected]>
Branch: release-1.5
Changeset: r2627:1a2d841f7896
Date: 2016-02-13 09:54 +0100
http://bitbucket.org/cffi/cffi/changeset/1a2d841f7896/
Log: hg merge default
diff --git a/MANIFEST.in b/MANIFEST.in
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,6 +1,6 @@
recursive-include cffi *.py *.h
recursive-include c *.c *.h *.asm *.py win64.obj
-recursive-include testing *.py
+recursive-include testing *.py *.c *.h
recursive-include doc *.py *.rst Makefile *.bat
-recursive-include demo py.cleanup *.py manual.c
+recursive-include demo py.cleanup *.py embedding_test.c manual.c
include AUTHORS LICENSE setup.py setup_base.py
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -2,7 +2,7 @@
#include <Python.h>
#include "structmember.h"
-#define CFFI_VERSION "1.5.0"
+#define CFFI_VERSION "1.5.1"
#ifdef MS_WIN32
#include <windows.h>
diff --git a/c/call_python.c b/c/call_python.c
--- a/c/call_python.c
+++ b/c/call_python.c
@@ -115,6 +115,7 @@
static int _update_cache_to_call_python(struct _cffi_externpy_s *externpy)
{
PyObject *interpstate_dict, *interpstate_key, *infotuple, *old1, *new1;
+ PyObject *old2;
interpstate_dict = _get_interpstate_dict();
if (interpstate_dict == NULL)
@@ -127,14 +128,17 @@
infotuple = PyDict_GetItem(interpstate_dict, interpstate_key);
Py_DECREF(interpstate_key);
if (infotuple == NULL)
- return 1; /* no ffi.def_extern() from this subinterpreter */
+ return 3; /* no ffi.def_extern() from this subinterpreter */
new1 = PyThreadState_GET()->interp->modules;
Py_INCREF(new1);
+ Py_INCREF(infotuple);
old1 = (PyObject *)externpy->reserved1;
+ old2 = (PyObject *)externpy->reserved2;
externpy->reserved1 = new1; /* holds a reference */
- externpy->reserved2 = infotuple; /* doesn't hold a reference */
+ externpy->reserved2 = infotuple; /* holds a reference (issue #246) */
Py_XDECREF(old1);
+ Py_XDECREF(old2);
return 0; /* no error */
@@ -213,9 +217,11 @@
gil_release(state);
}
if (err) {
- static const char *msg[2] = {
+ static const char *msg[] = {
"no code was attached to it yet with @ffi.def_extern()",
- "got internal exception (out of memory?)" };
+ "got internal exception (out of memory / shutdown issue)",
+ "@ffi.def_extern() was not called in the current subinterpreter",
+ };
fprintf(stderr, "extern \"Python\": function %s() called, "
"but %s. Returning 0.\n", externpy->name, msg[err-1]);
memset(args, 0, externpy->size_of_result);
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -12,7 +12,7 @@
# ____________________________________________________________
import sys
-assert __version__ == "1.5.0", ("This test_c.py file is for testing a version"
+assert __version__ == "1.5.1", ("This test_c.py file is for testing a version"
" of cffi that differs from the one that we"
" get from 'import _cffi_backend'")
if sys.version_info < (3,):
diff --git a/cffi/__init__.py b/cffi/__init__.py
--- a/cffi/__init__.py
+++ b/cffi/__init__.py
@@ -4,8 +4,8 @@
from .api import FFI, CDefError, FFIError
from .ffiplatform import VerificationError, VerificationMissing
-__version__ = "1.5.0"
-__version_info__ = (1, 5, 0)
+__version__ = "1.5.1"
+__version_info__ = (1, 5, 1)
# 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__
diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h
--- a/cffi/_cffi_include.h
+++ b/cffi/_cffi_include.h
@@ -231,6 +231,12 @@
((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
diff --git a/cffi/_embedding.h b/cffi/_embedding.h
--- a/cffi/_embedding.h
+++ b/cffi/_embedding.h
@@ -233,7 +233,7 @@
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.5.1"
"\n_cffi_backend module: ", f);
modules = PyImport_GetModuleDict();
mod = PyDict_GetItemString(modules, "_cffi_backend");
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -1,4 +1,4 @@
-import sys, types
+import sys, sysconfig, types
from .lock import allocate_lock
try:
@@ -550,17 +550,28 @@
lst.append(value)
#
if '__pypy__' in sys.builtin_module_names:
- if hasattr(sys, 'prefix'):
- import os
- ensure('library_dirs', os.path.join(sys.prefix, 'bin'))
- pythonlib = "pypy-c"
+ if sys.platform == "win32":
+ # we need 'libpypy-c.lib' (included with recent pypy distrib)
+ # in addition to the runtime 'libpypy-c.dll'
+ pythonlib = "libpypy-c"
+ if hasattr(sys, 'prefix'):
+ ensure('library_dirs', sys.prefix)
+ else:
+ # we need 'libpypy-c.{so,dylib}', which should be by
+ # default located in 'sys.prefix/bin'
+ pythonlib = "pypy-c"
+ if hasattr(sys, 'prefix'):
+ import os
+ ensure('library_dirs', os.path.join(sys.prefix, 'bin'))
else:
if sys.platform == "win32":
template = "python%d%d"
- if sys.flags.debug:
- template = template + '_d'
+ if hasattr(sys, 'gettotalrefcount'):
+ template += '_d'
else:
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'):
@@ -697,6 +708,10 @@
#
self._embedding = pysource
+ def def_extern(self, *args, **kwds):
+ raise ValueError("ffi.def_extern() is only available on API-mode FFI "
+ "objects")
+
def _load_backend_lib(backend, name, flags):
if name is None:
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -220,7 +220,7 @@
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
@@ -374,7 +374,7 @@
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:
tag = 'extern_python '
@@ -450,7 +450,7 @@
prevobj, prevquals = self._declarations[name]
if prevobj is obj and prevquals == quals:
return
- if not self._options['override']:
+ if not self._options.get('override'):
raise api.FFIError(
"multiple declarations of %s (for interactive usage, "
"try cdef(xx, override=True))" % (name,))
@@ -729,7 +729,7 @@
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)
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -1170,6 +1170,8 @@
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
diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py
--- a/cffi/vengine_cpy.py
+++ b/cffi/vengine_cpy.py
@@ -1,3 +1,6 @@
+#
+# DEPRECATED: implementation for ffi.verify()
+#
import sys, imp
from . import model, ffiplatform
diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py
--- a/cffi/vengine_gen.py
+++ b/cffi/vengine_gen.py
@@ -1,3 +1,6 @@
+#
+# DEPRECATED: implementation for ffi.verify()
+#
import sys, os
import types
diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -1,3 +1,6 @@
+#
+# DEPRECATED: implementation for ffi.verify()
+#
import sys, os, binascii, shutil, io
from . import __version_verifier_modules__
from . import ffiplatform
diff --git a/doc/source/conf.py b/doc/source/conf.py
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -47,7 +47,7 @@
# The short X.Y version.
version = '1.5'
# The full version, including alpha/beta/rc tags.
-release = '1.5.0'
+release = '1.5.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -51,11 +51,11 @@
Download and Installation:
-* http://pypi.python.org/packages/source/c/cffi/cffi-1.5.0.tar.gz
+* http://pypi.python.org/packages/source/c/cffi/cffi-1.5.1.tar.gz
- - MD5: dec8441e67880494ee881305059af656
+ - MD5: ...
- - SHA: fd21011ba2a3cab627001b52c69fd7274517e549
+ - SHA: ...
* Or grab the most current version from the `Bitbucket page`_:
``hg clone https://bitbucket.org/cffi/cffi``
diff --git a/doc/source/using.rst b/doc/source/using.rst
--- a/doc/source/using.rst
+++ b/doc/source/using.rst
@@ -476,16 +476,20 @@
Python function object that is, at runtime, attached with
``@ffi.def_extern()``.
-The ``@ffi.def_extern()`` decorator should be applied to a global
-function, but *only once.* This is because each function from the cdef with
-``extern "Python"`` turns into only one C function. To support some
-corner cases, it is possible to redefine the attached Python function
-by calling ``@ffi.def_extern()`` again---but this is not recommended!
-Better write the single global Python function more flexibly in the
-first place. Calling ``@ffi.def_extern()`` again changes the C logic
-to call the new Python function; the old Python function is not
-callable any more and the C function pointer you get from
-``lib.my_function`` is always the same.
+The ``@ffi.def_extern()`` decorator should be applied to **global
+functions,** one for each ``extern "Python"`` function of the same
+name.
+
+To support some corner cases, it is possible to redefine the attached
+Python function by calling ``@ffi.def_extern()`` again for the same
+name---but this is not recommended! Better attach a single global
+Python function for this name, and write it more flexibly in the first
+place. This is because each ``extern "Python"`` function turns into
+only one C function. Calling ``@ffi.def_extern()`` again changes this
+function's C logic to call the new Python function; the old Python
+function is not callable any more. The C function pointer you get
+from ``lib.my_function`` is always this C function's address, i.e. it
+remains the same.
Extern "Python" and ``void *`` arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -3,6 +3,18 @@
======================
+v1.5.1
+======
+
+* A few installation-time tweaks (thanks Stefano!)
+
+* Issue #245: Win32: ``__stdcall`` was never generated for
+ ``extern "Python"`` functions
+
+* Issue #246: trying to be more robust against CPython's fragile
+ interpreter shutdown logic
+
+
v1.5.0
======
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -144,7 +144,7 @@
`Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_
""",
- version='1.5.0',
+ version='1.5.1',
packages=['cffi'] if cpython else [],
package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h',
'_embedding.h']}
diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py
--- a/testing/cffi0/backend_tests.py
+++ b/testing/cffi0/backend_tests.py
@@ -1846,3 +1846,8 @@
thread.start_new_thread(f, ())
time.sleep(1.5)
assert seen == ['init!', 'init done'] + 6 * [7]
+
+ def test_sizeof_struct_directly(self):
+ # only works with the Python FFI instances
+ ffi = FFI(backend=self.Backend())
+ assert ffi.sizeof("struct{int a;}") == ffi.sizeof("int")
diff --git a/testing/cffi0/test_ffi_backend.py
b/testing/cffi0/test_ffi_backend.py
--- a/testing/cffi0/test_ffi_backend.py
+++ b/testing/cffi0/test_ffi_backend.py
@@ -419,3 +419,7 @@
]:
x = ffi.sizeof(name)
assert 1 <= x <= 16
+
+ def test_ffi_def_extern(self):
+ ffi = FFI()
+ py.test.raises(ValueError, ffi.def_extern)
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -1713,3 +1713,33 @@
# a case where 'onerror' is not callable
py.test.raises(TypeError, ffi.def_extern(name='bar', onerror=42),
lambda x: x)
+
+def test_extern_python_stdcall():
+ ffi = FFI()
+ ffi.cdef("""
+ extern "Python" int __stdcall foo(int);
+ extern "Python" int WINAPI bar(int);
+ int (__stdcall * mycb1)(int);
+ int indirect_call(int);
+ """)
+ lib = verify(ffi, 'test_extern_python_stdcall', """
+ #ifndef _MSC_VER
+ # define __stdcall
+ #endif
+ static int (__stdcall * mycb1)(int);
+ static int indirect_call(int x) {
+ return mycb1(x);
+ }
+ """)
+ #
+ @ffi.def_extern()
+ def foo(x):
+ return x + 42
+ @ffi.def_extern()
+ def bar(x):
+ return x + 43
+ assert lib.foo(100) == 142
+ assert lib.bar(100) == 143
+ lib.mycb1 = lib.foo
+ assert lib.mycb1(200) == 242
+ assert lib.indirect_call(300) == 342
diff --git a/testing/cffi1/test_zdist.py b/testing/cffi1/test_zdist.py
--- a/testing/cffi1/test_zdist.py
+++ b/testing/cffi1/test_zdist.py
@@ -48,7 +48,8 @@
import setuptools
except ImportError:
py.test.skip("setuptools not found")
- self.run(['setup.py', 'egg_info'], cwd=self.rootdir)
+ if os.path.exists(os.path.join(self.rootdir, 'setup.py')):
+ self.run(['setup.py', 'egg_info'], cwd=self.rootdir)
TestDist._setuptools_ready = True
def check_produced_files(self, content, curdir=None):
diff --git a/testing/embedding/test_basic.py b/testing/embedding/test_basic.py
--- a/testing/embedding/test_basic.py
+++ b/testing/embedding/test_basic.py
@@ -4,10 +4,6 @@
from testing.udir import udir
import cffi
-if hasattr(sys, 'gettotalrefcount'):
- py.test.skip("tried hard and failed to have these tests run "
- "in a debug-mode python")
-
local_dir = os.path.dirname(os.path.abspath(__file__))
_link_error = '?'
@@ -29,6 +25,14 @@
py.test.skip(str(_link_error))
+def prefix_pythonpath():
+ cffi_base = os.path.dirname(os.path.dirname(local_dir))
+ pythonpath = os.environ.get('PYTHONPATH', '').split(os.pathsep)
+ if cffi_base not in pythonpath:
+ pythonpath.insert(0, cffi_base)
+ return os.pathsep.join(pythonpath)
+
+
class EmbeddingTests:
_compiled_modules = {}
@@ -69,8 +73,7 @@
# find a solution to that: we could hack sys.path inside the
# script run here, but we can't hack it in the same way in
# execute().
- env_extra = {'PYTHONPATH':
- os.path.dirname(os.path.dirname(local_dir))}
+ env_extra = {'PYTHONPATH': prefix_pythonpath()}
output = self._run([sys.executable, os.path.join(local_dir,
filename)],
env_extra=env_extra)
match = re.compile(r"\bFILENAME: (.+)").search(output)
@@ -114,14 +117,19 @@
def execute(self, name):
path = self.get_path()
- env_extra = {}
- env_extra['PYTHONPATH'] = os.path.dirname(os.path.dirname(local_dir))
- libpath = os.environ.get('LD_LIBRARY_PATH')
- if libpath:
- libpath = path + ':' + libpath
+ env_extra = {'PYTHONPATH': prefix_pythonpath()}
+ if sys.platform == 'win32':
+ _path = os.environ.get('PATH')
+ # for libpypy-c.dll or Python27.dll
+ _path = os.path.split(sys.executable)[0] + ';' + _path
+ env_extra['PATH'] = _path
else:
- libpath = path
- env_extra['LD_LIBRARY_PATH'] = libpath
+ libpath = os.environ.get('LD_LIBRARY_PATH')
+ if libpath:
+ libpath = path + ':' + libpath
+ else:
+ libpath = path
+ env_extra['LD_LIBRARY_PATH'] = libpath
print('running %r in %r' % (name, path))
executable_name = name
if sys.platform == 'win32':
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit