[pypy-commit] pypy default: # python 'property' for rpython

aachurin noreply at buildbot.pypy.org
Wed Mar 11 12:23:26 CET 2015


Author: Andrey Churin <aachurin at gmail.com>
Branch: 
Changeset: r76325:8c6cd439d1d1
Date: 2015-03-11 14:13 +0300
http://bitbucket.org/pypy/pypy/changeset/8c6cd439d1d1/

Log:	# python 'property' for rpython

diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py
--- a/rpython/annotator/bookkeeper.py
+++ b/rpython/annotator/bookkeeper.py
@@ -12,7 +12,7 @@
     SomeBuiltin, SomePBC, SomeInteger, TLS, SomeUnicodeCodePoint,
     s_None, s_ImpossibleValue, SomeBool, SomeTuple,
     SomeImpossibleValue, SomeUnicodeString, SomeList, HarmlesslyBlocked,
-    SomeWeakRef, SomeByteArray, SomeConstantType)
+    SomeWeakRef, SomeByteArray, SomeConstantType, SomeProperty)
 from rpython.annotator.classdef import InstanceSource, ClassDef
 from rpython.annotator.listdef import ListDef, ListItem
 from rpython.annotator.dictdef import DictDef
@@ -301,6 +301,8 @@
                 s1 = self.immutablevalue(x1)
                 assert isinstance(s1, SomeInstance)
                 result = SomeWeakRef(s1.classdef)
+        elif tp is property:
+            return SomeProperty(x)
         elif ishashable(x) and x in BUILTIN_ANALYZERS:
             _module = getattr(x,"__module__","unknown")
             result = SomeBuiltin(BUILTIN_ANALYZERS[x], methodname="%s.%s" % (_module, x.__name__))
diff --git a/rpython/annotator/description.py b/rpython/annotator/description.py
--- a/rpython/annotator/description.py
+++ b/rpython/annotator/description.py
@@ -482,6 +482,19 @@
                 self.all_enforced_attrs = []    # no attribute allowed
 
     def add_source_attribute(self, name, value, mixin=False):
+        if isinstance(value, property):
+            # special case for property object
+            if value.fget is not None:
+                newname = name + '__getter__'
+                func = func_with_new_name(value.fget, newname)
+                self.add_source_attribute(newname, func, mixin)
+            if value.fset is not None:
+                newname = name + '__setter__'
+                func = func_with_new_name(value.fset, newname)
+                self.add_source_attribute(newname, func, mixin)
+            self.classdict[name] = Constant(value)
+            return
+
         if isinstance(value, types.FunctionType):
             # for debugging
             if not hasattr(value, 'class_'):
diff --git a/rpython/annotator/model.py b/rpython/annotator/model.py
--- a/rpython/annotator/model.py
+++ b/rpython/annotator/model.py
@@ -587,6 +587,19 @@
         return False
 
 
+class SomeProperty(SomeObject):
+    # used for union error only 
+    immutable = True
+    knowntype = type(property)
+
+    def __init__(self, prop):
+        self.fget = prop.fget
+        self.fset = prop.fset
+
+    def can_be_none(self):
+        return False
+
+
 s_None = SomeNone()
 s_Bool = SomeBool()
 s_Int = SomeInteger()
diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py
--- a/rpython/annotator/test/test_annrpython.py
+++ b/rpython/annotator/test/test_annrpython.py
@@ -4326,6 +4326,82 @@
         assert isinstance(s, annmodel.SomeString)
         assert not s.can_be_none()
 
