[pypy-commit] pypy default: cpyext: implement Py_CompileString and PyImport_ExecCodeModule
amauryfa
noreply at buildbot.pypy.org
Sat Aug 20 17:00:24 CEST 2011
Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch:
Changeset: r46663:0a6b064ee727
Date: 2011-08-19 23:13 +0200
http://bitbucket.org/pypy/pypy/changeset/0a6b064ee727/
Log: cpyext: implement Py_CompileString and PyImport_ExecCodeModule
diff --git a/pypy/module/cpyext/eval.py b/pypy/module/cpyext/eval.py
--- a/pypy/module/cpyext/eval.py
+++ b/pypy/module/cpyext/eval.py
@@ -1,11 +1,16 @@
from pypy.interpreter.error import OperationError
from pypy.rpython.lltypesystem import rffi, lltype
from pypy.module.cpyext.api import (
- cpython_api, CANNOT_FAIL, CONST_STRING, FILEP, fread, feof, Py_ssize_tP)
+ cpython_api, CANNOT_FAIL, CONST_STRING, FILEP, fread, feof, Py_ssize_tP,
+ cpython_struct)
from pypy.module.cpyext.pyobject import PyObject, borrow_from
from pypy.module.cpyext.pyerrors import PyErr_SetFromErrno
from pypy.module.__builtin__ import compiling
+PyCompilerFlags = cpython_struct(
+ "PyCompilerFlags", ())
+PyCompilerFlagsPtr = lltype.Ptr(PyCompilerFlags)
+
@cpython_api([PyObject, PyObject, PyObject], PyObject)
def PyEval_CallObjectWithKeywords(space, w_obj, w_arg, w_kwds):
return space.call(w_obj, w_arg, w_kwds)
@@ -69,7 +74,7 @@
Py_file_input = 257
Py_eval_input = 258
-def run_string(space, source, filename, start, w_globals, w_locals):
+def compile_string(space, source, filename, start):
w_source = space.wrap(source)
start = rffi.cast(lltype.Signed, start)
if start == Py_file_input:
@@ -80,8 +85,11 @@
mode = 'single'
else:
raise OperationError(space.w_ValueError, space.wrap(
- "invalid mode parameter for PyRun_String"))
- w_code = compiling.compile(space, w_source, filename, mode)
+ "invalid mode parameter for compilation"))
+ return compiling.compile(space, w_source, filename, mode)
+
+def run_string(space, source, filename, start, w_globals, w_locals):
+ w_code = compile_string(space, source, filename, start)
return compiling.eval(space, w_code, w_globals, w_locals)
@cpython_api([CONST_STRING], rffi.INT_real, error=-1)
@@ -140,3 +148,19 @@
pi[0] = space.getindex_w(w_obj, None)
return 1
+ at cpython_api([rffi.CCHARP, rffi.CCHARP, rffi.INT_real, PyCompilerFlagsPtr],
+ PyObject)
+def Py_CompileStringFlags(space, source, filename, start, flags):
+ """Parse and compile the Python source code in str, returning the
+ resulting code object. The start token is given by start; this
+ can be used to constrain the code which can be compiled and should
+ be Py_eval_input, Py_file_input, or Py_single_input. The filename
+ specified by filename is used to construct the code object and may
+ appear in tracebacks or SyntaxError exception messages. This
+ returns NULL if the code cannot be parsed or compiled."""
+ source = rffi.charp2str(source)
+ filename = rffi.charp2str(filename)
+ if flags:
+ raise OperationError(space.w_NotImplementedError, space.wrap(
+ "cpyext Py_CompileStringFlags does not accept flags"))
+ return compile_string(space, source, filename, start)
diff --git a/pypy/module/cpyext/import_.py b/pypy/module/cpyext/import_.py
--- a/pypy/module/cpyext/import_.py
+++ b/pypy/module/cpyext/import_.py
@@ -2,9 +2,11 @@
from pypy.module.cpyext.api import (
generic_cpy_call, cpython_api, PyObject, CONST_STRING)
from pypy.module.cpyext.pyobject import borrow_from
-from pypy.rpython.lltypesystem import rffi
+from pypy.rpython.lltypesystem import lltype, rffi
from pypy.interpreter.error import OperationError
from pypy.interpreter.module import Module
+from pypy.interpreter.pycode import PyCode
+from pypy.module.imp import importing
@cpython_api([PyObject], PyObject)
def PyImport_Import(space, w_name):
@@ -80,3 +82,44 @@
w_modulesDict = space.sys.get('modules')
return borrow_from(None, w_modulesDict)
+ at cpython_api([rffi.CCHARP, PyObject], PyObject)
+def PyImport_ExecCodeModule(space, name, w_code):
+ """Given a module name (possibly of the form package.module) and a code
+ object read from a Python bytecode file or obtained from the built-in
+ function compile(), load the module. Return a new reference to the module
+ object, or NULL with an exception set if an error occurred. Before Python
+ 2.4, the module could still be created in error cases. Starting with Python
+ 2.4, name is removed from sys.modules in error cases, and even if name was
+ already in sys.modules on entry to PyImport_ExecCodeModule(). Leaving
+ incompletely initialized modules in sys.modules is dangerous, as imports of
+ such modules have no way to know that the module object is an unknown (and
+ probably damaged with respect to the module author's intents) state.
+
+ The module's __file__ attribute will be set to the code object's
+ co_filename.
+
+ This function will reload the module if it was already imported. See
+ PyImport_ReloadModule() for the intended way to reload a module.
+
+ If name points to a dotted name of the form package.module, any package
+ structures not already created will still not be created.
+
+ name is removed from sys.modules in error cases."""
+ return PyImport_ExecCodeModuleEx(space, name, w_code,
+ lltype.nullptr(rffi.CCHARP.TO))
+
+
+ at cpython_api([rffi.CCHARP, PyObject, rffi.CCHARP], PyObject)
+def PyImport_ExecCodeModuleEx(space, name, w_code, pathname):
+ """Like PyImport_ExecCodeModule(), but the __file__ attribute of
+ the module object is set to pathname if it is non-NULL."""
+ code = space.interp_w(PyCode, w_code)
+ w_name = space.wrap(rffi.charp2str(name))
+ if pathname:
+ pathname = rffi.charp2str(pathname)
+ else:
+ pathname = code.co_filename
+ w_mod = importing.add_module(space, w_name)
+ space.setattr(w_mod, space.wrap('__file__'), space.wrap(pathname))
+ importing.exec_code_module(space, w_mod, code)
+ return w_mod
diff --git a/pypy/module/cpyext/include/pythonrun.h b/pypy/module/cpyext/include/pythonrun.h
--- a/pypy/module/cpyext/include/pythonrun.h
+++ b/pypy/module/cpyext/include/pythonrun.h
@@ -13,6 +13,12 @@
#define Py_FrozenFlag 0
+typedef struct {
+ int cf_flags; /* bitmask of CO_xxx flags relevant to future */
+} PyCompilerFlags;
+
+#define Py_CompileString(str, filename, start) Py_CompileStringFlags(str, filename, start, NULL)
+
#ifdef __cplusplus
}
#endif
diff --git a/pypy/module/cpyext/stubs.py b/pypy/module/cpyext/stubs.py
--- a/pypy/module/cpyext/stubs.py
+++ b/pypy/module/cpyext/stubs.py
@@ -1048,37 +1048,6 @@
"""
raise NotImplementedError
- at cpython_api([rffi.CCHARP, PyObject], PyObject)
-def PyImport_ExecCodeModule(space, name, co):
- """Given a module name (possibly of the form package.module) and a code
- object read from a Python bytecode file or obtained from the built-in
- function compile(), load the module. Return a new reference to the module
- object, or NULL with an exception set if an error occurred. Before Python
- 2.4, the module could still be created in error cases. Starting with Python
- 2.4, name is removed from sys.modules in error cases, and even if name was
- already in sys.modules on entry to PyImport_ExecCodeModule(). Leaving
- incompletely initialized modules in sys.modules is dangerous, as imports of
- such modules have no way to know that the module object is an unknown (and
- probably damaged with respect to the module author's intents) state.
-
- The module's __file__ attribute will be set to the code object's
- co_filename.
-
- This function will reload the module if it was already imported. See
- PyImport_ReloadModule() for the intended way to reload a module.
-
- If name points to a dotted name of the form package.module, any package
- structures not already created will still not be created.
-
- name is removed from sys.modules in error cases."""
- raise NotImplementedError
-
- at cpython_api([rffi.CCHARP, PyObject, rffi.CCHARP], PyObject)
-def PyImport_ExecCodeModuleEx(space, name, co, pathname):
- """Like PyImport_ExecCodeModule(), but the __file__ attribute of
- the module object is set to pathname if it is non-NULL."""
- raise NotImplementedError
-
@cpython_api([], lltype.Signed, error=CANNOT_FAIL)
def PyImport_GetMagicNumber(space):
"""Return the magic number for Python bytecode files (a.k.a. .pyc and
@@ -2992,23 +2961,6 @@
returns."""
raise NotImplementedError
- at cpython_api([rffi.CCHARP, rffi.CCHARP, rffi.INT_real], PyObject)
-def Py_CompileString(space, str, filename, start):
- """This is a simplified interface to Py_CompileStringFlags() below, leaving
- flags set to NULL."""
- raise NotImplementedError
-
- at cpython_api([rffi.CCHARP, rffi.CCHARP, rffi.INT_real, PyCompilerFlags], PyObject)
-def Py_CompileStringFlags(space, str, filename, start, flags):
- """Parse and compile the Python source code in str, returning the resulting code
- object. The start token is given by start; this can be used to constrain the
- code which can be compiled and should be Py_eval_input,
- Py_file_input, or Py_single_input. The filename specified by
- filename is used to construct the code object and may appear in tracebacks or
- SyntaxError exception messages. This returns NULL if the code cannot
- be parsed or compiled."""
- raise NotImplementedError
-
@cpython_api([PyCodeObject, PyObject, PyObject], PyObject)
def PyEval_EvalCode(space, co, globals, locals):
"""This is a simplified interface to PyEval_EvalCodeEx(), with just
diff --git a/pypy/module/cpyext/test/test_eval.py b/pypy/module/cpyext/test/test_eval.py
--- a/pypy/module/cpyext/test/test_eval.py
+++ b/pypy/module/cpyext/test/test_eval.py
@@ -221,4 +221,38 @@
return args
assert module.call_func(f) == (None,)
assert module.call_method("text") == 2
-
+
+ def test_CompileString_and_Exec(self):
+ module = self.import_extension('foo', [
+ ("compile_string", "METH_NOARGS",
+ """
+ return Py_CompileString(
+ "f = lambda x: x+5", "someFile", Py_file_input);
+ """),
+ ("exec_code", "METH_O",
+ """
+ return PyImport_ExecCodeModule("cpyext_test_modname", args);
+ """),
+ ("exec_code_ex", "METH_O",
+ """
+ return PyImport_ExecCodeModuleEx("cpyext_test_modname",
+ args, "otherFile");
+ """),
+ ])
+ code = module.compile_string()
+ assert code.co_filename == "someFile"
+ assert code.co_name == "<module>"
+
+ mod = module.exec_code(code)
+ assert mod.__name__ == "cpyext_test_modname"
+ assert mod.__file__ == "someFile"
+ print dir(mod)
+ print mod.__dict__
+ assert mod.f(42) == 47
+
+ mod = module.exec_code_ex(code)
+ assert mod.__name__ == "cpyext_test_modname"
+ assert mod.__file__ == "otherFile"
+ print dir(mod)
+ print mod.__dict__
+ assert mod.f(42) == 47
diff --git a/pypy/module/imp/importing.py b/pypy/module/imp/importing.py
--- a/pypy/module/imp/importing.py
+++ b/pypy/module/imp/importing.py
@@ -540,6 +540,13 @@
if pkgdir is not None:
space.setattr(w_mod, w('__path__'), space.newlist([w(pkgdir)]))
+def add_module(space, w_name):
+ w_mod = check_sys_modules(space, w_name)
+ if w_mod is None:
+ w_mod = space.wrap(Module(space, w_name))
+ space.sys.setmodule(w_mod)
+ return w_mod
+
def load_c_extension(space, filename, modulename):
# the next line is mandatory to init cpyext
space.getbuiltinmodule("cpyext")
More information about the pypy-commit
mailing list