Commit 31609acb authored by Dag Sverre Seljebotn's avatar Dag Sverre Seljebotn Committed by Mark Florisson

Make it possible to construct sub-magic-modules of cython

parent 4e19bc79
from Symtab import ModuleScope from Symtab import ModuleScope
from PyrexTypes import * from PyrexTypes import *
from UtilityCode import CythonUtilityCode from UtilityCode import CythonUtilityCode
from Errors import error
from Scanning import StringSourceDescriptor
class CythonScope(ModuleScope): class CythonScope(ModuleScope):
is_cython_builtin = 1
def __init__(self): def __init__(self):
ModuleScope.__init__(self, u'cython', None, None) ModuleScope.__init__(self, u'cython', None, None)
self.pxd_file_loaded = True self.pxd_file_loaded = True
...@@ -15,7 +19,31 @@ class CythonScope(ModuleScope): ...@@ -15,7 +19,31 @@ class CythonScope(ModuleScope):
return type return type
def find_module(self, module_name, pos): def find_module(self, module_name, pos):
error("cython.%s is not available" % module_name) error("cython.%s is not available" % module_name, pos)
def find_submodule(self, module_name):
entry = self.entries.get(module_name, None)
if entry and entry.as_module:
return entry.as_module
else:
# TODO: fix find_submodule control flow so that we're not
# expected to create a submodule here (to protect CythonScope's
# possible immutability). Hack ourselves out of the situation
# for now.
raise error((StringSourceDescriptor(u"cython", u""), 0, 0),
"cython.%s is not available" % module_name)
def lookup_qualified_name(self, qname):
# ExprNode.as_cython_attribute generates qnames and we untangle it here...
name_path = qname.split(u'.')
scope = self
while len(name_path) > 1:
scope = scope.lookup_here(name_path[0]).as_module
del name_path[0]
if scope is None:
return None
else:
return scope.lookup_here(name_path[0])
def populate_cython_scope(self): def populate_cython_scope(self):
# These are used to optimize isinstance in FinalOptimizePhase # These are used to optimize isinstance in FinalOptimizePhase
...@@ -25,28 +53,43 @@ class CythonScope(ModuleScope): ...@@ -25,28 +53,43 @@ class CythonScope(ModuleScope):
pos = None, pos = None,
cname = 'PyTypeObject') cname = 'PyTypeObject')
type_object.is_void = True type_object.is_void = True
type_object_type = type_object.type
self.declare_cfunction( self.declare_cfunction(
'PyObject_TypeCheck', 'PyObject_TypeCheck',
CFuncType(c_bint_type, [CFuncTypeArg("o", py_object_type, None), CFuncType(c_bint_type, [CFuncTypeArg("o", py_object_type, None),
CFuncTypeArg("t", c_ptr_type(type_object), None)]), CFuncTypeArg("t", c_ptr_type(type_object_type), None)]),
pos = None, pos = None,
defining = 1, defining = 1,
cname = 'PyObject_TypeCheck') cname = 'PyObject_TypeCheck')
#
# A special function just to make it easy to test the scope and # A special function just to make it easy to test the scope and
# utility code functionality in isolation. It is available to # utility code functionality in isolation. It is available to
# "end-users" but nobody will know it is there anyway... # "end-users" but nobody will know it is there anyway...
entry = self.declare_cfunction(
'_testscope',
CFuncType(py_object_type, [CFuncTypeArg("value", c_int_type, None)]),
pos=None,
defining=1,
cname='__pyx_cython__testscope'
)
entry.utility_code_definition = cython_testscope_utility_code
# #
testcythonscope = self.declare_cfunction( # The view sub-scope
'_testcythonscope', #
self.viewscope = viewscope = ModuleScope(u'cython.view', self, None)
self.declare_module('view', viewscope, None)
viewscope.is_cython_builtin = True
viewscope.pxd_file_loaded = True
entry = viewscope.declare_cfunction(
'_testscope',
CFuncType(py_object_type, [CFuncTypeArg("value", c_int_type, None)]), CFuncType(py_object_type, [CFuncTypeArg("value", c_int_type, None)]),
pos=None, pos=None,
defining=1, defining=1,
cname='__pyx_cython__testcythonscope' cname='__pyx_cython_view__testscope'
) )
testcythonscope.utility_code_definition = cython_testscope_utility_code entry.utility_code_definition = cythonview_testscope_utility_code
def create_cython_scope(context): def create_cython_scope(context):
# One could in fact probably make it a singleton, # One could in fact probably make it a singleton,
...@@ -55,6 +98,11 @@ def create_cython_scope(context): ...@@ -55,6 +98,11 @@ def create_cython_scope(context):
return CythonScope() return CythonScope()
cython_testscope_utility_code = CythonUtilityCode(u""" cython_testscope_utility_code = CythonUtilityCode(u"""
cdef object _testcythonscope(int value): cdef object _testscope(int value):
return "hello value=%d" % value return "hello from cython scope, value=%d" % value
""", name="cython utility code", prefix="__pyx_cython_") """, name="cython utility code", prefix="__pyx_cython_")
cythonview_testscope_utility_code = CythonUtilityCode(u"""
cdef object _testscope(int value):
return "hello from cython.view scope, value=%d" % value
""", name="cython utility code", prefix="__pyx_cython_view_")
...@@ -1029,6 +1029,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1029,6 +1029,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
entry.type.typeptr_cname) entry.type.typeptr_cname)
def generate_cvariable_declarations(self, env, code, definition): def generate_cvariable_declarations(self, env, code, definition):
if env.is_cython_builtin:
return
for entry in env.var_entries: for entry in env.var_entries:
if (entry.in_cinclude or entry.in_closure or if (entry.in_cinclude or entry.in_closure or
(entry.visibility == 'private' and (entry.visibility == 'private' and
......
...@@ -2193,7 +2193,7 @@ class TransformBuiltinMethods(EnvTransform): ...@@ -2193,7 +2193,7 @@ class TransformBuiltinMethods(EnvTransform):
entry=self.current_env().builtin_scope().lookup_here(attribute)) entry=self.current_env().builtin_scope().lookup_here(attribute))
elif PyrexTypes.parse_basic_type(attribute): elif PyrexTypes.parse_basic_type(attribute):
pass pass
elif self.context.cython_scope.lookup(attribute): elif self.context.cython_scope.lookup_qualified_name(attribute):
pass pass
else: else:
error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute) error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute)
...@@ -2284,11 +2284,9 @@ class TransformBuiltinMethods(EnvTransform): ...@@ -2284,11 +2284,9 @@ class TransformBuiltinMethods(EnvTransform):
node.cdivision = True node.cdivision = True
elif function == u'set': elif function == u'set':
node.function = ExprNodes.NameNode(node.pos, name=EncodedString('set')) node.function = ExprNodes.NameNode(node.pos, name=EncodedString('set'))
elif self.context.cython_scope.lookup_qualified_name(function):
pass
else: else:
entry = self.context.cython_scope.lookup(function)
if entry and entry.utility_code_definition:
self.env_stack[0].use_utility_code(entry.utility_code_definition)
if not entry:
error(node.function.pos, error(node.function.pos,
u"'%s' not a valid cython language construct" % function) u"'%s' not a valid cython language construct" % function)
......
...@@ -60,16 +60,30 @@ def inject_pxd_code_stage_factory(context): ...@@ -60,16 +60,30 @@ def inject_pxd_code_stage_factory(context):
return module_node return module_node
return inject_pxd_code_stage return inject_pxd_code_stage
def inject_utility_code_stage(module_node): def use_utility_code_definitions(scope, target):
for entry in scope.entries.itervalues():
if entry.used and entry.utility_code_definition:
target.use_utility_code(entry.utility_code_definition)
elif entry.as_module:
use_utility_code_definitions(entry.as_module, target)
def inject_utility_code_stage_factory(context):
def inject_utility_code_stage(module_node):
# First, make sure any utility code pulled in by using symbols in the cython
# scope is included
use_utility_code_definitions(context.cython_scope, module_node.scope)
added = [] added = []
# need to copy list as the list will be altered! # Note: the list might be extended inside the loop (if some utility code
for utilcode in module_node.scope.utility_code_list[:]: # pulls in other utility code)
for utilcode in module_node.scope.utility_code_list:
if utilcode in added: continue if utilcode in added: continue
added.append(utilcode) added.append(utilcode)
tree = utilcode.get_tree() tree = utilcode.get_tree()
if tree: if tree:
module_node.merge_in(tree.body, tree.scope, merge_scope=True) module_node.merge_in(tree.body, tree.scope, merge_scope=True)
return module_node return module_node
return inject_utility_code_stage
# #
# Pipeline factories # Pipeline factories
...@@ -109,6 +123,9 @@ def create_pipeline(context, mode, exclude_classes=()): ...@@ -109,6 +123,9 @@ def create_pipeline(context, mode, exclude_classes=()):
else: else:
_align_function_definitions = None _align_function_definitions = None
# NOTE: This is the "common" parts of the pipeline, which is also
# code in pxd files. So it will be run multiple times in a
# compilation stage.
stages = [ stages = [
NormalizeTree(context), NormalizeTree(context),
PostParse(context), PostParse(context),
...@@ -175,7 +192,7 @@ def create_pyx_pipeline(context, options, result, py=False, exclude_classes=()): ...@@ -175,7 +192,7 @@ def create_pyx_pipeline(context, options, result, py=False, exclude_classes=()):
create_pipeline(context, mode, exclude_classes=exclude_classes), create_pipeline(context, mode, exclude_classes=exclude_classes),
test_support, test_support,
[inject_pxd_code_stage_factory(context), [inject_pxd_code_stage_factory(context),
inject_utility_code_stage, inject_utility_code_stage_factory(context),
abort_on_errors], abort_on_errors],
debug_transform, debug_transform,
[generate_pyx_code_stage_factory(options, result)])) [generate_pyx_code_stage_factory(options, result)]))
......
...@@ -892,9 +892,11 @@ class ModuleScope(Scope): ...@@ -892,9 +892,11 @@ class ModuleScope(Scope):
# types_imported {PyrexType : 1} Set of types for which import code generated # types_imported {PyrexType : 1} Set of types for which import code generated
# has_import_star boolean Module contains import * # has_import_star boolean Module contains import *
# cpp boolean Compiling a C++ file # cpp boolean Compiling a C++ file
# is_cython_builtin boolean Is this the Cython builtin scope (or a child scope)
is_module_scope = 1 is_module_scope = 1
has_import_star = 0 has_import_star = 0
is_cython_builtin = 0
def __init__(self, name, parent_module, context): def __init__(self, name, parent_module, context):
import Builtin import Builtin
......
"""
>>> f()
hello from cython scope, value=4
hello from cython.view scope, value=4
hello from cython scope, value=3
hello from cython.view scope, value=3
"""
cimport cython
from cython cimport _testscope as tester
from cython.view cimport _testscope as viewtester
def f():
print cython._testscope(4)
print cython.view._testscope(4)
print tester(3)
print viewtester(3)
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