Skip to content

Commit

Permalink
Remove check_invalid() in favor of cache_off()
Browse files Browse the repository at this point in the history
  • Loading branch information
brandon-rhodes committed May 12, 2015
1 parent 8d1414b commit bf49bea
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 25 deletions.
42 changes: 24 additions & 18 deletions contingent/contingent/projectlib.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Provide a Project of related tasks that can be rebuilt when inputs change.
"""
from contextlib import contextmanager
from collections import namedtuple
from functools import wraps
from .graphlib import Graph
Expand All @@ -14,6 +15,7 @@ def __init__(self):
self._graph = Graph()
self._graph.sort_key = task_key
self._cache = {}
self._cache_on = True
self._task_stack = []
self._todo = set()
self._trace = None
Expand Down Expand Up @@ -47,9 +49,6 @@ def _add_task_to_trace(self, task, return_value):
"""Add a task to the currently running task trace."""
tup = (len(self._task_stack), return_value is _unavailable, task)
self._trace.append(tup)
if len(self._trace) > 30:
print(self.stop_output())
raise RuntimeError()

def task(self, task_function):
"""Decorate a function that defines one of the tasks for this project.
Expand All @@ -72,14 +71,7 @@ def task(self, task_function):
sure that if `task_function` invokes any further tasks, we can
remember that it used their return values and that it will need
to be re-invoked again in the future if any of those other tasks
change their return value.
The wrapper also includes a convenience function,
`check_invalid`, that invokes the `task_function` and updates
the value in the cache if the cache and the current value do not
match. If the cached value is determined to be invalid, the
consequences of the task are added to the to-do list for
reevaluation.
changes its return value.
"""
@wraps(task_function)
Expand All @@ -104,13 +96,6 @@ def wrapper(*args):

return return_value

def check_invalid(*args):
task = Task(wrapper, args)
current_value = task_function(*args)
self.set(task, current_value)

wrapper.check_invalid = check_invalid

return wrapper

def _get_from_cache(self, task):
Expand All @@ -120,10 +105,31 @@ def _get_from_cache(self, task):
returns the singleton `_unavailable` instead.
"""
if not self._cache_on:
return _unavailable
if task in self._todo:
return _unavailable
return self._cache.get(task, _unavailable)

@contextmanager
def cache_off(self):
"""Context manager that forces tasks to really be invoked.
Even if the project has already cached the output of a
particular task, re-running the task inside of this context
manager will make the project re-invoke the task::
with project.cache_off():
my_task()
"""
original_value = self._cache_on
self._cache_on = False
try:
yield
finally:
self._cache_on = original_value

def set(self, task, return_value):
"""Add the `return_value` of `task` to our cache of return values.
Expand Down
7 changes: 4 additions & 3 deletions contingent/example/blog_project.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Render a directory of blog posts as HTML."""
"""Render a directory of cross-referenced blog posts as HTML."""

import os
import re
Expand Down Expand Up @@ -113,8 +113,9 @@ def main():
changed_paths = looping_wait_on(paths)
print('=' * 72)
print('Reloading:', ' '.join(changed_paths))
for path in changed_paths:
read_text_file.check_invalid(path)
with project.cache_off():
for path in changed_paths:
read_text_file(path)
project.rebuild()

if __name__ == '__main__':
Expand Down
11 changes: 7 additions & 4 deletions contingent/make.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ def chapter_doctests(path):
optionflags=doctest.ELLIPSIS,
)

for dot in glob('*.dot'):
read_text_file.check_invalid(dot)
with project.cache_off():
for dot in glob('*.dot'):
read_text_file(dot)


@task
Expand All @@ -90,6 +91,7 @@ def render(path):
png = path[:-3] + 'png'
subprocess.call(['dot', '-Tpng', '-o', png, path])
elif path.endswith('.rst'):
read_text_file(path)
chapter_doctests(path)
check_rst_includes(path)
subprocess.call(['rst2html.py', 'chapter.rst', 'chapter.html'])
Expand All @@ -115,8 +117,9 @@ def main():
changed_paths = looping_wait_on(get_paths())
print('=' * 72)
print('Reloading:', ' '.join(changed_paths))
for path in changed_paths:
read_text_file.check_invalid(path)
with project.cache_off():
for path in changed_paths:
read_text_file(path)
project.start_tracing()
project.rebuild()
print(project.stop_tracing(True))
Expand Down

0 comments on commit bf49bea

Please sign in to comment.