[pypy-commit] pypy test-cpyext: Move support code to pypy.tool.cpyext.extbuild
rlamy
pypy.commits at gmail.com
Sat Oct 1 14:33:40 EDT 2016
Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: test-cpyext
Changeset: r87501:53117faa3adf
Date: 2016-10-01 19:32 +0100
http://bitbucket.org/pypy/pypy/changeset/53117faa3adf/
Log: Move support code to pypy.tool.cpyext.extbuild
(This a step towards making it possible to import and run this code
under Python3)
diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py
--- a/pypy/module/cpyext/test/test_cpyext.py
+++ b/pypy/module/cpyext/test/test_cpyext.py
@@ -1,14 +1,12 @@
import sys
import weakref
-import os
-import py, pytest
+import pytest
-from pypy import pypydir
+from pypy.tool.cpyext.extbuild import (
+ SystemCompilationInfo, HERE, get_sys_info_app)
from pypy.interpreter.gateway import unwrap_spec, interp2app
from rpython.rtyper.lltypesystem import lltype, ll2ctypes
-from rpython.translator.gensupp import uniquemodulename
-from rpython.tool.udir import udir
from pypy.module.cpyext import api
from pypy.module.cpyext.state import State
from pypy.module.cpyext.pyobject import Py_DecRef
@@ -16,9 +14,6 @@
from rpython.tool import leakfinder
from rpython.rlib import rawrefcount
-from .support import c_compile
-
-HERE = py.path.local(pypydir) / 'module' / 'cpyext' / 'test'
only_pypy ="config.option.runappdirect and '__pypy__' not in sys.builtin_module_names"
@api.cpython_api([], api.PyObject)
@@ -34,96 +29,6 @@
assert 'PyModule_Check' in api.FUNCTIONS
assert api.FUNCTIONS['PyModule_Check'].argtypes == [api.PyObject]
-def convert_sources_to_files(sources, dirname):
- files = []
- for i, source in enumerate(sources):
- filename = dirname / ('source_%d.c' % i)
- with filename.open('w') as f:
- f.write(str(source))
- files.append(filename)
- return files
-
-class SystemCompilationInfo(object):
- """Bundles all the generic information required to compile extensions.
-
- Note: here, 'system' means OS + target interpreter + test config + ...
- """
- def __init__(self, include_extra=None, compile_extra=None, link_extra=None,
- extra_libs=None, ext=None):
- self.include_extra = include_extra or []
- self.compile_extra = compile_extra
- self.link_extra = link_extra
- self.extra_libs = extra_libs
- self.ext = ext
-
- def compile_extension_module(self, name, include_dirs=None,
- source_files=None, source_strings=None):
- """
- Build an extension module and return the filename of the resulting
- native code file.
-
- name is the name of the module, possibly including dots if it is a
- module inside a package.
-
- Any extra keyword arguments are passed on to ExternalCompilationInfo to
- build the module (so specify your source with one of those).
- """
- include_dirs = include_dirs or []
- modname = name.split('.')[-1]
- dirname = (udir/uniquemodulename('module')).ensure(dir=1)
- if source_strings:
- assert not source_files
- files = convert_sources_to_files(source_strings, dirname)
- source_files = files
- soname = c_compile(source_files, outputfilename=str(dirname/modname),
- compile_extra=self.compile_extra,
- link_extra=self.link_extra,
- include_dirs=self.include_extra + include_dirs,
- libraries=self.extra_libs)
- pydname = soname.new(purebasename=modname, ext=self.ext)
- soname.rename(pydname)
- return str(pydname)
-
- def import_module(self, name, init=None, body='', filename=None,
- include_dirs=None, PY_SSIZE_T_CLEAN=False):
- """
- init specifies the overall template of the module.
-
- if init is None, the module source will be loaded from a file in this
- test directory, give a name given by the filename parameter.
-
- if filename is None, the module name will be used to construct the
- filename.
- """
- if init is not None:
- code = make_source(name, init, body, PY_SSIZE_T_CLEAN)
- kwds = dict(source_strings=[code])
- else:
- assert not PY_SSIZE_T_CLEAN
- if filename is None:
- filename = name
- filename = HERE / (filename + ".c")
- kwds = dict(source_files=[filename])
- mod = self.compile_extension_module(
- name, include_dirs=include_dirs, **kwds)
- return self.load_module(mod, name)
-
- def import_extension(self, modname, functions, prologue="",
- include_dirs=None, more_init="", PY_SSIZE_T_CLEAN=False):
- body = prologue + make_methods(functions, modname)
- init = """Py_InitModule("%s", methods);""" % (modname,)
- if more_init:
- init += more_init
- return self.import_module(
- name=modname, init=init, body=body, include_dirs=include_dirs,
- PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN)
-
-
-class ExtensionCompiler(SystemCompilationInfo):
- """Extension compiler for appdirect mode"""
- def load_module(space, mod, name):
- import imp
- return imp.load_dynamic(name, mod)
class SpaceCompiler(SystemCompilationInfo):
"""Extension compiler for regular (untranslated PyPy) mode"""
@@ -166,83 +71,6 @@
ext=get_so_extension(space))
-def get_so_suffix():
- from imp import get_suffixes, C_EXTENSION
- for suffix, mode, typ in get_suffixes():
- if typ == C_EXTENSION:
- return suffix
- else:
- raise RuntimeError("This interpreter does not define a filename "
- "suffix for C extensions!")
-
-def get_sys_info_app():
- from distutils.sysconfig import get_python_inc
- if sys.platform == 'win32':
- compile_extra = ["/we4013"]
- link_extra = ["/LIBPATH:" + os.path.join(sys.exec_prefix, 'libs')]
- elif sys.platform == 'darwin':
- compile_extra = link_extra = None
- pass
- elif sys.platform.startswith('linux'):
- compile_extra = [
- "-O0", "-g", "-Werror=implicit-function-declaration", "-fPIC"]
- link_extra = None
- ext = get_so_suffix()
- return ExtensionCompiler(
- include_extra=[get_python_inc()],
- compile_extra=compile_extra,
- link_extra=link_extra,
- ext=get_so_suffix())
-
-def make_methods(functions, modname):
- methods_table = []
- codes = []
- for funcname, flags, code in functions:
- cfuncname = "%s_%s" % (modname, funcname)
- methods_table.append(
- "{\"%s\", %s, %s}," % (funcname, cfuncname, flags))
- func_code = """
- static PyObject* %s(PyObject* self, PyObject* args)
- {
- %s
- }
- """ % (cfuncname, code)
- codes.append(func_code)
-
- body = "\n".join(codes) + """
- static PyMethodDef methods[] = {
- %s
- { NULL }
- };
- """ % ('\n'.join(methods_table),)
- return body
-
-def make_source(name, init, body, PY_SSIZE_T_CLEAN):
- code = """
- %(PY_SSIZE_T_CLEAN)s
- #include <Python.h>
- /* fix for cpython 2.7 Python.h if running tests with -A
- since pypy compiles with -fvisibility-hidden */
- #undef PyMODINIT_FUNC
- #ifdef __GNUC__
- # define RPY_EXPORTED extern __attribute__((visibility("default")))
- #else
- # define RPY_EXPORTED extern __declspec(dllexport)
- #endif
- #define PyMODINIT_FUNC RPY_EXPORTED void
-
- %(body)s
-
- PyMODINIT_FUNC
- init%(name)s(void) {
- %(init)s
- }
- """ % dict(name=name, init=init, body=body,
- PY_SSIZE_T_CLEAN='#define PY_SSIZE_T_CLEAN'
- if PY_SSIZE_T_CLEAN else '')
- return code
-
-
def freeze_refcnts(self):
rawrefcount._dont_free_any_more()
return #ZZZ
diff --git a/pypy/module/cpyext/test/test_pyfile.py b/pypy/module/cpyext/test/test_pyfile.py
--- a/pypy/module/cpyext/test/test_pyfile.py
+++ b/pypy/module/cpyext/test/test_pyfile.py
@@ -156,4 +156,3 @@
t_py = fid.tell()
assert t_c == t_py, 'after a fread, c level ftell(fp) %d but PyFile.tell() %d' % (t_c, t_py)
-
diff --git a/pypy/module/cpyext/test/support.py b/pypy/tool/cpyext/extbuild.py
rename from pypy/module/cpyext/test/support.py
rename to pypy/tool/cpyext/extbuild.py
--- a/pypy/module/cpyext/test/support.py
+++ b/pypy/tool/cpyext/extbuild.py
@@ -1,12 +1,157 @@
import os
+import sys
import py
-from sys import platform
+from pypy import pypydir
+from rpython.tool.udir import udir
+from rpython.translator.gensupp import uniquemodulename
if os.name != 'nt':
so_ext = 'so'
else:
so_ext = 'dll'
+HERE = py.path.local(pypydir) / 'module' / 'cpyext' / 'test'
+
+class SystemCompilationInfo(object):
+ """Bundles all the generic information required to compile extensions.
+
+ Note: here, 'system' means OS + target interpreter + test config + ...
+ """
+ def __init__(self, include_extra=None, compile_extra=None, link_extra=None,
+ extra_libs=None, ext=None):
+ self.include_extra = include_extra or []
+ self.compile_extra = compile_extra
+ self.link_extra = link_extra
+ self.extra_libs = extra_libs
+ self.ext = ext
+
+ def compile_extension_module(self, name, include_dirs=None,
+ source_files=None, source_strings=None):
+ """
+ Build an extension module and return the filename of the resulting
+ native code file.
+
+ name is the name of the module, possibly including dots if it is a
+ module inside a package.
+
+ Any extra keyword arguments are passed on to ExternalCompilationInfo to
+ build the module (so specify your source with one of those).
+ """
+ include_dirs = include_dirs or []
+ modname = name.split('.')[-1]
+ dirname = (udir/uniquemodulename('module')).ensure(dir=1)
+ if source_strings:
+ assert not source_files
+ files = convert_sources_to_files(source_strings, dirname)
+ source_files = files
+ soname = c_compile(source_files, outputfilename=str(dirname/modname),
+ compile_extra=self.compile_extra,
+ link_extra=self.link_extra,
+ include_dirs=self.include_extra + include_dirs,
+ libraries=self.extra_libs)
+ pydname = soname.new(purebasename=modname, ext=self.ext)
+ soname.rename(pydname)
+ return str(pydname)
+
+ def import_module(self, name, init=None, body='', filename=None,
+ include_dirs=None, PY_SSIZE_T_CLEAN=False):
+ """
+ init specifies the overall template of the module.
+
+ if init is None, the module source will be loaded from a file in this
+ test directory, give a name given by the filename parameter.
+
+ if filename is None, the module name will be used to construct the
+ filename.
+ """
+ if init is not None:
+ code = make_source(name, init, body, PY_SSIZE_T_CLEAN)
+ kwds = dict(source_strings=[code])
+ else:
+ assert not PY_SSIZE_T_CLEAN
+ if filename is None:
+ filename = name
+ filename = HERE / (filename + ".c")
+ kwds = dict(source_files=[filename])
+ mod = self.compile_extension_module(
+ name, include_dirs=include_dirs, **kwds)
+ return self.load_module(mod, name)
+
+ def import_extension(self, modname, functions, prologue="",
+ include_dirs=None, more_init="", PY_SSIZE_T_CLEAN=False):
+ body = prologue + make_methods(functions, modname)
+ init = """Py_InitModule("%s", methods);""" % (modname,)
+ if more_init:
+ init += more_init
+ return self.import_module(
+ name=modname, init=init, body=body, include_dirs=include_dirs,
+ PY_SSIZE_T_CLEAN=PY_SSIZE_T_CLEAN)
+
+class ExtensionCompiler(SystemCompilationInfo):
+ """Extension compiler for appdirect mode"""
+ def load_module(space, mod, name):
+ import imp
+ return imp.load_dynamic(name, mod)
+
+def convert_sources_to_files(sources, dirname):
+ files = []
+ for i, source in enumerate(sources):
+ filename = dirname / ('source_%d.c' % i)
+ with filename.open('w') as f:
+ f.write(str(source))
+ files.append(filename)
+ return files
+
+
+def make_methods(functions, modname):
+ methods_table = []
+ codes = []
+ for funcname, flags, code in functions:
+ cfuncname = "%s_%s" % (modname, funcname)
+ methods_table.append(
+ "{\"%s\", %s, %s}," % (funcname, cfuncname, flags))
+ func_code = """
+ static PyObject* %s(PyObject* self, PyObject* args)
+ {
+ %s
+ }
+ """ % (cfuncname, code)
+ codes.append(func_code)
+
+ body = "\n".join(codes) + """
+ static PyMethodDef methods[] = {
+ %s
+ { NULL }
+ };
+ """ % ('\n'.join(methods_table),)
+ return body
+
+def make_source(name, init, body, PY_SSIZE_T_CLEAN):
+ code = """
+ %(PY_SSIZE_T_CLEAN)s
+ #include <Python.h>
+ /* fix for cpython 2.7 Python.h if running tests with -A
+ since pypy compiles with -fvisibility-hidden */
+ #undef PyMODINIT_FUNC
+ #ifdef __GNUC__
+ # define RPY_EXPORTED extern __attribute__((visibility("default")))
+ #else
+ # define RPY_EXPORTED extern __declspec(dllexport)
+ #endif
+ #define PyMODINIT_FUNC RPY_EXPORTED void
+
+ %(body)s
+
+ PyMODINIT_FUNC
+ init%(name)s(void) {
+ %(init)s
+ }
+ """ % dict(name=name, init=init, body=body,
+ PY_SSIZE_T_CLEAN='#define PY_SSIZE_T_CLEAN'
+ if PY_SSIZE_T_CLEAN else '')
+ return code
+
+
def c_compile(cfilenames, outputfilename,
compile_extra=None, link_extra=None,
include_dirs=None, libraries=None, library_dirs=None):
@@ -15,9 +160,9 @@
include_dirs = include_dirs or []
libraries = libraries or []
library_dirs = library_dirs or []
- if platform == 'win32':
+ if sys.platform == 'win32':
link_extra = link_extra + ['/DEBUG'] # generate .pdb file
- if platform == 'darwin':
+ if sys.platform == 'darwin':
# support Fink & Darwinports
for s in ('/sw/', '/opt/local/'):
if (s + 'include' not in include_dirs
@@ -66,3 +211,30 @@
libraries=libraries,
extra_preargs=link_extra,
library_dirs=library_dirs)
+
+def get_so_suffix():
+ from imp import get_suffixes, C_EXTENSION
+ for suffix, mode, typ in get_suffixes():
+ if typ == C_EXTENSION:
+ return suffix
+ else:
+ raise RuntimeError("This interpreter does not define a filename "
+ "suffix for C extensions!")
+
+def get_sys_info_app():
+ from distutils.sysconfig import get_python_inc
+ if sys.platform == 'win32':
+ compile_extra = ["/we4013"]
+ link_extra = ["/LIBPATH:" + os.path.join(sys.exec_prefix, 'libs')]
+ elif sys.platform == 'darwin':
+ compile_extra = link_extra = None
+ pass
+ elif sys.platform.startswith('linux'):
+ compile_extra = [
+ "-O0", "-g", "-Werror=implicit-function-declaration", "-fPIC"]
+ link_extra = None
+ return ExtensionCompiler(
+ include_extra=[get_python_inc()],
+ compile_extra=compile_extra,
+ link_extra=link_extra,
+ ext=get_so_suffix())
More information about the pypy-commit
mailing list