Skip to content

Commit

Permalink
Don't propagate fallbacks to subclasses.
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-mitchell committed May 1, 2023
1 parent 9e202b0 commit bf50078
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 9 deletions.
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ Methods and properties defined on the delegate class itself take precedence (as
return 'my bar'

However, attempting to set an instance attribute as an override will just set the attribute on the underlying delegate
instead. If you want to override using an instance attribute, first define it as a class attribute::
instead. If you want to override an interface attribute using an instance attribute, first define it as a class attribute::

class MyDelegate(Delegate, IFoo):
pi_attr_delegates = {'impl': IFoo}
Expand Down
2 changes: 1 addition & 1 deletion pure_interface/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
except ImportError:
pass

__version__ = '7.0.2'
__version__ = '7.1.0'
9 changes: 5 additions & 4 deletions pure_interface/delegation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import operator

import pure_interface
from .errors import InterfaceError
from .interface import get_interface_names, type_is_interface, get_type_interfaces, InterfaceType

Expand Down Expand Up @@ -123,7 +124,7 @@ def i_have_attribute(attrib):
return True
return False

for delegate, attr_list in cls.pi_attr_delegates.items():
for delegate, attr_list in cls.__dict__.get('pi_attr_delegates', {}).items():
if isinstance(attr_list, type):
attr_list = list(get_interface_names(attr_list))
if delegate in cls.pi_attr_mapping:
Expand All @@ -135,11 +136,11 @@ def i_have_attribute(attrib):
continue
dotted_name = f'{delegate}.{attr}'
setattr(cls, attr, _Delegated(dotted_name))
for attr, dotted_name in cls.pi_attr_mapping.items():
for attr, dotted_name in cls.__dict__.get('pi_attr_mapping', {}).items():
if not i_have_attribute(attr):
setattr(cls, attr, _Delegated(dotted_name))
if cls.pi_attr_fallback:
fallback = cls.pi_attr_fallback
fallback = cls.__dict__.get('pi_attr_fallback', None)
if fallback is not None:
for interface in get_type_interfaces(cls):
interface_names = get_interface_names(interface)
for attr in interface_names:
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = pure_interface
version = 7.0.2
version = 7.1.0
description = A Python interface library that disallows function body content on interfaces and supports adaption.
keywords = abc interface adapt adaption mapper structural typing dataclass
author = Tim Mitchell
Expand Down
14 changes: 12 additions & 2 deletions tests/test_delegate.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ class DSubFallback(DFallback, ISubTalker):
pass


class DSubFallback2(DFallback, ISubTalker):
pi_attr_fallback = 'impl'



class DAttrMap(delegation.Delegate, IPoint):
pi_attr_mapping = {'x': 'a.x',
'y': 'b.y',
Expand Down Expand Up @@ -287,8 +292,13 @@ def test_delegate_subclass(self):
self.assertEqual(3, d3.z)

def test_delegate_subclass_fallback(self):
"""Fallback delegates are used for subclass interface attributes too."""
d = DSubFallback(Talker())
"""Check fallback delegates are not used for subclass interface attributes too."""
with self.assertRaises(TypeError):
DSubFallback(Talker()) # no implementation of chat

def test_delegate_subclass_fallback2(self):
"""Check subclass fallbacks are used for missing attributes."""
d = DSubFallback2(Talker())
self.assertEqual('chat', d.chat())


Expand Down

0 comments on commit bf50078

Please sign in to comment.