# declarators [CDeclaratorNode]
# in_pxd boolean
# api boolean
+ # need_properties [entry]
child_attrs = ["base_type", "declarators"]
+ need_properties = []
def analyse_declarations(self, env, dest_scope = None):
if not dest_scope:
dest_scope = env
+ self.dest_scope = dest_scope
base_type = self.base_type.analyse(env)
+
+ if (dest_scope.is_c_class_scope
+ and self.visibility == 'public'
+ and base_type.is_pyobject
+ and (base_type.is_builtin_type or base_type.is_extension_type)):
+ need_property = True
+ visibility = 'private'
+ else:
+ need_property = False
+ visibility = self.visibility
+
for declarator in self.declarators:
name_declarator, type = declarator.analyse(base_type, env)
if not type.is_complete():
if self.in_pxd and self.visibility != 'extern':
error(self.pos,
"Only 'extern' C variable declaration allowed in .pxd file")
- dest_scope.declare_var(name, type, declarator.pos,
- cname = cname, visibility = self.visibility, is_cdef = 1)
+ entry = dest_scope.declare_var(name, type, declarator.pos,
+ cname = cname, visibility = visibility, is_cdef = 1)
+ if need_property:
+ self.need_properties.append(entry)
class CStructOrUnionDefNode(StatNode):
class AnalyseDeclarationsTransform(CythonTransform):
+ basic_property = TreeFragment(u"""
+property NAME:
+ def __get__(self):
+ return ATTR
+ def __set__(self, value):
+ ATTR = value
+ """, level='c_class')
+
def __call__(self, root):
self.env_stack = [root.scope]
return super(AnalyseDeclarationsTransform, self).__call__(root)
# on these nodes in a seperate recursive process from the
# enclosing function or module, so we can simply drop them.
def visit_CVarDefNode(self, node):
- return None
+ if node.need_properties:
+ # cdef public attributes may need type testing on
+ # assignment, so we create a property accesss
+ # mechanism for them.
+ stats = []
+ for entry in node.need_properties:
+ property = self.basic_property.substitute({
+ u"ATTR": AttributeNode(pos=entry.pos, obj=NameNode(pos=entry.pos, name="self"), attribute=entry.name),
+ }, pos=entry.pos)
+ property.stats[0].name = entry.name
+ property.analyse_declarations(node.dest_scope)
+ self.visit(property)
+ stats.append(property)
+ return StatListNode(pos=node.pos, stats=stats)
+ else:
+ return None
class AnalyseExpressionsTransform(CythonTransform):
def visit_ModuleNode(self, node):
return result
else:
return None
+
+def p_code(s, level=None):
+ s.add_type_name("object")
+ s.add_type_name("Py_buffer")
+ body = p_statement_list(s, Ctx(level = level), first_statement = 1)
+ if s.sy != 'EOF':
+ s.error("Syntax error in statement [%s,%s]" % (
+ repr(s.sy), repr(s.systring)))
+ return body
def p_module(s, pxd, full_module_name):
s.add_type_name("object")
return entry
def declare_property(self, name, doc, pos):
- entry = self.declare(name, name, py_object_type, pos)
+ entry = self.lookup_here(name)
+ if entry is None:
+ entry = self.declare(name, name, py_object_type, pos)
entry.is_property = 1
entry.doc = doc
entry.scope = PropertyScope(name,
raise AssertionError("Not yet supporting any cimports/includes from string code snippets")
return ModuleScope(module_name, parent_module = None, context = self)
-def parse_from_strings(name, code, pxds={}):
+def parse_from_strings(name, code, pxds={}, level=None):
"""
Utility method to parse a (unicode) string of code. This is mostly
used for internal Cython compiler purposes (creating code snippets
scanner = PyrexScanner(buf, code_source, source_encoding = encoding,
scope = scope, context = context)
- tree = Parsing.p_module(scanner, 0, module_name)
+ if level is None:
+ tree = Parsing.p_module(scanner, 0, module_name)
+ else:
+ tree = Parsing.p_code(scanner, level=level)
return tree
class TreeCopier(VisitorTransform):
return lines
class TreeFragment(object):
- def __init__(self, code, name="(tree fragment)", pxds={}, temps=[], pipeline=[]):
+ def __init__(self, code, name="(tree fragment)", pxds={}, temps=[], pipeline=[], level=None):
if isinstance(code, unicode):
def fmt(x): return u"\n".join(strip_common_indent(x.split(u"\n")))
for key, value in pxds.iteritems():
fmt_pxds[key] = fmt(value)
- t = parse_from_strings(name, fmt_code, fmt_pxds)
- mod = t
- t = t.body # Make sure a StatListNode is at the top
+ mod = t = parse_from_strings(name, fmt_code, fmt_pxds, level=level)
+ if level is None:
+ t = t.body # Make sure a StatListNode is at the top
if not isinstance(t, StatListNode):
t = StatListNode(pos=mod.pos, stats=[t])
for transform in pipeline: