Repository: seequent/pure_interface

Issue:
functions are not removed from abstractproperties
This is not a biggie, but it does mean that they are checked for at instantiation.

Discussion:

Patch:
diff --git a/pure_interface.py b/pure_interface.py
index 78b5254..0324fdb 100644
--- a/pure_interface.py
+++ b/pure_interface.py
@@ -91,6 +91,28 @@ class _PIAttributes(object):
         return self.interface_method_names.union(self.interface_attribute_names)
 
 
+class AttributeProperty(object):
+    """ Property that stores it's value in the instance dict under the same name.
+        Abstract properties for concrete classes are replaced with these in the type definition to allow
+        implementations to use attributes.
+    """
+
+    def __init__(self, name):
+        self.name = name
+        super(AttributeProperty, self).__init__()
+
+    def __get__(self, instance, owner):
+        if instance is None:
+            return self
+        try:
+            return instance.__dict__[self.name]
+        except KeyError:
+            raise AttributeError(self.name)
+
+    def __set__(self, instance, value):
+        instance.__dict__[self.name] = value
+
+
 class _ImplementationWrapper(object):
     def __init__(self, implementation, interface):
         self.__impl = implementation
@@ -243,7 +265,7 @@ def _get_instructions(code_obj):
 
 
 def _is_descriptor(obj):  # in our context we only care about __get__
-    return hasattr(obj, '__get__') and not isinstance(obj, types.FunctionType)
+    return hasattr(obj, '__get__')
 
 
 def _signature_info(arg_spec):


Test patch:
diff --git a/tests/test_implementation_checks.py b/tests/test_implementation_checks.py
index 2698387..77a355d 100644
--- a/tests/test_implementation_checks.py
+++ b/tests/test_implementation_checks.py
@@ -514,6 +514,19 @@ class TestAttributeImplementations(unittest.TestCase):
         except:
             self.fail('Instantiation with property that raises failed')
 
+    def test_attr_overridden_with_func(self):
+        # forgotten @property decorator
+        try:
+            class Function(pure_interface.Concrete, IAttribute):
+                def a(self):
+                    return 2
+
+            Function()
+        except:
+            self.fail('Overriding attribute with function should not crash')
+
+        self.assertEqual(frozenset(), Function._pi.abstractproperties)
+
 
 py_36_tests = """
 def test_annotations(self):