Skip to content

Instantly share code, notes, and snippets.

@BaylorRae
Created January 9, 2019 13:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BaylorRae/288b775e03ad4d3667ec7d827fdb674f to your computer and use it in GitHub Desktop.
Save BaylorRae/288b775e03ad4d3667ec7d827fdb674f to your computer and use it in GitHub Desktop.
def test_ordered_reducer_pattern():
class RandomNumberDependency(Dependency):
def promises(self):
return ['random-number']
def execute(self, context):
context['random-number'] = 5
return context
class BigRandomNumberDependency(Dependency):
def expects(self):
return ['random-number']
def promises(self):
return ['big-random-number']
def execute(self, context):
context['big-random-number'] = context['random-number'] * 5
return context
dependencies = [
RandomNumberDependency,
BigRandomNumberDependency
]
needs = ['big-random-number']
result, history = satisfy_needs(dependencies, needs)
assert result == {
'random-number': 5,
'big-random-number': 25
}
assert history == [
{},
{'random-number': 5},
{'random-number': 5, 'big-random-number': 25}
]
def test_more_dependencies():
class Dep1(Dependency):
def promises(self):
return ['thing-1']
def execute(self, context):
context['thing-1'] = 1
return context
class Dep2(Dependency):
def promises(self):
return ['thing-2']
def expects(self):
return ['thing-1']
def execute(self, context):
context['thing-2'] = context['thing-1'] * 2
return context
class Dep3(Dependency):
def promises(self):
return ['thing-3-from-4-2-1']
def expects(self):
return ['thing-1', 'thing-4.1', 'thing-2', 'thing-4.2']
def execute(self, context):
context['thing-3-from-4-2-1'] = sum(context[req] for req in self.expects())
return context
class Dep4(Dependency):
def promises(self):
return ['thing-4.1', 'thing-4.2']
def expects(self):
return ['thing-2', 'thing-1']
def execute(self, context):
context['thing-4.1'] = context['thing-2'] * 2
context['thing-4.2'] = context['thing-1'] + context['thing-2']
return context
dependencies = [Dep1, Dep2, Dep3, Dep4]
needs = ['thing-3-from-4-2-1']
result, history = satisfy_needs(dependencies, needs)
assert result == {
'thing-1': 1,
'thing-2': 2,
'thing-3-from-4-2-1': 10,
'thing-4.1': 4,
'thing-4.2': 3
}
assert history == [
{},
{'thing-1': 1},
{'thing-1': 1, 'thing-2': 2},
{'thing-1': 1, 'thing-2': 2, 'thing-4.1': 4, 'thing-4.2': 3},
{'thing-1': 1, 'thing-2': 2, 'thing-4.1': 4, 'thing-4.2': 3, 'thing-3-from-4-2-1': 10},
]
def test_stop_early():
"""
I'm not sure if we should return the modified context when stopping early or not.
"""
class RandomNumberDependency(Dependency):
def promises(self):
return ['random-number']
def execute(self, context):
context['random-number'] = 5
return context
class BigRandomNumberDependency(Dependency):
def expects(self):
return ['random-number', 'finished-early']
def promises(self):
return ['big-random-number']
def execute(self, context):
context['big-random-number'] = context['random-number'] * 5
return context
class EarlyFinishDependency(Dependency):
def expects(self):
return ['random-number']
def promises(self):
return ['finished-early']
def execute(self, context):
context['finished-early'] = True
raise StopEarly()
return context
dependencies = [
RandomNumberDependency,
BigRandomNumberDependency,
EarlyFinishDependency
]
needs = ['big-random-number']
result, history = satisfy_needs(dependencies, needs)
assert result == {
'random-number': 5
}
assert history == [
{},
{'random-number': 5}
]
from functools import reduce
class Dependency:
def expects(self):
return []
def promises(self):
return []
def execute(self, context):
return context
class StopEarly(Exception):
pass
class Runner:
def __init__(self):
self.result = None
self.history = []
def run(self, tasks, initial_context={}):
self.history = [initial_context]
try:
reduce(self.run_task, tasks, initial_context)
except StopEarly:
pass
return self.result
def run_task(self, context, task_class):
self.result = task_class.execute(context.copy())
self.history = [*self.history, self.result]
return self.result
def reducer_pattern(tasks, initial_context={}):
runner = Runner()
return runner.run(tasks, initial_context), runner.history
def build_requirements(promises, needs):
for need in needs:
if need not in promises:
raise RuntimeError(f'Could not find {need}')
dependency = promises[need]
for requirement in build_requirements(promises, dependency.expects()):
yield requirement
yield dependency
def simplify_requirements(tasks):
seen = []
for task in tasks:
if task in seen:
continue
seen.append(task)
yield task
def satisfy_needs(dependencies, needs):
promises = {
promise: dependency
for dependency in [d() for d in dependencies]
for promise in dependency.promises()
}
tasks = simplify_requirements(build_requirements(promises, needs))
return reducer_pattern(tasks, {})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment