Merge pull request #10 from harlowja/book-names
Rename logbook contents.
This commit is contained in:
@@ -90,71 +90,79 @@ class MemoryCatalog(catalog.Catalog):
|
||||
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):
|
||||
super(MemoryChapter, self).__init__(book, name)
|
||||
self._pages = []
|
||||
super(MemoryWorkflowDetail, self).__init__(book, name)
|
||||
self._tasks = []
|
||||
|
||||
def __iter__(self):
|
||||
for p in self._pages:
|
||||
yield p
|
||||
for t in self._tasks:
|
||||
yield t
|
||||
|
||||
def __contains__(self, page_name):
|
||||
for p in self._pages:
|
||||
if p.name == page_name:
|
||||
def __contains__(self, task_name):
|
||||
for t in self:
|
||||
if t.name == task_name:
|
||||
return True
|
||||
return False
|
||||
|
||||
def fetch_pages(self, page_name):
|
||||
return [p for p in self._pages if p.name == page_name]
|
||||
def fetch_tasks(self, task_name):
|
||||
return [t for t in self if t.name == task_name]
|
||||
|
||||
def __len__(self):
|
||||
return len(self._pages)
|
||||
return len(self._tasks)
|
||||
|
||||
def add_page(self, page):
|
||||
self._pages.append(page)
|
||||
def add_task(self, task_details):
|
||||
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):
|
||||
def __init__(self):
|
||||
super(MemoryLogBook, self).__init__()
|
||||
self._chapters = []
|
||||
self._chapter_names = set()
|
||||
self._workflows = []
|
||||
self._workflow_names = set()
|
||||
self._closed = False
|
||||
|
||||
@check_not_closed
|
||||
def add_chapter(self, chapter_name):
|
||||
if chapter_name in self._chapter_names:
|
||||
raise exc.ChapterAlreadyExists("Chapter %s already exists" %
|
||||
(chapter_name))
|
||||
self._chapters.append(MemoryChapter(self, chapter_name))
|
||||
self._chapter_names.add(chapter_name)
|
||||
def add_workflow(self, workflow_name):
|
||||
if workflow_name in self._workflow_names:
|
||||
raise exc.AlreadyExists()
|
||||
self._workflows.append(MemoryWorkflowDetail(self, workflow_name))
|
||||
self._workflow_names.add(workflow_name)
|
||||
|
||||
@check_not_closed
|
||||
def fetch_chapter(self, chapter_name):
|
||||
if chapter_name not in self._chapter_names:
|
||||
raise exc.ChapterNotFound("No chapter named %s" % (chapter_name))
|
||||
for c in self._chapters:
|
||||
if c.name == chapter_name:
|
||||
return c
|
||||
def fetch_workflow(self, workflow_name):
|
||||
if workflow_name not in self._workflow_names:
|
||||
raise exc.NotFound()
|
||||
for w in self._workflows:
|
||||
if w.name == workflow_name:
|
||||
return w
|
||||
|
||||
@check_not_closed
|
||||
def __iter__(self):
|
||||
for c in self._chapters:
|
||||
yield c
|
||||
for w in self._workflows:
|
||||
yield w
|
||||
|
||||
def close(self):
|
||||
self._closed = True
|
||||
|
||||
@check_not_closed
|
||||
def __contains__(self, chapter_name):
|
||||
for c in self:
|
||||
if c.name == chapter_name:
|
||||
return True
|
||||
return False
|
||||
def __contains__(self, workflow_name):
|
||||
try:
|
||||
self.fetch_workflow(workflow_name)
|
||||
return True
|
||||
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):
|
||||
return len(self._chapters)
|
||||
return len(self._workflows)
|
||||
|
||||
|
||||
class MemoryJobBoard(jobboard.JobBoard):
|
||||
|
||||
@@ -33,13 +33,13 @@ class TaskException(TaskFlowException):
|
||||
self.cause = cause
|
||||
|
||||
|
||||
class ChapterNotFound(TaskFlowException):
|
||||
"""Raised when a chapter of a logbook doesn't exist."""
|
||||
class NotFound(TaskFlowException):
|
||||
"""Raised when some entry in some object doesn't exist."""
|
||||
pass
|
||||
|
||||
|
||||
class ChapterAlreadyExists(TaskFlowException):
|
||||
"""Raised when a chapter of a logbook already exists."""
|
||||
class AlreadyExists(TaskFlowException):
|
||||
"""Raised when some entry in some object already exists."""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ import weakref
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class Page(object):
|
||||
"""A logbook page has the bare minimum of these fields."""
|
||||
class TaskDetail(object):
|
||||
"""Task details have the bare minimum of these fields/methods."""
|
||||
|
||||
def __init__(self, name, metadata=None):
|
||||
self.date_created = datetime.utcnow()
|
||||
@@ -31,12 +31,12 @@ class Page(object):
|
||||
self.metadata = metadata
|
||||
|
||||
def __str__(self):
|
||||
return "Page (%s, %s): %s" % (self.name, self.date_created,
|
||||
self.metadata)
|
||||
return "TaskDetail (%s, %s): %s" % (self.name, self.date_created,
|
||||
self.metadata)
|
||||
|
||||
|
||||
class Chapter(object):
|
||||
"""Base class for what a chapter of a logbook should provide."""
|
||||
class WorkflowDetail(object):
|
||||
"""Workflow details have the bare minimum of these fields/methods."""
|
||||
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@@ -46,34 +46,39 @@ class Chapter(object):
|
||||
|
||||
@abc.abstractmethod
|
||||
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."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def __contains__(self, page_name):
|
||||
"""Determines if any page with the given name exists in this
|
||||
chapter."""
|
||||
def __contains__(self, task_name):
|
||||
"""Determines if any task details with the given name exists in this
|
||||
workflow details."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def fetch_pages(self, page_name):
|
||||
"""Fetch any pages that match the given page name."""
|
||||
def fetch_tasks(self, task_name):
|
||||
"""Fetch any task details that match the given task name."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_page(self, page):
|
||||
"""Adds a page to the underlying chapter."""
|
||||
def add_task(self, task_details):
|
||||
"""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()
|
||||
|
||||
@abc.abstractmethod
|
||||
def __len__(self):
|
||||
"""Returns how many pages the underlying chapter has."""
|
||||
"""Returns how many task details objects the workflow contains."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def __str__(self):
|
||||
return "Chapter (%s): %s pages" % (self.name, len(self))
|
||||
return "WorkflowDetail (%s): %s entries" % (self.name, len(self))
|
||||
|
||||
|
||||
class LogBook(object):
|
||||
@@ -82,35 +87,42 @@ class LogBook(object):
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_chapter(self, chapter_name):
|
||||
"""Atomically adds a new chapter to the given logbook
|
||||
or raises an exception if that chapter (or a chapter with
|
||||
def add_workflow(self, workflow_name):
|
||||
"""Atomically adds a new workflow details object to the given logbook
|
||||
or raises an exception if that workflow (or a workflow with
|
||||
that name) already exists.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def fetch_chapter(self, chapter_name):
|
||||
"""Fetches the given chapter or raises an exception if that chapter
|
||||
does not exist."""
|
||||
def fetch_workflow(self, workflow_name):
|
||||
"""Fetches the given workflow details object for the given workflow
|
||||
name or raises an exception if that workflow name does not exist."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def __contains__(self, chapter_name):
|
||||
"""Determines if a chapter with the given name exists in this
|
||||
logbook."""
|
||||
def __contains__(self, workflow_name):
|
||||
"""Determines if a workflow details object with the given workflow name
|
||||
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()
|
||||
|
||||
@abc.abstractmethod
|
||||
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."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def __len__(self):
|
||||
"""Returns how many pages the underlying chapter has."""
|
||||
"""Returns how many workflow details the logbook contains."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def close(self):
|
||||
|
||||
@@ -34,16 +34,26 @@ from taskflow.backends import memory
|
||||
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):
|
||||
def __init__(self, functor):
|
||||
super(FunctorTask, self).__init__(functor.__name__)
|
||||
self._functor = functor
|
||||
def __init__(self, apply_functor, revert_functor):
|
||||
super(FunctorTask, self).__init__("%s-%s" % (apply_functor.__name__,
|
||||
revert_functor.__name__))
|
||||
self._apply_functor = apply_functor
|
||||
self._revert_functor = revert_functor
|
||||
|
||||
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):
|
||||
pass
|
||||
return self._revert_functor(context, result, cause)
|
||||
|
||||
|
||||
class MemoryBackendTest(unittest.TestCase):
|
||||
@@ -60,32 +70,31 @@ class MemoryBackendTest(unittest.TestCase):
|
||||
def wf_state_change_listener(context, wf, old_state):
|
||||
if wf.name in j.logbook:
|
||||
return
|
||||
j.logbook.add_chapter(wf.name)
|
||||
j.logbook.add_workflow(wf.name)
|
||||
|
||||
stop_after = []
|
||||
|
||||
def task_state_change_listener(context, state, wf, task, result=None):
|
||||
metadata = None
|
||||
chp = j.logbook.fetch_chapter(wf.name)
|
||||
wf_details = j.logbook.fetch_workflow(wf.name)
|
||||
if state in (states.SUCCESS,):
|
||||
metadata = {
|
||||
'result': result,
|
||||
}
|
||||
if task.name in stop_after:
|
||||
# Oops, stopping...
|
||||
wf.interrupt()
|
||||
stop_after.remove(task.name)
|
||||
page_name = "%s:%s" % (task.name, state)
|
||||
if page_name not in chp:
|
||||
chp.add_page(logbook.Page(page_name, metadata))
|
||||
td_name = gen_task_name(task, state)
|
||||
if td_name not in wf_details:
|
||||
wf_details.add_task(logbook.TaskDetail(td_name, metadata))
|
||||
|
||||
def task_result_fetcher(context, wf, task):
|
||||
chp = j.logbook.fetch_chapter(wf.name)
|
||||
# Attempt to find the results page for the given workflow
|
||||
# and task.
|
||||
results_page = "%s:%s" % (task.name, states.SUCCESS)
|
||||
if results_page in chp:
|
||||
page = chp.fetch_pages(results_page)[0]
|
||||
return (True, page.metadata['result'])
|
||||
wf_details = j.logbook.fetch_workflow(wf.name)
|
||||
td_name = gen_task_name(task, states.SUCCESS)
|
||||
if td_name in wf_details:
|
||||
task_details = wf_details.fetch_tasks(td_name)[0]
|
||||
return (True, task_details.metadata['result'])
|
||||
return (False, None)
|
||||
|
||||
wf = lw.Workflow("the-big-action")
|
||||
@@ -99,8 +108,8 @@ class MemoryBackendTest(unittest.TestCase):
|
||||
def do_2(context, *args, **kwargs):
|
||||
call_log.append(2)
|
||||
|
||||
task_1 = FunctorTask(do_1)
|
||||
task_2 = FunctorTask(do_2)
|
||||
task_1 = FunctorTask(do_1, null_functor)
|
||||
task_2 = FunctorTask(do_2, null_functor)
|
||||
wf.add(task_1)
|
||||
wf.add(task_2)
|
||||
wf.task_listeners.append(task_state_change_listener)
|
||||
@@ -112,7 +121,7 @@ class MemoryBackendTest(unittest.TestCase):
|
||||
wf.run({})
|
||||
|
||||
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))
|
||||
|
||||
wf.reset()
|
||||
@@ -120,7 +129,7 @@ class MemoryBackendTest(unittest.TestCase):
|
||||
wf.run({})
|
||||
|
||||
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(states.SUCCESS, wf.state)
|
||||
|
||||
@@ -137,24 +146,24 @@ class MemoryBackendTest(unittest.TestCase):
|
||||
def wf_state_change_listener(context, wf, old_state):
|
||||
if wf.name in j.logbook:
|
||||
return
|
||||
j.logbook.add_chapter(wf.name)
|
||||
j.logbook.add_workflow(wf.name)
|
||||
|
||||
def task_state_change_listener(context, state, wf, task, result=None):
|
||||
metadata = None
|
||||
chp = j.logbook.fetch_chapter(wf.name)
|
||||
wf_details = j.logbook.fetch_workflow(wf.name)
|
||||
if state in (states.SUCCESS,):
|
||||
metadata = {
|
||||
'result': result,
|
||||
}
|
||||
page_name = "%s:%s" % (task.name, state)
|
||||
chp.add_page(logbook.Page(page_name, metadata))
|
||||
wf_details.add_task(logbook.TaskDetail(gen_task_name(task, state),
|
||||
metadata))
|
||||
|
||||
def task_result_fetcher(context, wf, task):
|
||||
chp = j.logbook.fetch_chapter(wf.name)
|
||||
results_page = "%s:%s" % (task.name, states.SUCCESS)
|
||||
if results_page in chp:
|
||||
page = chp.fetch_pages(results_page)[0]
|
||||
return (True, page.metadata['result'])
|
||||
wf_details = j.logbook.fetch_workflow(wf.name)
|
||||
td_name = gen_task_name(task, states.SUCCESS)
|
||||
if td_name in wf_details:
|
||||
task_details = wf_details.fetch_tasks(td_name)[0]
|
||||
return (True, task_details.metadata['result'])
|
||||
return (False, None)
|
||||
|
||||
wf = lw.Workflow("the-big-action")
|
||||
@@ -168,15 +177,15 @@ class MemoryBackendTest(unittest.TestCase):
|
||||
def do_2(context, *args, **kwargs):
|
||||
call_log.append(2)
|
||||
|
||||
wf.add(FunctorTask(do_1))
|
||||
wf.add(FunctorTask(do_2))
|
||||
wf.add(FunctorTask(do_1, null_functor))
|
||||
wf.add(FunctorTask(do_2, null_functor))
|
||||
wf.task_listeners.append(task_state_change_listener)
|
||||
wf.listeners.append(wf_state_change_listener)
|
||||
wf.result_fetcher = task_result_fetcher
|
||||
wf.run({})
|
||||
|
||||
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(states.SUCCESS, wf.state)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user