Skip to content

Commit

Permalink
Use a descriptor for DynamicState.fetch_attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
jpic committed Mar 24, 2014
1 parent 41961e1 commit b710558
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
@@ -1,3 +1,5 @@
0.0.5 Use a descriptor for DynamicState.fetch_attribute to avoid conflicts.

0.0.4 Added signalslot.contrib.dynamic_state.

0.0.3 Removed signal.connect_once(), simplified the code and improved
Expand Down
2 changes: 1 addition & 1 deletion signalslot/__init__.py
@@ -1,4 +1,4 @@
from .signal import Signal
from .exceptions import *

__version__ = '0.0.4'
__version__ = '0.0.5'
56 changes: 54 additions & 2 deletions signalslot/contrib/dynamic_state.py
@@ -1,6 +1,12 @@

from signalslot import Signal


class DynamicStateSignalDescriptor(property):
def __get__(self, cls, owner):
return self.fget.__get__(None, owner)()


class DynamicState(object):
"""
Base class for classes which require having dynamic states.
Expand Down Expand Up @@ -45,8 +51,6 @@ class DynamicState(object):
>>> with pytest.raises(AttributeError):
... test.oops
"""
fetch_attribute = Signal(args=['obj', 'name'])

def __getattr__(self, name):
if name not in self.__dict__:
result = self.fetch_attribute.emit(obj=self, name=name)
Expand All @@ -57,3 +61,51 @@ def __getattr__(self, name):
raise AttributeError

return getattr(self, name)

@classmethod
def get_fetch_attribute(cls):
"""
Using a property descriptor, we ensure that each DynamicState subclass
has its own signal.
>>> class YourObject(DynamicState):
... pass
...
>>> class YourData(DynamicState):
... pass
...
>>> def fetch_yourobject_foo(obj, name, **kwargs):
... if name != 'foo':
... return # not my responsability
... return 'yourobject foo'
...
>>> def fetch_yourobject_data(obj, name, **kwargs):
... if name != 'data':
... return # not my responsability
... return YourData()
...
>>> def fetch_yourdata_foo(obj, name, **kwargs):
... if name != 'foo':
... return # not my responsability
... return 'yourdata foo'
...
>>> YourObject.fetch_attribute.connect(fetch_yourobject_foo)
>>> YourObject.fetch_attribute.connect(fetch_yourobject_data)
>>> YourData.fetch_attribute.connect(fetch_yourdata_foo)
>>> your_object = YourObject()
>>> assert your_object.foo == 'yourobject foo'
>>> assert your_object.data.foo == 'yourdata foo'
"""
fetch_attribute = getattr(cls, '_fetch_attribute', None)

if fetch_attribute is None:
cls._fetch_attribute = Signal(args=['obj', 'name'])

return cls._fetch_attribute

@classmethod
def set_fetch_attribute(cls, value):
cls._fetch_attribute = value

fetch_attribute = DynamicStateSignalDescriptor(get_fetch_attribute,
set_fetch_attribute)

0 comments on commit b710558

Please sign in to comment.