+    def test_property_getter(self):
+        class O1(object):
+            def __init__(self, x):
+                self._x = x
+            @property
+            def x(self):
+                return self._x
+        def f(n):
+            o = O1(n)
+            return o.x + getattr(o, 'x')
+        a = self.RPythonAnnotator()
+        s = a.build_types(f, [int])
+        assert isinstance(s, annmodel.SomeInteger)
+        op = list(graphof(a, f).iterblocks())[0].operations
+        i = 0
+        c = 0
+        while i < len(op):
+            if op[i].opname == 'getattr':
+                c += 1
+                assert op[i].args[1].value == 'x__getter__'
+                i += 1
+                assert i < len(op) and op[i].opname == 'simple_call' and \
+                            op[i].args[0] == op[i - 1].result
+            i += 1
+        assert c == 2
+
+    def test_property_setter(self):
+        class O2(object):
+            def __init__(self):
+                self._x = 0
+            def set_x(self, v):
+                self._x = v
+            x = property(fset=set_x)
+        def f(n):
+            o = O2()
+            o.x = n
+            setattr(o, 'x', n)
+        a = self.RPythonAnnotator()
+        s = a.build_types(f, [int])
+        op = list(graphof(a, f).iterblocks())[0].operations
+        i = 0
+        c = 0
+        while i < len(op):
+            if op[i].opname == 'getattr':
+                c += 1
+                assert op[i].args[1].value == 'x__setter__'
+                i += 1
+                assert i < len(op) and op[i].opname == 'simple_call' and \
+                            op[i].args[0] == op[i - 1].result and len(op[i].args) == 2
+            i += 1
+        assert c == 2
+
+    def test_property_unionerr(self):
+        class O1(object):
+            def __init__(self, x):
+                self._x = x
+            @property
+            def x(self):
+                return self._x
+        class O2(O1):
+            def set_x(self, v):
+                self._x = v
+            x = property(fset=set_x)
+        def f1(n):
+            o = O2(n)
+            return o.x
+        def f2(n):
+            o = O2(n)
+            o.x = 20
+        a = self.RPythonAnnotator()
+        with py.test.raises(annmodel.UnionError) as exc:
+            a.build_types(f1, [int])
+        a = self.RPythonAnnotator()
+        with py.test.raises(annmodel.UnionError) as exc:
+            a.build_types(f2, [int])
+
 
 def g(n):
     return [0, 1, 2, n]
diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py
--- a/rpython/annotator/unaryop.py
+++ b/rpython/annotator/unaryop.py
@@ -5,7 +5,7 @@
 from __future__ import absolute_import
 
 from rpython.flowspace.operation import op
-from rpython.flowspace.model import const
+from rpython.flowspace.model import const, Constant
 from rpython.annotator.model import (SomeObject, SomeInteger, SomeBool,
     SomeString, SomeChar, SomeList, SomeDict, SomeTuple, SomeImpossibleValue,
     SomeUnicodeCodePoint, SomeInstance, SomeBuiltin, SomeBuiltinMethod,
@@ -715,6 +715,50 @@
             op.simple_call(get_setslice.result, v_start, v_stop, v_iterable)]
 
 
+def _find_property_meth(s_obj, attr, meth):
+    result = []
+    for clsdef in s_obj.classdef.getmro():
+        dct = clsdef.classdesc.classdict
+        if attr not in dct:
+            continue
+        obj = dct[attr]
+        if (not isinstance(obj, Constant) or 
+                not isinstance(obj.value, property)):
+            return
+        result.append(getattr(obj.value, meth))
+    return result
+
+
+ at op.getattr.register_transform(SomeInstance)
+def getattr_SomeInstance(annotator, v_obj, v_attr):
+    s_attr = annotator.annotation(v_attr)
+    if not s_attr.is_constant() or not isinstance(s_attr.const, str):
+        return
+    attr = s_attr.const
+    getters = _find_property_meth(annotator.annotation(v_obj), attr, 'fget')
+    if getters:
+        if all(getters):
+            get_getter = op.getattr(v_obj, const(attr + '__getter__'))
+            return [get_getter, op.simple_call(get_getter.result)]
+        elif not any(getters):
+            raise AnnotatorError("Attribute %r is unreadable" % attr)
+
+
+ at op.setattr.register_transform(SomeInstance)
+def setattr_SomeInstance(annotator, v_obj, v_attr, v_value):
+    s_attr = annotator.annotation(v_attr)
+    if not s_attr.is_constant() or not isinstance(s_attr.const, str):
+        return
+    attr = s_attr.const
+    setters = _find_property_meth(annotator.annotation(v_obj), attr, 'fset')
+    if setters:
+        if all(setters):
+            get_setter = op.getattr(v_obj, const(attr + '__setter__'))
+            return [get_setter, op.simple_call(get_setter.result, v_value)]
+        elif not any(setters):
+            raise AnnotatorError("Attribute %r is unwritable" % attr)
+
+
 class __extend__(SomeBuiltin):
     def call(self, args, implicit_init=False):
         args_s, kwds = args.unpack()


More information about the pypy-commit mailing list