Skip to content

Commit

Permalink
Merge pull request #48 from ethanrowe/release-0.7
Browse files Browse the repository at this point in the history
Release 0.7
  • Loading branch information
ethanrowe committed Oct 26, 2016
2 parents 534c19e + d63db9f commit 69ff6ed
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 10 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,12 @@
# Version 0.7.0

* Adds introspection tools for channel management objects:
* `get_channelmethods`
* `get_channelproperties`
* Changes logger names for artifacts and downgrades some messages from INFO level to DEBUG
* This change in logger names could break deployed logging configurations, and for this
reason, we incremented the minor version; otherwise this would be a micro bump.

# Version 0.6.0

* Adds the `channelmethod` decorator as a callable-oriented version of `channelproperty` (Issue #41)
Expand Down
18 changes: 9 additions & 9 deletions flowz/artifacts.py
Expand Up @@ -14,7 +14,7 @@ class AbstractArtifact(object):
An object that wraps the details for asynchronous access to an artifact.
"""

logger = logging.getLogger('Artifact')
logger = logging.getLogger(__name__)
name = None

__exists__ = False
Expand Down Expand Up @@ -117,7 +117,7 @@ def __start_get__(self):
except:
self.logger.exception("%s getter failure." % str(self))
raise
self.logger.info("%s retrieved." % str(self))
self.logger.debug("%s retrieved." % str(self))
raise gen.Return(result)


Expand All @@ -140,17 +140,17 @@ def __init__(self, deriver, *sources, **kw):

@gen.coroutine
def __start_get__(self):
self.logger.info("%s waiting on sources." % str(self))
self.logger.debug("%s waiting on sources." % str(self))
sources = yield [maybe_artifact(source) for source in self.sources]
self.logger.info("%s running deriver." % str(self))
self.logger.debug("%s running deriver." % str(self))
yield gen.moment
try:
result = self.deriver(*sources)
except:
self.logger.exception("%s deriver failure." % str(self))
raise
self.__exists__ = True
self.logger.info("%s ready." % str(self))
self.logger.debug("%s ready." % str(self))
self.sources = None
self.deriver = None
raise gen.Return(result)
Expand All @@ -169,12 +169,12 @@ def __init__(self, executor, deriver, *sources, **kw):
@param sources: zero or more sources that can be synchronous or asychronous
"""
super(ThreadedDerivedArtifact, self).__init__(deriver, *sources, **kw)
self.logger.info("%s created (%s)." % (str(self), self.name))
self.logger.debug("%s created (%s)." % (str(self), self.name))
self.executor = executor

@concurrent.run_on_executor
def __derive__(self, *sources):
self.logger.info("%s running deriver on executor." % str(self))
self.logger.debug("%s running deriver on executor." % str(self))
try:
return self.deriver(*sources)
except:
Expand All @@ -183,11 +183,11 @@ def __derive__(self, *sources):

@gen.coroutine
def __start_get__(self):
self.logger.info("%s waiting on sources." % str(self))
self.logger.debug("%s waiting on sources." % str(self))
sources = yield [maybe_artifact(source) for source in self.sources]
result = yield self.__derive__(*sources)
self.__exists__ = True
self.logger.info("%s ready." % str(self))
self.logger.debug("%s ready." % str(self))
self.sources = None
self.deriver = None
raise gen.Return(result)
Expand Down
29 changes: 29 additions & 0 deletions flowz/channels/management.py
@@ -1,4 +1,5 @@
import functools
import inspect

class AbstractChannelAccessor(object):
"""
Expand Down Expand Up @@ -157,6 +158,7 @@ def wrapped(self):
except KeyError:
manager.add_builder(name, lambda: fn(self))
return manager[name]
wrapped.is_channelmethod = True
return wrapped

def _channelproperty(manager_name, fn):
Expand Down Expand Up @@ -350,3 +352,30 @@ def pairs(self):
return _channelmethod('channel_manager', fn_or_name)
return lambda fn: _channelmethod(fn_or_name, fn)


def get_channelmethods(obj):
"""
Returns a sorted list of the names of all channelmethods defined for an object.
"""
channelmethods = list()
# getmembers() returns a list of tuples already sorted by name
for name, method in inspect.getmembers(obj, inspect.ismethod):
# To be a channelmethod, the method must have an attribute called `is_channelmethod`,
# *and* it must be bound to the object, since it is common for channelmethods from one
# channel manager to be passed into the constructor of a subsequent channel manager!
if hasattr(method, 'is_channelmethod') and (method.__self__ == obj):
channelmethods.append(name)
return tuple(channelmethods)


def get_channelproperties(obj):
"""
Returns a sorted list of the names of all channelproperties defined for an object.
"""
channelproperties = list()
# getmembers() returns a list of tuples already sorted by name
for name, property in inspect.getmembers(obj.__class__, inspect.isdatadescriptor):
if hasattr(property, 'fget'):
if hasattr(property.fget, 'is_channelmethod'):
channelproperties.append(name)
return tuple(channelproperties)
41 changes: 41 additions & 0 deletions flowz/test/manager_test.py
Expand Up @@ -299,3 +299,44 @@ def final(self):
return self.build('final', self.child_a(), self.child_b())


class IgnoreMe(object):
@management.channelmethod
def method_to_ignore(self):
pass


class OneOfBoth(object):
def __init__(self):
super(OneOfBoth, self).__init__()
# This tests to make sure that have an attribute that is a channelmethod passed
# from *another* object is not seen as a channelmethod for this object
self.should_ignore = IgnoreMe().method_to_ignore

@management.channelproperty
def prop(self):
pass

@management.channelmethod
def method(self):
pass


def test_get_channelmethods():
# This test not integrated into classes above because of monkey business creating get_* methods
expected = ChannelBuildHelperCases.HELPER_NAMES
tools.assert_equals(management.get_channelmethods(TestChannelMethod()), expected)
tools.assert_equals(management.get_channelmethods(TestChannelMethodAlternateName()), expected)
tools.assert_equals(management.get_channelmethods(TestChannelProperty()), tuple())
tools.assert_equals(management.get_channelmethods(TestChannelPropertyAlternateName()), tuple())

tools.assert_equals(management.get_channelmethods(OneOfBoth()), ('method',))


def test_get_channelproperties():
expected = ChannelBuildHelperCases.HELPER_NAMES
tools.assert_equals(management.get_channelproperties(TestChannelMethod()), tuple())
tools.assert_equals(management.get_channelproperties(TestChannelMethodAlternateName()), tuple())
tools.assert_equals(management.get_channelproperties(TestChannelProperty()), expected)
tools.assert_equals(management.get_channelproperties(TestChannelPropertyAlternateName()), expected)

tools.assert_equals(management.get_channelproperties(OneOfBoth()), ('prop',))
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -2,7 +2,7 @@

setup(
name = 'flowz',
version = '0.6.0',
version = '0.7.0',
description = 'Async I/O - oriented dependency programming framework',
url = 'https://github.com/ethanrowe/flowz',
author = 'Ethan Rowe',
Expand Down

0 comments on commit 69ff6ed

Please sign in to comment.