Merge pull request #10 from harlowja/book-names

Rename logbook contents.
This commit is contained in:
Joshua Harlow
2013-05-16 11:54:47 -07:00
4 changed files with 128 additions and 99 deletions

View File

@@ -90,71 +90,79 @@ class MemoryCatalog(catalog.Catalog):
self._catalogs = [(j, b) for (j, b) in self._catalogs if j != job] self._catalogs = [(j, b) for (j, b) in self._catalogs if j != job]
class MemoryChapter(logbook.Chapter): class MemoryWorkflowDetail(logbook.WorkflowDetail):
def __init__(self, book, name): def __init__(self, book, name):
super(MemoryChapter, self).__init__(book, name) super(MemoryWorkflowDetail, self).__init__(book, name)
self._pages = [] self._tasks = []
def __iter__(self): def __iter__(self):
for p in self._pages: for t in self._tasks:
yield p yield t
def __contains__(self, page_name): def __contains__(self, task_name):
for p in self._pages: for t in self:
if p.name == page_name: if t.name == task_name:
return True return True
return False return False
def fetch_pages(self, page_name): def fetch_tasks(self, task_name):
return [p for p in self._pages if p.name == page_name] return [t for t in self if t.name == task_name]
def __len__(self): def __len__(self):
return len(self._pages) return len(self._tasks)
def add_page(self, page): def add_task(self, task_details):
self._pages.append(page) self._tasks.append(task_details)
def delete_tasks(self, task_name):
self._tasks = [t for t in self if t.name != task_name]
class MemoryLogBook(logbook.LogBook): class MemoryLogBook(logbook.LogBook):
def __init__(self): def __init__(self):
super(MemoryLogBook, self).__init__() super(MemoryLogBook, self).__init__()
self._chapters = [] self._workflows = []
self._chapter_names = set() self._workflow_names = set()
self._closed = False self._closed = False
@check_not_closed @check_not_closed
def add_chapter(self, chapter_name): def add_workflow(self, workflow_name):
if chapter_name in self._chapter_names: if workflow_name in self._workflow_names:
raise exc.ChapterAlreadyExists("Chapter %s already exists" % raise exc.AlreadyExists()
(chapter_name)) self._workflows.append(MemoryWorkflowDetail(self, workflow_name))
self._chapters.append(MemoryChapter(self, chapter_name)) self._workflow_names.add(workflow_name)
self._chapter_names.add(chapter_name)
@check_not_closed @check_not_closed
def fetch_chapter(self, chapter_name): def fetch_workflow(self, workflow_name):
if chapter_name not in self._chapter_names: if workflow_name not in self._workflow_names:
raise exc.ChapterNotFound("No chapter named %s" % (chapter_name)) raise exc.NotFound()
for c in self._chapters: for w in self._workflows:
if c.name == chapter_name: if w.name == workflow_name:
return c return w
@check_not_closed @check_not_closed
def __iter__(self): def __iter__(self):
for c in self._chapters: for w in self._workflows:
yield c yield w
def close(self): def close(self):
self._closed = True self._closed = True
@check_not_closed @check_not_closed
def __contains__(self, chapter_name): def __contains__(self, workflow_name):
for c in self: try:
if c.name == chapter_name: self.fetch_workflow(workflow_name)
return True return True
return False except exc.NotFound:
return False
def delete_workflow(self, workflow_name):
w = self.fetch_workflow(workflow_name)
self._workflow_names.remove(workflow_name)
self._workflows.remove(w)
def __len__(self): def __len__(self):
return len(self._chapters) return len(self._workflows)
class MemoryJobBoard(jobboard.JobBoard): class MemoryJobBoard(jobboard.JobBoard):

View File

@@ -33,13 +33,13 @@ class TaskException(TaskFlowException):
self.cause = cause self.cause = cause
class ChapterNotFound(TaskFlowException): class NotFound(TaskFlowException):
"""Raised when a chapter of a logbook doesn't exist.""" """Raised when some entry in some object doesn't exist."""
pass pass
class ChapterAlreadyExists(TaskFlowException): class AlreadyExists(TaskFlowException):
"""Raised when a chapter of a logbook already exists.""" """Raised when some entry in some object already exists."""
pass pass

View File

@@ -22,8 +22,8 @@ import weakref
from datetime import datetime from datetime import datetime
class Page(object): class TaskDetail(object):
"""A logbook page has the bare minimum of these fields.""" """Task details have the bare minimum of these fields/methods."""
def __init__(self, name, metadata=None): def __init__(self, name, metadata=None):
self.date_created = datetime.utcnow() self.date_created = datetime.utcnow()
@@ -31,12 +31,12 @@ class Page(object):
self.metadata = metadata self.metadata = metadata
def __str__(self): def __str__(self):
return "Page (%s, %s): %s" % (self.name, self.date_created, return "TaskDetail (%s, %s): %s" % (self.name, self.date_created,
self.metadata) self.metadata)
class Chapter(object): class WorkflowDetail(object):
"""Base class for what a chapter of a logbook should provide.""" """Workflow details have the bare minimum of these fields/methods."""
__metaclass__ = abc.ABCMeta __metaclass__ = abc.ABCMeta
@@ -46,34 +46,39 @@ class Chapter(object):
@abc.abstractmethod @abc.abstractmethod
def __iter__(self): def __iter__(self):
"""Iterates over all pages in the given chapter. """Iterates over all task details.
The order will be in the same order that they were added.""" The order will be in the same order that they were added."""
raise NotImplementedError() raise NotImplementedError()
@abc.abstractmethod @abc.abstractmethod
def __contains__(self, page_name): def __contains__(self, task_name):
"""Determines if any page with the given name exists in this """Determines if any task details with the given name exists in this
chapter.""" workflow details."""
raise NotImplementedError() raise NotImplementedError()
@abc.abstractmethod @abc.abstractmethod
def fetch_pages(self, page_name): def fetch_tasks(self, task_name):
"""Fetch any pages that match the given page name.""" """Fetch any task details that match the given task name."""
raise NotImplementedError() raise NotImplementedError()
@abc.abstractmethod @abc.abstractmethod
def add_page(self, page): def add_task(self, task_details):
"""Adds a page to the underlying chapter.""" """Adds a task detail entry to this workflow details."""
raise NotImplementedError()
@abc.abstractmethod
def delete_tasks(self, task_name):
"""Deletes any task details that match the given task name."""
raise NotImplementedError() raise NotImplementedError()
@abc.abstractmethod @abc.abstractmethod
def __len__(self): def __len__(self):
"""Returns how many pages the underlying chapter has.""" """Returns how many task details objects the workflow contains."""
raise NotImplementedError() raise NotImplementedError()
def __str__(self): def __str__(self):
return "Chapter (%s): %s pages" % (self.name, len(self)) return "WorkflowDetail (%s): %s entries" % (self.name, len(self))
class LogBook(object): class LogBook(object):
@@ -82,35 +87,42 @@ class LogBook(object):
__metaclass__ = abc.ABCMeta __metaclass__ = abc.ABCMeta
@abc.abstractmethod @abc.abstractmethod
def add_chapter(self, chapter_name): def add_workflow(self, workflow_name):
"""Atomically adds a new chapter to the given logbook """Atomically adds a new workflow details object to the given logbook
or raises an exception if that chapter (or a chapter with or raises an exception if that workflow (or a workflow with
that name) already exists. that name) already exists.
""" """
raise NotImplementedError() raise NotImplementedError()
@abc.abstractmethod @abc.abstractmethod
def fetch_chapter(self, chapter_name): def fetch_workflow(self, workflow_name):
"""Fetches the given chapter or raises an exception if that chapter """Fetches the given workflow details object for the given workflow
does not exist.""" name or raises an exception if that workflow name does not exist."""
raise NotImplementedError() raise NotImplementedError()
@abc.abstractmethod @abc.abstractmethod
def __contains__(self, chapter_name): def __contains__(self, workflow_name):
"""Determines if a chapter with the given name exists in this """Determines if a workflow details object with the given workflow name
logbook.""" exists in this logbook."""
raise NotImplementedError()
@abc.abstractmethod
def delete_workflow(self, workflow_name):
"""Removes the given workflow details object that matches the provided
workflow name or raises an exception if that workflow name does not
exist."""
raise NotImplementedError() raise NotImplementedError()
@abc.abstractmethod @abc.abstractmethod
def __iter__(self): def __iter__(self):
"""Iterates over all the chapters. """Iterates over all the contained workflow details.
The order will be in the same order that they were added.""" The order will be in the same order that they were added."""
raise NotImplementedError() raise NotImplementedError()
@abc.abstractmethod @abc.abstractmethod
def __len__(self): def __len__(self):
"""Returns how many pages the underlying chapter has.""" """Returns how many workflow details the logbook contains."""
raise NotImplementedError() raise NotImplementedError()
def close(self): def close(self):

View File

@@ -34,16 +34,26 @@ from taskflow.backends import memory
from taskflow.patterns import linear_workflow as lw from taskflow.patterns import linear_workflow as lw
def null_functor(*args, **kwargs):
return None
def gen_task_name(task, state):
return "%s:%s" % (task.name, state)
class FunctorTask(task.Task): class FunctorTask(task.Task):
def __init__(self, functor): def __init__(self, apply_functor, revert_functor):
super(FunctorTask, self).__init__(functor.__name__) super(FunctorTask, self).__init__("%s-%s" % (apply_functor.__name__,
self._functor = functor revert_functor.__name__))
self._apply_functor = apply_functor
self._revert_functor = revert_functor
def apply(self, context, *args, **kwargs): def apply(self, context, *args, **kwargs):
return self._functor(context, *args, **kwargs) return self._apply_functor(context, *args, **kwargs)
def revert(self, context, result, cause): def revert(self, context, result, cause):
pass return self._revert_functor(context, result, cause)
class MemoryBackendTest(unittest.TestCase): class MemoryBackendTest(unittest.TestCase):
@@ -60,32 +70,31 @@ class MemoryBackendTest(unittest.TestCase):
def wf_state_change_listener(context, wf, old_state): def wf_state_change_listener(context, wf, old_state):
if wf.name in j.logbook: if wf.name in j.logbook:
return return
j.logbook.add_chapter(wf.name) j.logbook.add_workflow(wf.name)
stop_after = [] stop_after = []
def task_state_change_listener(context, state, wf, task, result=None): def task_state_change_listener(context, state, wf, task, result=None):
metadata = None metadata = None
chp = j.logbook.fetch_chapter(wf.name) wf_details = j.logbook.fetch_workflow(wf.name)
if state in (states.SUCCESS,): if state in (states.SUCCESS,):
metadata = { metadata = {
'result': result, 'result': result,
} }
if task.name in stop_after: if task.name in stop_after:
# Oops, stopping...
wf.interrupt() wf.interrupt()
stop_after.remove(task.name) stop_after.remove(task.name)
page_name = "%s:%s" % (task.name, state) td_name = gen_task_name(task, state)
if page_name not in chp: if td_name not in wf_details:
chp.add_page(logbook.Page(page_name, metadata)) wf_details.add_task(logbook.TaskDetail(td_name, metadata))
def task_result_fetcher(context, wf, task): def task_result_fetcher(context, wf, task):
chp = j.logbook.fetch_chapter(wf.name) wf_details = j.logbook.fetch_workflow(wf.name)
# Attempt to find the results page for the given workflow td_name = gen_task_name(task, states.SUCCESS)
# and task. if td_name in wf_details:
results_page = "%s:%s" % (task.name, states.SUCCESS) task_details = wf_details.fetch_tasks(td_name)[0]
if results_page in chp: return (True, task_details.metadata['result'])
page = chp.fetch_pages(results_page)[0]
return (True, page.metadata['result'])
return (False, None) return (False, None)
wf = lw.Workflow("the-big-action") wf = lw.Workflow("the-big-action")
@@ -99,8 +108,8 @@ class MemoryBackendTest(unittest.TestCase):
def do_2(context, *args, **kwargs): def do_2(context, *args, **kwargs):
call_log.append(2) call_log.append(2)
task_1 = FunctorTask(do_1) task_1 = FunctorTask(do_1, null_functor)
task_2 = FunctorTask(do_2) task_2 = FunctorTask(do_2, null_functor)
wf.add(task_1) wf.add(task_1)
wf.add(task_2) wf.add(task_2)
wf.task_listeners.append(task_state_change_listener) wf.task_listeners.append(task_state_change_listener)
@@ -112,7 +121,7 @@ class MemoryBackendTest(unittest.TestCase):
wf.run({}) wf.run({})
self.assertEquals(1, len(j.logbook)) self.assertEquals(1, len(j.logbook))
self.assertEquals(2, len(j.logbook.fetch_chapter("the-big-action"))) self.assertEquals(2, len(j.logbook.fetch_workflow("the-big-action")))
self.assertEquals(1, len(call_log)) self.assertEquals(1, len(call_log))
wf.reset() wf.reset()
@@ -120,7 +129,7 @@ class MemoryBackendTest(unittest.TestCase):
wf.run({}) wf.run({})
self.assertEquals(1, len(j.logbook)) self.assertEquals(1, len(j.logbook))
self.assertEquals(4, len(j.logbook.fetch_chapter("the-big-action"))) self.assertEquals(4, len(j.logbook.fetch_workflow("the-big-action")))
self.assertEquals(2, len(call_log)) self.assertEquals(2, len(call_log))
self.assertEquals(states.SUCCESS, wf.state) self.assertEquals(states.SUCCESS, wf.state)
@@ -137,24 +146,24 @@ class MemoryBackendTest(unittest.TestCase):
def wf_state_change_listener(context, wf, old_state): def wf_state_change_listener(context, wf, old_state):
if wf.name in j.logbook: if wf.name in j.logbook:
return return
j.logbook.add_chapter(wf.name) j.logbook.add_workflow(wf.name)
def task_state_change_listener(context, state, wf, task, result=None): def task_state_change_listener(context, state, wf, task, result=None):
metadata = None metadata = None
chp = j.logbook.fetch_chapter(wf.name) wf_details = j.logbook.fetch_workflow(wf.name)
if state in (states.SUCCESS,): if state in (states.SUCCESS,):
metadata = { metadata = {
'result': result, 'result': result,
} }
page_name = "%s:%s" % (task.name, state) wf_details.add_task(logbook.TaskDetail(gen_task_name(task, state),
chp.add_page(logbook.Page(page_name, metadata)) metadata))
def task_result_fetcher(context, wf, task): def task_result_fetcher(context, wf, task):
chp = j.logbook.fetch_chapter(wf.name) wf_details = j.logbook.fetch_workflow(wf.name)
results_page = "%s:%s" % (task.name, states.SUCCESS) td_name = gen_task_name(task, states.SUCCESS)
if results_page in chp: if td_name in wf_details:
page = chp.fetch_pages(results_page)[0] task_details = wf_details.fetch_tasks(td_name)[0]
return (True, page.metadata['result']) return (True, task_details.metadata['result'])
return (False, None) return (False, None)
wf = lw.Workflow("the-big-action") wf = lw.Workflow("the-big-action")
@@ -168,15 +177,15 @@ class MemoryBackendTest(unittest.TestCase):
def do_2(context, *args, **kwargs): def do_2(context, *args, **kwargs):
call_log.append(2) call_log.append(2)
wf.add(FunctorTask(do_1)) wf.add(FunctorTask(do_1, null_functor))
wf.add(FunctorTask(do_2)) wf.add(FunctorTask(do_2, null_functor))
wf.task_listeners.append(task_state_change_listener) wf.task_listeners.append(task_state_change_listener)
wf.listeners.append(wf_state_change_listener) wf.listeners.append(wf_state_change_listener)
wf.result_fetcher = task_result_fetcher wf.result_fetcher = task_result_fetcher
wf.run({}) wf.run({})
self.assertEquals(1, len(j.logbook)) self.assertEquals(1, len(j.logbook))
self.assertEquals(4, len(j.logbook.fetch_chapter("the-big-action"))) self.assertEquals(4, len(j.logbook.fetch_workflow("the-big-action")))
self.assertEquals(2, len(call_log)) self.assertEquals(2, len(call_log))
self.assertEquals(states.SUCCESS, wf.state) self.assertEquals(states.SUCCESS, wf.state)