Commit 082fd063 authored by Robert Bradshaw's avatar Robert Bradshaw

cdef public extension type attributes

parent fc147b68
......@@ -624,13 +624,27 @@ class CVarDefNode(StatNode):
# 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():
......@@ -653,8 +667,10 @@ class CVarDefNode(StatNode):
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):
......
......@@ -301,6 +301,14 @@ class DecoratorTransform(CythonTransform):
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)
......@@ -325,7 +333,22 @@ class AnalyseDeclarationsTransform(CythonTransform):
# 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):
......
......@@ -2299,6 +2299,15 @@ def p_doc_string(s):
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")
......
......@@ -1457,7 +1457,9 @@ class CClassScope(ClassScope):
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,
......
......@@ -28,7 +28,7 @@ class StringParseContext(Main.Context):
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
......@@ -56,7 +56,10 @@ def parse_from_strings(name, code, pxds={}):
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):
......@@ -171,7 +174,7 @@ def strip_common_indent(lines):
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")))
......@@ -180,9 +183,9 @@ class TreeFragment(object):
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:
......
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