From 082fd0638ce9b4eec80d630f4454c631f61a5e32 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Sat, 2 Aug 2008 16:33:39 -0700 Subject: [PATCH] cdef public extension type attributes --- Cython/Compiler/Nodes.py | 20 ++++++++++++++++++-- Cython/Compiler/ParseTreeTransforms.py | 25 ++++++++++++++++++++++++- Cython/Compiler/Parsing.py | 9 +++++++++ Cython/Compiler/Symtab.py | 4 +++- Cython/Compiler/TreeFragment.py | 15 +++++++++------ 5 files changed, 63 insertions(+), 10 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 1f857e84..8046b4ff 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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): diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index 1fc7778e..c37d2167 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -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): diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 3ff972d0..da3c44c0 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -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") diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index b29789ae..fa450d7b 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -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, diff --git a/Cython/Compiler/TreeFragment.py b/Cython/Compiler/TreeFragment.py index 3e5da2c5..92419837 100644 --- a/Cython/Compiler/TreeFragment.py +++ b/Cython/Compiler/TreeFragment.py @@ -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: -- 2.26.2