1 from TreeFragment
import parse_from_strings
, StringParseContext
6 class NonManglingModuleScope(Symtab
.ModuleScope
):
8 def __init__(self
, prefix
, *args
, **kw
):
10 self
.cython_scope
= None
11 Symtab
.ModuleScope
.__init
__(self
, *args
, **kw
)
13 def add_imported_entry(self
, name
, entry
, pos
):
15 return super(NonManglingModuleScope
, self
).add_imported_entry(
18 def mangle(self
, prefix
, name
=None):
20 if prefix
in (Naming
.typeobj_prefix
, Naming
.func_prefix
, Naming
.var_prefix
, Naming
.pyfunc_prefix
):
21 # Functions, classes etc. gets a manually defined prefix easily
22 # manually callable instead (the one passed to CythonUtilityCode)
24 return "%s%s" % (prefix
, name
)
26 return Symtab
.ModuleScope
.mangle(self
, prefix
)
28 class CythonUtilityCodeContext(StringParseContext
):
31 def find_module(self
, module_name
, relative_to
= None, pos
= None,
34 if module_name
!= self
.module_name
:
35 if module_name
not in self
.modules
:
36 raise AssertionError("Only the cython cimport is supported.")
38 return self
.modules
[module_name
]
40 if self
.scope
is None:
41 self
.scope
= NonManglingModuleScope(self
.prefix
,
49 class CythonUtilityCode(Code
.UtilityCodeBase
):
51 Utility code written in the Cython language itself.
53 The @cname decorator can set the cname for a function, method of cdef class.
54 Functions decorated with @cname('c_func_name') get the given cname.
56 For cdef classes the rules are as follows:
57 obj struct -> <cname>_obj
58 obj type ptr -> <cname>_type
59 methods -> <class_cname>_<method_cname>
61 For methods the cname decorator is optional, but without the decorator the
62 methods will not be prototyped. See Cython.Compiler.CythonScope and
63 tests/run/cythonscope.pyx for examples.
66 is_cython_utility
= True
68 def __init__(self
, impl
, name
="__pyxutil", prefix
="", requires
=None,
69 file=None, from_scope
=None, context
=None):
70 # 1) We need to delay the parsing/processing, so that all modules can be
71 # imported without import loops
72 # 2) The same utility code object can be used for multiple source files;
73 # while the generated node trees can be altered in the compilation of a
75 # Hence, delay any processing until later.
76 if context
is not None:
77 impl
= Code
.sub_tempita(impl
, context
, file, name
)
82 self
.requires
= requires
or []
83 self
.from_scope
= from_scope
85 def get_tree(self
, entries_only
=False, cython_scope
=None):
86 from AnalysedTreeTransforms
import AutoTestDictTransform
87 # The AutoTestDictTransform creates the statement "__test__ = {}",
88 # which when copied into the main ModuleNode overwrites
89 # any __test__ in user code; not desired
90 excludes
= [AutoTestDictTransform
]
92 import Pipeline
, ParseTreeTransforms
93 context
= CythonUtilityCodeContext(self
.name
)
94 context
.prefix
= self
.prefix
95 context
.cython_scope
= cython_scope
96 #context = StringParseContext(self.name)
97 tree
= parse_from_strings(self
.name
, self
.impl
, context
=context
,
98 allow_struct_enum_decorator
=True)
99 pipeline
= Pipeline
.create_pipeline(context
, 'pyx', exclude_classes
=excludes
)
105 if isinstance(p
, ParseTreeTransforms
.AnalyseDeclarationsTransform
):
110 transform
= ParseTreeTransforms
.CnameDirectivesTransform(context
)
111 # InterpretCompilerDirectives already does a cdef declarator check
112 #before = ParseTreeTransforms.DecoratorTransform
113 before
= ParseTreeTransforms
.InterpretCompilerDirectives
114 pipeline
= Pipeline
.insert_into_pipeline(pipeline
, transform
,
118 def scope_transform(module_node
):
119 module_node
.scope
.merge_in(self
.from_scope
)
122 transform
= ParseTreeTransforms
.AnalyseDeclarationsTransform
123 pipeline
= Pipeline
.insert_into_pipeline(pipeline
, scope_transform
,
126 (err
, tree
) = Pipeline
.run_pipeline(pipeline
, tree
, printtree
=False)
130 def put_code(self
, output
):
134 def load_as_string(cls
, util_code_name
, from_file
=None, **kwargs
):
136 Load a utility code as a string. Returns (proto, implementation)
138 util
= cls
.load(util_code_name
, from_file
, **kwargs
)
139 return util
.proto
, util
.impl
# keep line numbers => no lstrip()
141 def declare_in_scope(self
, dest_scope
, used
=False, cython_scope
=None,
144 Declare all entries from the utility code in dest_scope. Code will only
145 be included for used entries. If module_name is given, declare the
146 type entries with that name.
148 tree
= self
.get_tree(entries_only
=True, cython_scope
=cython_scope
)
150 entries
= tree
.scope
.entries
151 entries
.pop('__name__')
152 entries
.pop('__file__')
153 entries
.pop('__builtins__')
154 entries
.pop('__doc__')
156 for name
, entry
in entries
.iteritems():
157 entry
.utility_code_definition
= self
160 original_scope
= tree
.scope
161 dest_scope
.merge_in(original_scope
, merge_unused
=True,
163 tree
.scope
= dest_scope
165 for dep
in self
.requires
:
166 if dep
.is_cython_utility
:
167 dep
.declare_in_scope(dest_scope
)
169 return original_scope
171 def declare_declarations_in_scope(declaration_string
, env
, private_type
=True,
174 Declare some declarations given as Cython code in declaration_string
177 CythonUtilityCode(declaration_string
, *args
, **kwargs
).declare_in_scope(env
)