From 885236845a881f235cfc787775af12eaf457d9bc Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Mon, 6 Aug 2012 20:32:19 +0200 Subject: [PATCH] provide compiler directive to set __file__ and __path__ from the source path at module init time --- Cython/Compiler/ModuleNode.py | 24 +++++++++++++++++++++- Cython/Compiler/Options.py | 4 ++++ Cython/Compiler/Symtab.py | 2 +- tests/run/initial_file_path.srctree | 41 +++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 tests/run/initial_file_path.srctree diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index ce65b68..b199565 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -22,7 +22,7 @@ import PyrexTypes from Errors import error, warning from PyrexTypes import py_object_type -from Cython.Utils import open_new_file, replace_suffix +from Cython.Utils import open_new_file, replace_suffix, decode_filename from Code import UtilityCode from StringEncoding import EncodedString @@ -1829,6 +1829,28 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.error_goto(self.pos))) code.putln("}") + if env.directives['set_initial_path_from_source'] and self.pos[0]: + source_path = self.pos[0].filename + if source_path: + code.putln('if (__Pyx_SetAttrString(%s, "__file__", %s) < 0) %s;' % ( + env.module_cname, + code.globalstate.get_py_string_const( + EncodedString(decode_filename(source_path))).cname, + code.error_goto(self.pos))) + if os.path.splitext(os.path.basename(source_path))[0] == '__init__': + # compiling a package => set __path__ as well + temp = code.funcstate.allocate_temp(py_object_type, True) + code.putln('%s = Py_BuildValue("[O]", %s); %s' % ( + temp, + code.globalstate.get_py_string_const( + EncodedString(decode_filename(os.path.dirname(source_path)))).cname, + code.error_goto_if_null(temp, self.pos))) + code.putln('if (__Pyx_SetAttrString(%s, "__path__", %s) < 0) %s;' % ( + env.module_cname, + temp, + code.error_goto(self.pos))) + code.funcstate.release_temp(temp) + if Options.cache_builtins: code.putln("/*--- Builtin init code ---*/") code.putln(code.error_goto_if_neg("__Pyx_InitCachedBuiltins()", self.pos)) diff --git a/Cython/Compiler/Options.py b/Cython/Compiler/Options.py index 05d1056..26e0abf 100644 --- a/Cython/Compiler/Options.py +++ b/Cython/Compiler/Options.py @@ -98,6 +98,9 @@ directive_defaults = { 'fast_getattr': False, # Undocumented until we come up with a better way to handle this everywhere. 'py2_import': False, # For backward compatibility of Cython's source code in Py3 source mode + # set __file__ and/or __path__ to source file path at import time (instead of not having them available) + 'set_initial_path_from_source' : False, + 'warn': None, 'warn.undeclared': False, 'warn.unreachable': True, @@ -153,6 +156,7 @@ directive_scopes = { # defaults to available everywhere 'autotestdict' : ('module',), 'autotestdict.all' : ('module',), 'autotestdict.cdef' : ('module',), + 'set_initial_path_from_source' : ('module',), 'test_assert_path_exists' : ('function', 'class', 'cclass'), 'test_fail_if_path_exists' : ('function', 'class', 'cclass'), } diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 2c3b63f..48e7ba3 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -991,7 +991,7 @@ class ModuleScope(Scope): self.cached_builtins = [] self.undeclared_cached_builtins = [] self.namespace_cname = self.module_cname - for var_name in ['__builtins__', '__name__', '__file__', '__doc__']: + for var_name in ['__builtins__', '__name__', '__file__', '__doc__', '__path__']: self.declare_var(EncodedString(var_name), py_object_type, None) def qualifying_scope(self): diff --git a/tests/run/initial_file_path.srctree b/tests/run/initial_file_path.srctree new file mode 100644 index 0000000..27d7d1a --- /dev/null +++ b/tests/run/initial_file_path.srctree @@ -0,0 +1,41 @@ +PYTHON setup.py build_ext --inplace +PYTHON -c "import my_test_package, my_test_package.a; my_test_package.test(); my_test_package.a.test()" + +######## setup.py ######## + +from Cython.Build.Dependencies import cythonize +from distutils.core import setup + +setup( + ext_modules = cythonize("my_test_package/*.py"), +) + +######## my_test_package/__init__.py ######## + +# cython: set_initial_path_from_source=True + +initial_path = __path__ +initial_file = __file__ + +import a + +def test(): + assert initial_path[0].endswith('my_test_package'), initial_path + assert initial_file.endswith('__init__.py'), initial_file + +######## my_test_package/a.py ######## + +# cython: set_initial_path_from_source=True + +initial_file = __file__ + +try: + initial_path = __path__ +except NameError: + got_name_error = True +else: + got_name_error = False + +def test(): + assert initial_file.endswith('a.py'), initial_file + assert got_name_error, "looks like __path__ was set at module init time: " + initial_path -- 2.7.4