Commit 88523684 authored by Stefan Behnel's avatar Stefan Behnel

provide compiler directive to set __file__ and __path__ from the source path at module init time

parent e7c0e635
......@@ -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))
......
......@@ -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'),
}
......
......@@ -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):
......
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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment