Author: Amaury Forgeot d'Arc <amaur...@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 +@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) +@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)) + + +@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 -@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 - -@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 -@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 - -@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") _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit