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

cdef public extension type attributes

parent fc147b68
...@@ -624,13 +624,27 @@ class CVarDefNode(StatNode): ...@@ -624,13 +624,27 @@ class CVarDefNode(StatNode):
# declarators [CDeclaratorNode] # declarators [CDeclaratorNode]
# in_pxd boolean # in_pxd boolean
# api boolean # api boolean
# need_properties [entry]
child_attrs = ["base_type", "declarators"] child_attrs = ["base_type", "declarators"]
need_properties = []
def analyse_declarations(self, env, dest_scope = None): def analyse_declarations(self, env, dest_scope = None):
if not dest_scope: if not dest_scope:
dest_scope = env dest_scope = env
self.dest_scope = dest_scope
base_type = self.base_type.analyse(env) 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: for declarator in self.declarators:
name_declarator, type = declarator.analyse(base_type, env) name_declarator, type = declarator.analyse(base_type, env)
if not type.is_complete(): if not type.is_complete():
...@@ -653,8 +667,10 @@ class CVarDefNode(StatNode): ...@@ -653,8 +667,10 @@ class CVarDefNode(StatNode):
if self.in_pxd and self.visibility != 'extern': if self.in_pxd and self.visibility != 'extern':
error(self.pos, error(self.pos,
"Only 'extern' C variable declaration allowed in .pxd file") "Only 'extern' C variable declaration allowed in .pxd file")
dest_scope.declare_var(name, type, declarator.pos, entry = dest_scope.declare_var(name, type, declarator.pos,
cname = cname, visibility = self.visibility, is_cdef = 1) cname = cname, visibility = visibility, is_cdef = 1)
if need_property:
self.need_properties.append(entry)
class CStructOrUnionDefNode(StatNode): class CStructOrUnionDefNode(StatNode):
......
...@@ -301,6 +301,14 @@ class DecoratorTransform(CythonTransform): ...@@ -301,6 +301,14 @@ class DecoratorTransform(CythonTransform):
class AnalyseDeclarationsTransform(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): def __call__(self, root):
self.env_stack = [root.scope] self.env_stack = [root.scope]
return super(AnalyseDeclarationsTransform, self).__call__(root) return super(AnalyseDeclarationsTransform, self).__call__(root)
...@@ -325,7 +333,22 @@ class AnalyseDeclarationsTransform(CythonTransform): ...@@ -325,7 +333,22 @@ class AnalyseDeclarationsTransform(CythonTransform):
# on these nodes in a seperate recursive process from the # on these nodes in a seperate recursive process from the
# enclosing function or module, so we can simply drop them. # enclosing function or module, so we can simply drop them.
def visit_CVarDefNode(self, node): 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): class AnalyseExpressionsTransform(CythonTransform):
def visit_ModuleNode(self, node): def visit_ModuleNode(self, node):
......
...@@ -2299,6 +2299,15 @@ def p_doc_string(s): ...@@ -2299,6 +2299,15 @@ def p_doc_string(s):
return result return result
else: else:
return None 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): def p_module(s, pxd, full_module_name):
s.add_type_name("object") s.add_type_name("object")
......
...@@ -1457,7 +1457,9 @@ class CClassScope(ClassScope): ...@@ -1457,7 +1457,9 @@ class CClassScope(ClassScope):
return entry return entry
def declare_property(self, name, doc, pos): 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.is_property = 1
entry.doc = doc entry.doc = doc
entry.scope = PropertyScope(name, entry.scope = PropertyScope(name,
......
...@@ -28,7 +28,7 @@ class StringParseContext(Main.Context): ...@@ -28,7 +28,7 @@ class StringParseContext(Main.Context):
raise AssertionError("Not yet supporting any cimports/includes from string code snippets") raise AssertionError("Not yet supporting any cimports/includes from string code snippets")
return ModuleScope(module_name, parent_module = None, context = self) 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 Utility method to parse a (unicode) string of code. This is mostly
used for internal Cython compiler purposes (creating code snippets used for internal Cython compiler purposes (creating code snippets
...@@ -56,7 +56,10 @@ def parse_from_strings(name, code, pxds={}): ...@@ -56,7 +56,10 @@ def parse_from_strings(name, code, pxds={}):
scanner = PyrexScanner(buf, code_source, source_encoding = encoding, scanner = PyrexScanner(buf, code_source, source_encoding = encoding,
scope = scope, context = context) 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 return tree
class TreeCopier(VisitorTransform): class TreeCopier(VisitorTransform):
...@@ -171,7 +174,7 @@ def strip_common_indent(lines): ...@@ -171,7 +174,7 @@ def strip_common_indent(lines):
return lines return lines
class TreeFragment(object): 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): if isinstance(code, unicode):
def fmt(x): return u"\n".join(strip_common_indent(x.split(u"\n"))) def fmt(x): return u"\n".join(strip_common_indent(x.split(u"\n")))
...@@ -180,9 +183,9 @@ class TreeFragment(object): ...@@ -180,9 +183,9 @@ class TreeFragment(object):
for key, value in pxds.iteritems(): for key, value in pxds.iteritems():
fmt_pxds[key] = fmt(value) fmt_pxds[key] = fmt(value)
t = parse_from_strings(name, fmt_code, fmt_pxds) mod = t = parse_from_strings(name, fmt_code, fmt_pxds, level=level)
mod = t if level is None:
t = t.body # Make sure a StatListNode is at the top t = t.body # Make sure a StatListNode is at the top
if not isinstance(t, StatListNode): if not isinstance(t, StatListNode):
t = StatListNode(pos=mod.pos, stats=[t]) t = StatListNode(pos=mod.pos, stats=[t])
for transform in pipeline: 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