def trash(self, file) :
        """
        Trash a file in the appropriate trash directory.
        If the file belong to the same volume of the trash home directory it
        will be trashed in the home trash directory.
        Otherwise it will be trashed in one of the relevant volume trash
        directories.

        Each volume can have two trash directories, they are
            - $volume/.Trash/$uid
            - $volume/.Trash-$uid

        Firstly the software attempt to trash the file in the first directory
        then try to trash in the second trash directory.
        """

        if self._should_skipped_by_specs(file):
            self.reporter.unable_to_trash_dot_entries(file)
            return

        volume_of_file_to_be_trashed = self.volume_of_parent(file)
        self.reporter.volume_of_file(volume_of_file_to_be_trashed)
        candidates = self._possible_trash_directories_for(
                        volume_of_file_to_be_trashed)

        self.try_trash_file_using_candidates(file,
                                             volume_of_file_to_be_trashed,
                                             candidates)

def trash ( self , file ) : if self . _should_skipped_by_specs ( file ) : self . reporter . unable_to_trash_dot_entries ( file ) return volume_of_file_to_be_trashed = self . volume_of_parent ( file ) self . reporter . volume_of_file ( volume_of_file_to_be_trashed ) candidates = self . _possible_trash_directories_for ( volume_of_file_to_be_trashed ) self . try_trash_file_using_candidates ( file , volume_of_file_to_be_trashed , candidates )

Trash a file in the appropriate trash directory . If the file belong to the same volume of the trash home directory it will be trashed in the home trash directory . Otherwise it will be trashed in one of the relevant volume trash directories .


def get_process_parser(self, process_id_or_name):
        """
        Returns the ProcessParser for the given process ID or name. It matches
        by name first.
        """
        if process_id_or_name in self.process_parsers_by_name:
            return self.process_parsers_by_name[process_id_or_name]
        else:
            return self.process_parsers[process_id_or_name]

def get_process_parser ( self , process_id_or_name ) : if process_id_or_name in self . process_parsers_by_name : return self . process_parsers_by_name [ process_id_or_name ] else : return self . process_parsers [ process_id_or_name ]

Returns the ProcessParser for the given process ID or name . It matches by name first .


def add_bpmn_files(self, filenames):
        """
        Add all filenames in the given list to the parser's set.
        """
        for filename in filenames:
            f = open(filename, 'r')
            try:
                self.add_bpmn_xml(ET.parse(f), filename=filename)
            finally:
                f.close()

def add_bpmn_files ( self , filenames ) : for filename in filenames : f = open ( filename , 'r' ) try : self . add_bpmn_xml ( ET . parse ( f ) , filename = filename ) finally : f . close ( )

Add all filenames in the given list to the parser s set .


def add_bpmn_xml(self, bpmn, svg=None, filename=None):
        """
        Add the given lxml representation of the BPMN file to the parser's set.

        :param svg: Optionally, provide the text data for the SVG of the BPMN
          file
        :param filename: Optionally, provide the source filename.
        """
        xpath = xpath_eval(bpmn)

        processes = xpath('.//bpmn:process')
        for process in processes:
            process_parser = self.PROCESS_PARSER_CLASS(
                self, process, svg, filename=filename, doc_xpath=xpath)
            if process_parser.get_id() in self.process_parsers:
                raise ValidationException(
                    'Duplicate process ID', node=process, filename=filename)
            if process_parser.get_name() in self.process_parsers_by_name:
                raise ValidationException(
                    'Duplicate process name', node=process, filename=filename)
            self.process_parsers[process_parser.get_id()] = process_parser
            self.process_parsers_by_name[
                process_parser.get_name()] = process_parser

def
add_bpmn_xml
(
self
,
bpmn
,
svg
=
,
filename
=
)
:
xpath
=
xpath_eval
(
bpmn
)
processes
=
xpath
(
'.//bpmn:process'
)
for
process
in
processes
:
process_parser
=
self
.
PROCESS_PARSER_CLASS
(
self
,
process
,
svg
,
filename
=
filename
,
doc_xpath
=
xpath
)
if
process_parser
.
get_id
(
)
in
self
.
process_parsers
:
raise
ValidationException
(
'Duplicate process ID'
,
node
=
process
,
filename
=
filename
)
if
process_parser
.
get_name
(
)
in
self
.
process_parsers_by_name
:
raise
ValidationException
(
'Duplicate process name'
,
node
=
process
,
filename
=
filename
)
self
.
process_parsers
[
process_parser
.
get_id
(
)
]
=
process_parser
self
.
process_parsers_by_name
[
process_parser
.
get_name
(
)
]
=
process_parser

Add the given lxml representation of the BPMN file to the parser s set .


def one(nodes, or_none=False):
    """
    Assert that there is exactly one node in the give list, and return it.
    """
    if not nodes and or_none:
        return None
    assert len(
        nodes) == 1, 'Expected 1 result. Received %d results.' % (len(nodes))
    return nodes[0]

Assert that there is exactly one node in the give list and return it .


def serialize_value(self, parent_elem, value):
        """
        Serializes str, Attrib, or PathAttrib objects.

        Example::

            <attribute>foobar</attribute>
        """
        if isinstance(value, (str, int)) or type(value).__name__ == 'str':
            parent_elem.text = str(value)
        elif value is None:
            parent_elem.text = None
        else:
            parent_elem.append(value.serialize(self))

def serialize_value ( self , parent_elem , value ) : if isinstance ( value , ( str , int ) ) or type ( value ) . __name__ == 'str' : parent_elem . text = str ( value ) elif value is None : parent_elem . text = None else : parent_elem . append ( value . serialize ( self ) )

Serializes str Attrib or PathAttrib objects .


def serialize_value_list(self, list_elem, thelist):
        """
        Serializes a list, where the values are objects of type
        str, Attrib, or PathAttrib.

        Example::

            <value>text</value>
            <value><attribute>foobar</attribute></value>
            <value><path>foobar</path></value>
        """
        for value in thelist:
            value_elem = SubElement(list_elem, 'value')
            self.serialize_value(value_elem, value)
        return list_elem

def serialize_value_list ( self , list_elem , thelist ) : for value in thelist : value_elem = SubElement ( list_elem , 'value' ) self . serialize_value ( value_elem , value ) return list_elem

Serializes a list where the values are objects of type str Attrib or PathAttrib .


def get_event_definition(self):
        """
        Parse the event definition node, and return an instance of Event
        """
        messageEventDefinition = first(
            self.xpath('.//bpmn:messageEventDefinition'))
        if messageEventDefinition is not None:
            return self.get_message_event_definition(messageEventDefinition)

        timerEventDefinition = first(
            self.xpath('.//bpmn:timerEventDefinition'))
        if timerEventDefinition is not None:
            return self.get_timer_event_definition(timerEventDefinition)

        raise NotImplementedError(
            'Unsupported Intermediate Catch Event: %r', ET.tostring(self.node))

Parse the event definition node and return an instance of Event


def get_message_event_definition(self, messageEventDefinition):
        """
        Parse the messageEventDefinition node and return an instance of
        MessageEventDefinition
        """
        messageRef = first(self.xpath('.//bpmn:messageRef'))
        message = messageRef.get(
            'name') if messageRef is not None else self.node.get('name')
        return MessageEventDefinition(message)

def get_message_event_definition ( self , messageEventDefinition ) : messageRef = first ( self . xpath ( './/bpmn:messageRef' ) ) message = messageRef . get ( 'name' ) if messageRef is not None else self . node . get ( 'name' ) return MessageEventDefinition ( message )


def get_timer_event_definition(self, timerEventDefinition):
        """
        Parse the timerEventDefinition node and return an instance of
        TimerEventDefinition

        This currently only supports the timeDate node for specifying an expiry
        time for the timer.
        """
        timeDate = first(self.xpath('.//bpmn:timeDate'))
        return TimerEventDefinition(
            self.node.get('name', timeDate.text),
            self.parser.parse_condition(
                timeDate.text, None, None, None, None, self))

def get_timer_event_definition ( self , timerEventDefinition ) : timeDate = first ( self . xpath ( './/bpmn:timeDate' ) ) return TimerEventDefinition ( self . node . get ( 'name' , timeDate . text ) , self . parser . parse_condition ( timeDate . text , None , None , None , None , self ) )


def to_html_string(self):
        """
        Returns an etree HTML node with a document describing the process. This
        is only supported if the editor provided an SVG representation.
        """
        html = ET.Element('html')
        head = ET.SubElement(html, 'head')
        title = ET.SubElement(head, 'title')
        title.text = self.description
        body = ET.SubElement(html, 'body')
        h1 = ET.SubElement(body, 'h1')
        h1.text = self.description
        span = ET.SubElement(body, 'span')
        span.text = '___CONTENT___'

        html_text = ET.tostring(html)

        svg_content = ''
        svg_done = set()
        for spec in self.get_specs_depth_first():
            if spec.svg and spec.svg not in svg_done:
                svg_content += '<p>' + spec.svg + "</p>"
                svg_done.add(spec.svg)
        return html_text.replace('___CONTENT___', svg_content)

def to_html_string ( self ) : html = ET . Element ( 'html' ) head = ET . SubElement ( html , 'head' ) title = ET . SubElement ( head , 'title' ) title . text = self . description body = ET . SubElement ( html , 'body' ) h1 = ET . SubElement ( body , 'h1' ) h1 . text = self . description span = ET . SubElement ( body , 'span' ) span . text = ' CONTENT ' html_text = ET . tostring ( html ) svg_content = '' svg_done = set ( ) for spec in self . get_specs_depth_first ( ) : if spec . svg and spec . svg not in svg_done : svg_content += '<p>' + spec . svg + "</p>" svg_done . add ( spec . svg ) return html_text . replace ( ' CONTENT ' , svg_content )

Returns an etree HTML node with a document describing the process . This is only supported if the editor provided an SVG representation .


def connect(self, callback, *args, **kwargs):
        """
        Connects the event with the given callback.
        When the signal is emitted, the callback is invoked.

        .. note::

            The signal handler is stored with a hard reference, so you
            need to make sure to call :class:`disconnect()` if you want the
            handler
            to be garbage collected.

        :type  callback: object
        :param callback: The callback function.
        :type  args: tuple
        :param args: Optional arguments passed to the callback.
        :type  kwargs: dict
        :param kwargs: Optional keyword arguments passed to the callback.
        """
        if self.is_connected(callback):
            raise AttributeError('callback is already connected')
        if self.hard_subscribers is None:
            self.hard_subscribers = []
        self.hard_subscribers.append((callback, args, kwargs))

def
connect
(
self
,
callback
,
*
args
,
**
kwargs
)
:
if
self
.
is_connected
(
callback
)
:
raise
AttributeError
(
'callback is already connected'
)
if
self
.
hard_subscribers
is
:
self
.
hard_subscribers
=
[
]
self
.
hard_subscribers
.
append
(
(
callback
,
args
,
kwargs
)
)

Connects the event with the given callback . When the signal is emitted the callback is invoked .


def n_subscribers(self):
        """
        Returns the number of connected subscribers.

        :rtype:  int
        :returns: The number of subscribers.
        """
        hard = self.hard_subscribers and len(self.hard_subscribers) or 0
        weak = self.weak_subscribers and len(self.weak_subscribers) or 0
        return hard + weak

def n_subscribers ( self ) : hard = self . hard_subscribers and len ( self . hard_subscribers ) or 0 weak = self . weak_subscribers and len ( self . weak_subscribers ) or 0 return hard + weak

Returns the number of connected subscribers .


def is_connected(self, callback):
        """
        Returns True if the event is connected to the given function.

        :type  callback: object
        :param callback: The callback function.
        :rtype:  bool
        :returns: Whether the signal is connected to the given function.
        """
        index = self._weakly_connected_index(callback)
        if index is not None:
            return True
        if self.hard_subscribers is None:
            return False
        return callback in self._hard_callbacks()

def is_connected ( self , callback ) : index = self . _weakly_connected_index ( callback ) if index is not None : return True if self . hard_subscribers is None : return False return callback in self . _hard_callbacks ( )

Returns True if the event is connected to the given function .


def _try_disconnect(self, ref):
        """
        Called by the weak reference when its target dies.
        In other words, we can assert that self.weak_subscribers is not
        None at this time.
        """
        with self.lock:
            weak = [s[0] for s in self.weak_subscribers]
            try:
                index = weak.index(ref)
            except ValueError:
                # subscriber was already removed by a call to disconnect()
                pass
            else:
                self.weak_subscribers.pop(index)

def _try_disconnect ( self , ref ) : with self . lock : weak = [ s [ 0 ] for s in self . weak_subscribers ] try : index = weak . index ( ref ) except ValueError : pass else : self . weak_subscribers . pop ( index )

Called by the weak reference when its target dies . In other words we can assert that self . weak_subscribers is not None at this time .


def disconnect(self, callback):
        """
        Disconnects the signal from the given function.

        :type  callback: object
        :param callback: The callback function.
        """
        if self.weak_subscribers is not None:
            with self.lock:
                index = self._weakly_connected_index(callback)
                if index is not None:
                    self.weak_subscribers.pop(index)[0]
        if self.hard_subscribers is not None:
            try:
                index = self._hard_callbacks().index(callback)
            except ValueError:
                pass
            else:
                self.hard_subscribers.pop(index)

def disconnect ( self , callback ) : if self . weak_subscribers is not None : with self . lock : index = self . _weakly_connected_index ( callback ) if index is not None : self . weak_subscribers . pop ( index ) [ 0 ] if self . hard_subscribers is not None : try : index = self . _hard_callbacks ( ) . index ( callback ) except ValueError : pass else : self . hard_subscribers . pop ( index )

Disconnects the signal from the given function .


def parse_node(self):
        """
        Parse this node, and all children, returning the connected task spec.
        """

        try:
            self.task = self.create_task()

            self.task.documentation = self.parser._parse_documentation(
                self.node, xpath=self.xpath, task_parser=self)

            boundary_event_nodes = self.process_xpath(
                './/bpmn:boundaryEvent[@attachedToRef="%s"]' % self.get_id())
            if boundary_event_nodes:
                parent_task = _BoundaryEventParent(
                    self.spec, '%s.BoundaryEventParent' % self.get_id(),
                    self.task, lane=self.task.lane)
                self.process_parser.parsed_nodes[
                    self.node.get('id')] = parent_task

                parent_task.connect_outgoing(
                    self.task, '%s.FromBoundaryEventParent' % self.get_id(),
                    None, None)
                for boundary_event in boundary_event_nodes:
                    b = self.process_parser.parse_node(boundary_event)
                    parent_task.connect_outgoing(
                        b,
                        '%s.FromBoundaryEventParent' % boundary_event.get(
                            'id'),
                        None, None)
            else:
                self.process_parser.parsed_nodes[
                    self.node.get('id')] = self.task

            children = []
            outgoing = self.process_xpath(
                './/bpmn:sequenceFlow[@sourceRef="%s"]' % self.get_id())
            if len(outgoing) > 1 and not self.handles_multiple_outgoing():
                raise ValidationException(
                    'Multiple outgoing flows are not supported for '
                    'tasks of type',
                    node=self.node,
                    filename=self.process_parser.filename)
            for sequence_flow in outgoing:
                target_ref = sequence_flow.get('targetRef')
                target_node = one(
                    self.process_xpath('.//*[@id="%s"]' % target_ref))
                c = self.process_parser.parse_node(target_node)
                children.append((c, target_node, sequence_flow))

            if children:
                default_outgoing = self.node.get('default')
                if not default_outgoing:
                    (c, target_node, sequence_flow) = children[0]
                    default_outgoing = sequence_flow.get('id')

                for (c, target_node, sequence_flow) in children:
                    self.connect_outgoing(
                        c, target_node, sequence_flow,
                        sequence_flow.get('id') == default_outgoing)

            return parent_task if boundary_event_nodes else self.task
        except ValidationException:
            raise
        except Exception as ex:
            exc_info = sys.exc_info()
            tb = "".join(traceback.format_exception(
                exc_info[0], exc_info[1], exc_info[2]))
            LOG.error("%r\n%s", ex, tb)
            raise ValidationException(
                "%r" % (ex), node=self.node,
                filename=self.process_parser.filename)

Parse this node and all children returning the connected task spec .


def create_task(self):
        """
        Create an instance of the task appropriately. A subclass can override
        this method to get extra information from the node.
        """
        return self.spec_class(self.spec, self.get_task_spec_name(),
                               lane=self.get_lane(),
                               description=self.node.get('name', None))

def create_task ( self ) : return self . spec_class ( self . spec , self . get_task_spec_name ( ) , lane = self . get_lane ( ) , description = self . node . get ( 'name' , None ) )

Create an instance of the task appropriately . A subclass can override this method to get extra information from the node .


def connect_outgoing(self, outgoing_task, outgoing_task_node,
                         sequence_flow_node, is_default):
        """
        Connects this task to the indicating outgoing task, with the details in
        the sequence flow. A subclass can override this method to get extra
        information from the node.
        """
        self.task.connect_outgoing(
            outgoing_task, sequence_flow_node.get('id'),
            sequence_flow_node.get(
                'name', None),
            self.parser._parse_documentation(sequence_flow_node,
                                             task_parser=self))

def connect_outgoing ( self , outgoing_task , outgoing_task_node , sequence_flow_node , is_default ) : self . task . connect_outgoing ( outgoing_task , sequence_flow_node . get ( 'id' ) , sequence_flow_node . get ( 'name' , None ) , self . parser . _parse_documentation ( sequence_flow_node , task_parser = self ) )

Connects this task to the indicating outgoing task with the details in the sequence flow . A subclass can override this method to get extra information from the node .


def connect_outgoing(self, taskspec, sequence_flow_id, sequence_flow_name,
                         documentation):
        """
        Connect this task spec to the indicated child.

        :param sequence_flow_id: The ID of the connecting sequenceFlow node.

        :param sequence_flow_name: The name of the connecting sequenceFlow
        node.
        """
        self.connect(taskspec)
        s = SequenceFlow(
            sequence_flow_id, sequence_flow_name, documentation, taskspec)
        self.outgoing_sequence_flows[taskspec.name] = s
        self.outgoing_sequence_flows_by_id[sequence_flow_id] = s

def connect_outgoing ( self , taskspec , sequence_flow_id , sequence_flow_name , documentation ) : self . connect ( taskspec ) s = SequenceFlow ( sequence_flow_id , sequence_flow_name , documentation , taskspec ) self . outgoing_sequence_flows [ taskspec . name ] = s self . outgoing_sequence_flows_by_id [ sequence_flow_id ] = s

Connect this task spec to the indicated child .


def get_outgoing_sequence_names(self):
        """
        Returns a list of the names of outgoing sequences. Some may be None.
        """
        return sorted([s.name for s in
                       list(self.outgoing_sequence_flows_by_id.values())])

def get_outgoing_sequence_names ( self ) : return sorted ( [ s . name for s in list ( self . outgoing_sequence_flows_by_id . values ( ) ) ] )

Returns a list of the names of outgoing sequences . Some may be None .


def connect_if(self, condition, task_spec):
        """
        Connects a taskspec that is executed if the condition DOES match.

        condition -- a condition (Condition)
        taskspec -- the conditional task spec
        """
        assert task_spec is not None
        self.outputs.append(task_spec)
        self.cond_task_specs.append((condition, task_spec.name))
        task_spec._connect_notify(self)

def connect_if ( self , condition , task_spec ) : assert task_spec is not None self . outputs . append ( task_spec ) self . cond_task_specs . append ( ( condition , task_spec . name ) ) task_spec . _connect_notify ( self )

Connects a taskspec that is executed if the condition DOES match .


def _on_complete_hook(self, my_task):
        """
        Runs the task. Should not be called directly.
        Returns True if completed, False otherwise.
        """
        # Find all matching conditions.
        outputs = []
        for condition, output in self.cond_task_specs:
            if self.choice is not None and output not in self.choice:
                continue
            if condition is None:
                outputs.append(self._wf_spec.get_task_spec_from_name(output))
                continue
            if not condition._matches(my_task):
                continue
            outputs.append(self._wf_spec.get_task_spec_from_name(output))

        my_task._sync_children(outputs, Task.FUTURE)
        for child in my_task.children:
            child.task_spec._update(child)

def _on_complete_hook ( self , my_task ) : outputs = [ ] for condition , output in self . cond_task_specs : if self . choice is not None and output not in self . choice : continue if condition is None : outputs . append ( self . _wf_spec . get_task_spec_from_name ( output ) ) continue if not condition . _matches ( my_task ) : continue outputs . append ( self . _wf_spec . get_task_spec_from_name ( output ) ) my_task . _sync_children ( outputs , Task . FUTURE ) for child in my_task . children : child . task_spec . _update ( child )

Runs the task . Should not be called directly . Returns True if completed False otherwise .


def is_completed(self):
        """
        Returns True if the entire Workflow is completed, False otherwise.

        :rtype: bool
        :return: Whether the workflow is completed.
        """
        mask = Task.NOT_FINISHED_MASK
        iter = Task.Iterator(self.task_tree, mask)
        try:
            next(iter)
        except StopIteration:
            # No waiting tasks found.
            return True
        return False

def is_completed ( self ) : mask = Task . NOT_FINISHED_MASK iter = Task . Iterator ( self . task_tree , mask ) try : next ( iter ) except StopIteration : return True return False

Returns True if the entire Workflow is completed False otherwise .


def cancel(self, success=False):
        """
        Cancels all open tasks in the workflow.

        :type  success: bool
        :param success: Whether the Workflow should be marked as successfully
                        completed.
        """
        self.success = success
        cancel = []
        mask = Task.NOT_FINISHED_MASK
        for task in Task.Iterator(self.task_tree, mask):
            cancel.append(task)
        for task in cancel:
            task.cancel()

def cancel ( self , success = False ) : self . success = success cancel = [ ] mask = Task . NOT_FINISHED_MASK for task in Task . Iterator ( self . task_tree , mask ) : cancel . append ( task ) for task in cancel : task . cancel ( )

Cancels all open tasks in the workflow .


def get_task(self, id):
        """
        Returns the task with the given id.

        :type id:integer
        :param id: The id of a task.
        :rtype: Task
        :returns: The task with the given id.
        """
        tasks = [task for task in self.get_tasks() if task.id == id]
        return tasks[0] if len(tasks) == 1 else None

def get_task ( self , id ) : tasks = [ task for task in self . get_tasks ( ) if task . id == id ] return tasks [ 0 ] if len ( tasks ) == 1 else None


def get_tasks_from_spec_name(self, name):
        """
        Returns all tasks whose spec has the given name.

        :type name: str
        :param name: The name of a task spec.
        :rtype: Task
        :return: The task that relates to the spec with the given name.
        """
        return [task for task in self.get_tasks()
                if task.task_spec.name == name]

def get_tasks_from_spec_name ( self , name ) : return [ task for task in self . get_tasks ( ) if task . task_spec . name == name ]

Returns all tasks whose spec has the given name .


def get_tasks(self, state=Task.ANY_MASK):
        """
        Returns a list of Task objects with the given state.

        :type  state: integer
        :param state: A bitmask of states.
        :rtype:  list[Task]
        :returns: A list of tasks.
        """
        return [t for t in Task.Iterator(self.task_tree, state)]

def get_tasks ( self , state = Task . ANY_MASK ) : return [ t for t in Task . Iterator ( self . task_tree , state ) ]

Returns a list of Task objects with the given state .


def complete_task_from_id(self, task_id):
        """
        Runs the task with the given id.

        :type  task_id: integer
        :param task_id: The id of the Task object.
        """
        if task_id is None:
            raise WorkflowException(self.spec, 'task_id is None')
        for task in self.task_tree:
            if task.id == task_id:
                return task.complete()
        msg = 'A task with the given task_id (%s) was not found' % task_id
        raise WorkflowException(self.spec, msg)


def complete_next(self, pick_up=True, halt_on_manual=True):
        """
        Runs the next task.
        Returns True if completed, False otherwise.

        :type  pick_up: bool
        :param pick_up: When True, this method attempts to choose the next
                        task not by searching beginning at the root, but by
                        searching from the position at which the last call
                        of complete_next() left off.
        :type  halt_on_manual: bool
        :param halt_on_manual: When True, this method will not attempt to
                        complete any tasks that have manual=True.
                        See :meth:`SpiffWorkflow.specs.TaskSpec.__init__`
        :rtype:  bool
        :returns: True if all tasks were completed, False otherwise.
        """
        # Try to pick up where we left off.
        blacklist = []
        if pick_up and self.last_task is not None:
            try:
                iter = Task.Iterator(self.last_task, Task.READY)
                task = next(iter)
            except StopIteration:
                task = None
            self.last_task = None
            if task is not None:
                if not (halt_on_manual and task.task_spec.manual):
                    if task.complete():
                        self.last_task = task
                        return True
                blacklist.append(task)

        # Walk through all ready tasks.
        for task in Task.Iterator(self.task_tree, Task.READY):
            for blacklisted_task in blacklist:
                if task._is_descendant_of(blacklisted_task):
                    continue
            if not (halt_on_manual and task.task_spec.manual):
                if task.complete():
                    self.last_task = task
                    return True
            blacklist.append(task)

        # Walk through all waiting tasks.
        for task in Task.Iterator(self.task_tree, Task.WAITING):
            task.task_spec._update(task)
            if not task._has_state(Task.WAITING):
                self.last_task = task
                return True
        return False

def complete_next ( self , pick_up = True , halt_on_manual = True ) : blacklist = [ ] if pick_up and self . last_task is not None : try : iter = Task . Iterator ( self . last_task , Task . READY ) task = next ( iter ) except StopIteration : task = None self . last_task = None if task is not None : if not ( halt_on_manual and task . task_spec . manual ) : if task . complete ( ) : self . last_task = task return True blacklist . append ( task ) for task in Task . Iterator ( self . task_tree , Task . READY ) : for blacklisted_task in blacklist : if task . _is_descendant_of ( blacklisted_task ) : continue if not ( halt_on_manual and task . task_spec . manual ) : if task . complete ( ) : self . last_task = task return True blacklist . append ( task ) for task in Task . Iterator ( self . task_tree , Task . WAITING ) : task . task_spec . _update ( task ) if not task . _has_state ( Task . WAITING ) : self . last_task = task return True return False

Runs the next task . Returns True if completed False otherwise .


def ref(function, callback=None):
    """
    Returns a weak reference to the given method or function.
    If the callback argument is not None, it is called as soon
    as the referenced function is garbage deleted.

    :type  function: callable
    :param function: The function to reference.
    :type  callback: callable
    :param callback: Called when the function dies.
    """
    try:
        function.__func__
    except AttributeError:
        return _WeakMethodFree(function, callback)
    return _WeakMethodBound(function, callback)

def ref ( function , callback = None ) : try : function . __func__ except AttributeError : return _WeakMethodFree ( function , callback ) return _WeakMethodBound ( function , callback )

Returns a weak reference to the given method or function . If the callback argument is not None it is called as soon as the referenced function is garbage deleted .


def new_workflow(self, workflow_spec, read_only=False, **kwargs):
        """
        Create a new workflow instance from the given spec and arguments.

        :param workflow_spec: the workflow spec to use

        :param read_only: this should be in read only mode

        :param kwargs: Any extra kwargs passed to the deserialize_workflow
        method will be passed through here
        """
        return BpmnWorkflow(workflow_spec, read_only=read_only, **kwargs)

def new_workflow ( self , workflow_spec , read_only = False , ** kwargs ) : return BpmnWorkflow ( workflow_spec , read_only = read_only , ** kwargs )

Create a new workflow instance from the given spec and arguments .


def _add_child(self, task_spec, state=MAYBE):
        """
        Adds a new child and assigns the given TaskSpec to it.

        :type  task_spec: TaskSpec
        :param task_spec: The task spec that is assigned to the new child.
        :type  state: integer
        :param state: The bitmask of states for the new child.
        :rtype:  Task
        :returns: The new child task.
        """
        if task_spec is None:
            raise ValueError(self, '_add_child() requires a TaskSpec')
        if self._is_predicted() and state & self.PREDICTED_MASK == 0:
            msg = 'Attempt to add non-predicted child to predicted task'
            raise WorkflowException(self.task_spec, msg)
        task = Task(self.workflow, task_spec, self, state=state)
        task.thread_id = self.thread_id
        if state == self.READY:
            task._ready()
        return task

Adds a new child and assigns the given TaskSpec to it .


def _assign_new_thread_id(self, recursive=True):
        """
        Assigns a new thread id to the task.

        :type  recursive: bool
        :param recursive: Whether to assign the id to children recursively.
        :rtype:  bool
        :returns: The new thread id.
        """
        self.__class__.thread_id_pool += 1
        self.thread_id = self.__class__.thread_id_pool
        if not recursive:
            return self.thread_id
        for child in self:
            child.thread_id = self.thread_id
        return self.thread_id

def _assign_new_thread_id ( self , recursive = True ) : self . __class__ . thread_id_pool += 1 self . thread_id = self . __class__ . thread_id_pool if not recursive : return self . thread_id for child in self : child . thread_id = self . thread_id return self . thread_id


def _is_descendant_of(self, parent):
        """
        Returns True if parent is in the list of ancestors, returns False
        otherwise.

        :type  parent: Task
        :param parent: The parent that is searched in the ancestors.
        :rtype:  bool
        :returns: Whether the parent was found.
        """
        if self.parent is None:
            return False
        if self.parent == parent:
            return True
        return self.parent._is_descendant_of(parent)

def _is_descendant_of ( self , parent ) : if self . parent is None : return False if self . parent == parent : return True return self . parent . _is_descendant_of ( parent )

Returns True if parent is in the list of ancestors returns False otherwise .


def _find_child_of(self, parent_task_spec):
        """
        Returns the ancestor that has a task with the given task spec
        as a parent.
        If no such ancestor was found, the root task is returned.

        :type  parent_task_spec: TaskSpec
        :param parent_task_spec: The wanted ancestor.
        :rtype:  Task
        :returns: The child of the given ancestor.
        """
        if self.parent is None:
            return self
        if self.parent.task_spec == parent_task_spec:
            return self
        return self.parent._find_child_of(parent_task_spec)

def _find_child_of ( self , parent_task_spec ) : if self . parent is None : return self if self . parent . task_spec == parent_task_spec : return self return self . parent . _find_child_of ( parent_task_spec )

Returns the ancestor that has a task with the given task spec as a parent . If no such ancestor was found the root task is returned .


def _find_any(self, task_spec):
        """
        Returns any descendants that have the given task spec assigned.

        :type  task_spec: TaskSpec
        :param task_spec: The wanted task spec.
        :rtype:  list(Task)
        :returns: The tasks objects that are attached to the given task spec.
        """
        tasks = []
        if self.task_spec == task_spec:
            tasks.append(self)
        for child in self:
            if child.task_spec != task_spec:
                continue
            tasks.append(child)
        return tasks

def _find_any ( self , task_spec ) : tasks = [ ] if self . task_spec == task_spec : tasks . append ( self ) for child in self : if child . task_spec != task_spec : continue tasks . append ( child ) return tasks

Returns any descendants that have the given task spec assigned .


def _find_ancestor(self, task_spec):
        """
        Returns the ancestor that has the given task spec assigned.
        If no such ancestor was found, the root task is returned.

        :type  task_spec: TaskSpec
        :param task_spec: The wanted task spec.
        :rtype:  Task
        :returns: The ancestor.
        """
        if self.parent is None:
            return self
        if self.parent.task_spec == task_spec:
            return self.parent
        return self.parent._find_ancestor(task_spec)

def _find_ancestor ( self , task_spec ) : if self . parent is None : return self if self . parent . task_spec == task_spec : return self . parent return self . parent . _find_ancestor ( task_spec )

Returns the ancestor that has the given task spec assigned . If no such ancestor was found the root task is returned .


def _find_ancestor_from_name(self, name):
        """
        Returns the ancestor that has a task with the given name assigned.
        Returns None if no such ancestor was found.

        :type  name: str
        :param name: The name of the wanted task.
        :rtype:  Task
        :returns: The ancestor.
        """
        if self.parent is None:
            return None
        if self.parent.get_name() == name:
            return self.parent
        return self.parent._find_ancestor_from_name(name)

def _find_ancestor_from_name ( self , name ) : if self . parent is None : return None if self . parent . get_name ( ) == name : return self . parent return self . parent . _find_ancestor_from_name ( name )

Returns the ancestor that has a task with the given name assigned . Returns None if no such ancestor was found .


def _ready(self):
        """
        Marks the task as ready for execution.
        """
        if self._has_state(self.COMPLETED) or self._has_state(self.CANCELLED):
            return
        self._set_state(self.READY)
        self.task_spec._on_ready(self)

def _ready ( self ) : if self . _has_state ( self . COMPLETED ) or self . _has_state ( self . CANCELLED ) : return self . _set_state ( self . READY ) self . task_spec . _on_ready ( self )


def get_state_name(self):
        """
        Returns a textual representation of this Task's state.
        """
        state_name = []
        for state, name in list(self.state_names.items()):
            if self._has_state(state):
                state_name.append(name)
        return '|'.join(state_name)

def get_state_name ( self ) : state_name = [ ] for state , name in list ( self . state_names . items ( ) ) : if self . _has_state ( state ) : state_name . append ( name ) return '|' . join ( state_name )

Returns a textual representation of this Task s state .


def _inherit_data(self):
        """
        Inherits the data from the parent.
        """
        LOG.debug("'%s' inheriting data from '%s'" % (self.get_name(),
                                                      self.parent.get_name()),
                  extra=dict(data=self.parent.data))
        self.set_data(**self.parent.data)

def
_inherit_data
(
self
)
:
LOG
.
debug
(
"'%s' inheriting data from '%s'"
%
(
self
.
get_name
(
)
,
self
.
parent
.
get_name
(
)
)
,
extra
=
dict
(
data
=
self
.
parent
.
data
)
)
self
.
set_data
(
**
self
.
parent
.
data
)


def cancel(self):
        """
        Cancels the item if it was not yet completed, and removes
        any children that are LIKELY.
        """
        if self._is_finished():
            for child in self.children:
                child.cancel()
            return
        self._set_state(self.CANCELLED)
        self._drop_children()
        self.task_spec._on_cancel(self)

def cancel ( self ) : if self . _is_finished ( ) : for child in self . children : child . cancel ( ) return self . _set_state ( self . CANCELLED ) self . _drop_children ( ) self . task_spec . _on_cancel ( self )

Cancels the item if it was not yet completed and removes any children that are LIKELY .


def get_dump(self, indent=0, recursive=True):
        """
        Returns the subtree as a string for debugging.

        :rtype:  str
        :returns: The debug information.
        """
        dbg = (' ' * indent * 2)
        dbg += '%s/' % self.id
        dbg += '%s:' % self.thread_id
        dbg += ' Task of %s' % self.get_name()
        if self.task_spec.description:
            dbg += ' (%s)' % self.get_description()
        dbg += ' State: %s' % self.get_state_name()
        dbg += ' Children: %s' % len(self.children)
        if recursive:
            for child in self.children:
                dbg += '\n' + child.get_dump(indent + 1)
        return dbg

def
get_dump
(
self
,
indent
=
0
,
recursive
=
True
)
:
dbg
=
(
' '
*
indent
*
2
)
dbg
+=
'%s/'
%
self
.
id
dbg
+=
'%s:'
%
self
.
thread_id
dbg
+=
' Task of %s'
%
self
.
get_name
(
)
if
self
.
task_spec
.
description
:
dbg
+=
' (%s)'
%
self
.
get_description
(
)
dbg
+=
' State: %s'
%
self
.
get_state_name
(
)
dbg
+=
' Children: %s'
%
len
(
self
.
children
)
if
recursive
:
for
child
in
self
.
children
:
dbg
+=
'\n'
+
child
.
get_dump
(
indent
+
1
)
return
dbg

Returns the subtree as a string for debugging .


def _eval_args(args, my_task):
    """Parses args and evaluates any Attrib entries"""
    results = []
    for arg in args:
        if isinstance(arg, Attrib) or isinstance(arg, PathAttrib):
            results.append(valueof(my_task, arg))
        else:
            results.append(arg)
    return results

def _eval_args ( args , my_task ) : results = [ ] for arg in args : if isinstance ( arg , Attrib ) or isinstance ( arg , PathAttrib ) : results . append ( valueof ( my_task , arg ) ) else : results . append ( arg ) return results


def _eval_kwargs(kwargs, my_task):
    """Parses kwargs and evaluates any Attrib entries"""
    results = {}
    for kwarg, value in list(kwargs.items()):
        if isinstance(value, Attrib) or isinstance(value, PathAttrib):
            results[kwarg] = valueof(my_task, value)
        else:
            results[kwarg] = value
    return results

def _eval_kwargs ( kwargs , my_task ) : results = { } for kwarg , value in list ( kwargs . items ( ) ) : if isinstance ( value , Attrib ) or isinstance ( value , PathAttrib ) : results [ kwarg ] = valueof ( my_task , value ) else : results [ kwarg ] = value return results


def Serializable(o):
    """Make sure an object is JSON-serializable
    Use this to return errors and other info that does not need to be
    deserialized or does not contain important app data. Best for returning
    error info and such"""
    if isinstance(o, (str, dict, int)):
        return o
    else:
        try:
            json.dumps(o)
            return o
        except Exception:
            LOG.debug("Got a non-serilizeable object: %s" % o)
            return o.__repr__()

def
Serializable
(
o
)
:
if
isinstance
(
o
,
(
str
,
dict
,
int
)
)
:
return
o
else
:
try
:
json
.
dumps
(
o
)
return
o
except
Exception
:
LOG
.
debug
(
"Got a non-serilizeable object: %s"
%
o
)
return
o
.
__repr__
(
)

Make sure an object is JSON - serializable Use this to return errors and other info that does not need to be deserialized or does not contain important app data . Best for returning error info and such


def _send_call(self, my_task):
        """Sends Celery asynchronous call and stores async call information for
        retrieval laster"""
        args, kwargs = None, None
        if self.args:
            args = _eval_args(self.args, my_task)
        if self.kwargs:
            kwargs = _eval_kwargs(self.kwargs, my_task)
        LOG.debug(
            "%s (task id %s) calling %s" % (self.name, my_task.id, self.call),
            extra=dict(data=dict(args=args, kwargs=kwargs)))
        async_call = default_app.send_task(self.call, args=args, kwargs=kwargs)
        my_task._set_internal_data(task_id=async_call.task_id)
        my_task.async_call = async_call
        LOG.debug("'%s' called: %s" % (self.call, my_task.async_call.task_id))

def
_send_call
(
self
,
my_task
)
:
args
,
kwargs
=
,
if
self
.
args
:
args
=
_eval_args
(
self
.
args
,
my_task
)
if
self
.
kwargs
:
kwargs
=
_eval_kwargs
(
self
.
kwargs
,
my_task
)
LOG
.
debug
(
"%s (task id %s) calling %s"
%
(
self
.
name
,
my_task
.
id
,
self
.
call
)
,
extra
=
dict
(
data
=
dict
(
args
=
args
,
kwargs
=
kwargs
)
)
)
async_call
=
default_app
.
send_task
(
self
.
call
,
args
=
args
,
kwargs
=
kwargs
)
my_task
.
_set_internal_data
(
task_id
=
async_call
.
task_id
)
my_task
.
async_call
=
async_call
LOG
.
debug
(
"'%s' called: %s"
%
(
self
.
call
,
my_task
.
async_call
.
task_id
)
)


def _restart(self, my_task):
        """ Abort celery task and retry it"""
        if not my_task._has_state(Task.WAITING):
            raise WorkflowException(my_task, "Cannot refire a task that is not"
                                    "in WAITING state")
        # Check state of existing call and abort it (save history)
        if my_task._get_internal_data('task_id') is not None:
            if not hasattr(my_task, 'async_call'):
                task_id = my_task._get_internal_data('task_id')
                my_task.async_call = default_app.AsyncResult(task_id)
                my_task.deserialized = True
                my_task.async_call.state  # manually refresh
            async_call = my_task.async_call
            if async_call.state == 'FAILED':
                pass
            elif async_call.state in ['RETRY', 'PENDING', 'STARTED']:
                async_call.revoke()
                LOG.info("Celery task '%s' was in %s state and was revoked" % (
                    async_call.state, async_call))
            elif async_call.state == 'SUCCESS':
                LOG.warning("Celery task '%s' succeeded, but a refire was "
                            "requested" % async_call)
            self._clear_celery_task_data(my_task)
        # Retrigger
        return self._start(my_task)


def _clear_celery_task_data(self, my_task):
        """ Clear celery task data """
        # Save history
        if 'task_id' in my_task.internal_data:
            # Save history for diagnostics/forensics
            history = my_task._get_internal_data('task_history', [])
            history.append(my_task._get_internal_data('task_id'))
            del my_task.internal_data['task_id']
            my_task._set_internal_data(task_history=history)
        if 'task_state' in my_task.internal_data:
            del my_task.internal_data['task_state']
        if 'error' in my_task.internal_data:
            del my_task.internal_data['error']
        if hasattr(my_task, 'async_call'):
            delattr(my_task, 'async_call')
        if hasattr(my_task, 'deserialized'):
            delattr(my_task, 'deserialized')

def _clear_celery_task_data ( self , my_task ) : if 'task_id' in my_task . internal_data : history = my_task . _get_internal_data ( 'task_history' , [ ] ) history . append ( my_task . _get_internal_data ( 'task_id' ) ) del my_task . internal_data [ 'task_id' ] my_task . _set_internal_data ( task_history = history ) if 'task_state' in my_task . internal_data : del my_task . internal_data [ 'task_state' ] if 'error' in my_task . internal_data : del my_task . internal_data [ 'error' ] if hasattr ( my_task , 'async_call' ) : delattr ( my_task , 'async_call' ) if hasattr ( my_task , 'deserialized' ) : delattr ( my_task , 'deserialized' )


def ancestors(self):
        """Returns list of ancestor task specs based on inputs"""
        results = []

        def recursive_find_ancestors(task, stack):
            for input in task.inputs:
                if input not in stack:
                    stack.append(input)
                    recursive_find_ancestors(input, stack)
        recursive_find_ancestors(self, results)

        return results

def ancestors ( self ) : results = [ ] def recursive_find_ancestors ( task , stack ) : for input in task . inputs : if input not in stack : stack . append ( input ) recursive_find_ancestors ( input , stack ) recursive_find_ancestors ( self , results ) return results


def _predict(self, my_task, seen=None, looked_ahead=0):
        """
        Updates the branch such that all possible future routes are added.

        Should NOT be overwritten! Instead, overwrite _predict_hook().

        :type  my_task: Task
        :param my_task: The associated task in the task tree.
        :type  seen: list[taskspec]
        :param seen: A list of already visited tasks.
        :type  looked_ahead: integer
        :param looked_ahead: The depth of the predicted path so far.
        """
        if my_task._is_finished():
            return
        if seen is None:
            seen = []
        elif self in seen:
            return
        if not my_task._is_finished():
            self._predict_hook(my_task)
        if not my_task._is_definite():
            if looked_ahead + 1 >= self.lookahead:
                return
            seen.append(self)
        for child in my_task.children:
            child.task_spec._predict(child, seen[:], looked_ahead + 1)

def _predict ( self , my_task , seen = None , looked_ahead = 0 ) : if my_task . _is_finished ( ) : return if seen is None : seen = [ ] elif self in seen : return if not my_task . _is_finished ( ) : self . _predict_hook ( my_task ) if not my_task . _is_definite ( ) : if looked_ahead + 1 >= self . lookahead : return seen . append ( self ) for child in my_task . children : child . task_spec . _predict ( child , seen [ : ] , looked_ahead + 1 )

Updates the branch such that all possible future routes are added .


def _on_ready(self, my_task):
        """
        Return True on success, False otherwise.

        :type  my_task: Task
        :param my_task: The associated task in the task tree.
        """
        assert my_task is not None
        self.test()

        # Acquire locks, if any.
        for lock in self.locks:
            mutex = my_task.workflow._get_mutex(lock)
            if not mutex.testandset():
                return

        # Assign variables, if so requested.
        for assignment in self.pre_assign:
            assignment.assign(my_task, my_task)

        # Run task-specific code.
        self._on_ready_before_hook(my_task)
        self.reached_event.emit(my_task.workflow, my_task)
        self._on_ready_hook(my_task)

        # Run user code, if any.
        if self.ready_event.emit(my_task.workflow, my_task):
            # Assign variables, if so requested.
            for assignment in self.post_assign:
                assignment.assign(my_task, my_task)

        # Release locks, if any.
        for lock in self.locks:
            mutex = my_task.workflow._get_mutex(lock)
            mutex.unlock()

        self.finished_event.emit(my_task.workflow, my_task)

def _on_ready ( self , my_task ) : assert my_task is not None self . test ( ) for lock in self . locks : mutex = my_task . workflow . _get_mutex ( lock ) if not mutex . testandset ( ) : return for assignment in self . pre_assign : assignment . assign ( my_task , my_task ) self . _on_ready_before_hook ( my_task ) self . reached_event . emit ( my_task . workflow , my_task ) self . _on_ready_hook ( my_task ) if self . ready_event . emit ( my_task . workflow , my_task ) : for assignment in self . post_assign : assignment . assign ( my_task , my_task ) for lock in self . locks : mutex = my_task . workflow . _get_mutex ( lock ) mutex . unlock ( ) self . finished_event . emit ( my_task . workflow , my_task )

Return True on success False otherwise .


def create_package(self):
        """
        Creates the package, writing the data out to the provided file-like
        object.
        """

        # Check that all files exist (and calculate the longest shared path
        # prefix):
        self.input_path_prefix = None
        for filename in self.input_files:
            if not os.path.isfile(filename):
                raise ValueError(
                    '%s does not exist or is not a file' % filename)
            if self.input_path_prefix:
                full = os.path.abspath(os.path.dirname(filename))
                while not (full.startswith(self.input_path_prefix) and
                           self.input_path_prefix):
                    self.input_path_prefix = self.input_path_prefix[:-1]
            else:
                self.input_path_prefix = os.path.abspath(
                    os.path.dirname(filename))

        # Parse all of the XML:
        self.bpmn = {}
        for filename in self.input_files:
            bpmn = ET.parse(filename)
            self.bpmn[os.path.abspath(filename)] = bpmn

        # Now run through pre-parsing and validation:
        for filename, bpmn in list(self.bpmn.items()):
            bpmn = self.pre_parse_and_validate(bpmn, filename)
            self.bpmn[os.path.abspath(filename)] = bpmn

        # Now check that we can parse it fine:
        for filename, bpmn in list(self.bpmn.items()):
            self.parser.add_bpmn_xml(bpmn, filename=filename)

        self.wf_spec = self.parser.get_spec(self.entry_point_process)

        # Now package everything:
        self.package_zip = zipfile.ZipFile(
            self.package_file, "w", compression=zipfile.ZIP_DEFLATED)

        done_files = set()
        for spec in self.wf_spec.get_specs_depth_first():
            filename = spec.file
            if filename not in done_files:
                done_files.add(filename)

                bpmn = self.bpmn[os.path.abspath(filename)]
                self.write_to_package_zip(
                    "%s.bpmn" % spec.name, ET.tostring(bpmn.getroot()))

                self.write_file_to_package_zip(
                    "src/" + self._get_zip_path(filename), filename)

                self._call_editor_hook('package_for_editor', spec, filename)

        self.write_meta_data()
        self.write_manifest()

        self.package_zip.close()

def
create_package
(
self
)
:
self
.
input_path_prefix
=
for
filename
in
self
.
input_files
:
if
not
os
.
path
.
isfile
(
filename
)
:
raise
ValueError
(
'%s does not exist or is not a file'
%
filename
)
if
self
.
input_path_prefix
:
full
=
os
.
path
.
abspath
(
os
.
path
.
dirname
(
filename
)
)
while
not
(
full
.
startswith
(
self
.
input_path_prefix
)
and
self
.
input_path_prefix
)
:
self
.
input_path_prefix
=
self
.
input_path_prefix
[
:
1
]
else
:
self
.
input_path_prefix
=
os
.
path
.
abspath
(
os
.
path
.
dirname
(
filename
)
)
self
.
bpmn
=
{
}
for
filename
in
self
.
input_files
:
bpmn
=
ET
.
parse
(
filename
)
self
.
bpmn
[
os
.
path
.
abspath
(
filename
)
]
=
bpmn
for
filename
,
bpmn
in
list
(
self
.
bpmn
.
items
(
)
)
:
bpmn
=
self
.
pre_parse_and_validate
(
bpmn
,
filename
)
self
.
bpmn
[
os
.
path
.
abspath
(
filename
)
]
=
bpmn
for
filename
,
bpmn
in
list
(
self
.
bpmn
.
items
(
)
)
:
self
.
parser
.
add_bpmn_xml
(
bpmn
,
filename
=
filename
)
self
.
wf_spec
=
self
.
parser
.
get_spec
(
self
.
entry_point_process
)
self
.
package_zip
=
zipfile
.
ZipFile
(
self
.
package_file
,
"w"
,
compression
=
zipfile
.
ZIP_DEFLATED
)
done_files
=
set
(
)
for
spec
in
self
.
wf_spec
.
get_specs_depth_first
(
)
:
filename
=
spec
.
file
if
filename
not
in
done_files
:
done_files
.
add
(
filename
)
bpmn
=
self
.
bpmn
[
os
.
path
.
abspath
(
filename
)
]
self
.
write_to_package_zip
(
"%s.bpmn"
%
spec
.
name
,
ET
.
tostring
(
bpmn
.
getroot
(
)
)
)
self
.
write_file_to_package_zip
(
"src/"
+
self
.
_get_zip_path
(
filename
)
,
filename
)
self
.
_call_editor_hook
(
'package_for_editor'
,
spec
,
filename
)
self
.
write_meta_data
(
)
self
.
write_manifest
(
)
self
.
package_zip
.
close
(
)

Creates the package writing the data out to the provided file - like object .


def write_file_to_package_zip(self, filename, src_filename):
        """
        Writes a local file in to the zip file and adds it to the manifest
        dictionary

        :param filename: The zip file name

        :param src_filename: the local file name
        """
        f = open(src_filename)
        with f:
            data = f.read()
        self.manifest[filename] = md5hash(data)
        self.package_zip.write(src_filename, filename)

def write_file_to_package_zip ( self , filename , src_filename ) : f = open ( src_filename ) with f : data = f . read ( ) self . manifest [ filename ] = md5hash ( data ) self . package_zip . write ( src_filename , filename )


def write_to_package_zip(self, filename, data):
        """
        Writes data to the zip file and adds it to the manifest dictionary

        :param filename: The zip file name

        :param data: the data
        """
        self.manifest[filename] = md5hash(data)
        self.package_zip.writestr(filename, data)

def write_to_package_zip ( self , filename , data ) : self . manifest [ filename ] = md5hash ( data ) self . package_zip . writestr ( filename , data )


def write_manifest(self):
        """
        Write the manifest content to the zip file. It must be a predictable
        order.
        """
        config = configparser.ConfigParser()

        config.add_section('Manifest')

        for f in sorted(self.manifest.keys()):
            config.set('Manifest', f.replace(
                '\\', '/').lower(), self.manifest[f])

        ini = StringIO()
        config.write(ini)
        self.manifest_data = ini.getvalue()
        self.package_zip.writestr(self.MANIFEST_FILE, self.manifest_data)

def write_manifest ( self ) : config = configparser . ConfigParser ( ) config . add_section ( 'Manifest' ) for f in sorted ( self . manifest . keys ( ) ) : config . set ( 'Manifest' , f . replace ( '\\' , '/' ) . lower ( ) , self . manifest [ f ] ) ini = StringIO ( ) config . write ( ini ) self . manifest_data = ini . getvalue ( ) self . package_zip . writestr ( self . MANIFEST_FILE , self . manifest_data )

Write the manifest content to the zip file . It must be a predictable order .


def pre_parse_and_validate(self, bpmn, filename):
        """
        A subclass can override this method to provide additional parseing or
        validation. It should call the parent method first.

        :param bpmn: an lxml tree of the bpmn content

        :param filename: the source file name

        This must return the updated bpmn object (or a replacement)
        """
        bpmn = self._call_editor_hook(
            'pre_parse_and_validate', bpmn, filename) or bpmn

        return bpmn

def pre_parse_and_validate ( self , bpmn , filename ) : bpmn = self . _call_editor_hook ( 'pre_parse_and_validate' , bpmn , filename ) or bpmn return bpmn

A subclass can override this method to provide additional parseing or validation . It should call the parent method first .


def pre_parse_and_validate_signavio(self, bpmn, filename):
        """
        This is the Signavio specific editor hook for pre-parsing and
        validation.

        A subclass can override this method to provide additional parseing or
        validation. It should call the parent method first.

        :param bpmn: an lxml tree of the bpmn content

        :param filename: the source file name

        This must return the updated bpmn object (or a replacement)
        """
        self._check_for_disconnected_boundary_events_signavio(bpmn, filename)
        self._fix_call_activities_signavio(bpmn, filename)
        return bpmn

This is the Signavio specific editor hook for pre - parsing and validation .


def package_for_editor_signavio(self, spec, filename):
        """
        Adds the SVG files to the archive for this BPMN file.
        """
        signavio_file = filename[:-len('.bpmn20.xml')] + '.signavio.xml'
        if os.path.exists(signavio_file):
            self.write_file_to_package_zip(
                "src/" + self._get_zip_path(signavio_file), signavio_file)

            f = open(signavio_file, 'r')
            try:
                signavio_tree = ET.parse(f)
            finally:
                f.close()
            svg_node = one(signavio_tree.findall('.//svg-representation'))
            self.write_to_package_zip("%s.svg" % spec.name, svg_node.text)

def package_for_editor_signavio ( self , spec , filename ) : signavio_file = filename [ : - len ( '.bpmn20.xml' ) ] + '.signavio.xml' if os . path . exists ( signavio_file ) : self . write_file_to_package_zip ( "src/" + self . _get_zip_path ( signavio_file ) , signavio_file ) f = open ( signavio_file , 'r' ) try : signavio_tree = ET . parse ( f ) finally : f . close ( ) svg_node = one ( signavio_tree . findall ( './/svg-representation' ) ) self . write_to_package_zip ( "%s.svg" % spec . name , svg_node . text )

Adds the SVG files to the archive for this BPMN file .


def write_meta_data(self):
        """
        Writes the metadata.ini file to the archive.
        """
        config = configparser.ConfigParser()

        config.add_section('MetaData')
        config.set('MetaData', 'entry_point_process', self.wf_spec.name)
        if self.editor:
            config.set('MetaData', 'editor', self.editor)

        for k, v in self.meta_data:
            config.set('MetaData', k, v)

        if not self.PARSER_CLASS == BpmnParser:
            config.set('MetaData', 'parser_class_module',
                       inspect.getmodule(self.PARSER_CLASS).__name__)
            config.set('MetaData', 'parser_class', self.PARSER_CLASS.__name__)

        ini = StringIO()
        config.write(ini)
        self.write_to_package_zip(self.METADATA_FILE, ini.getvalue())

def write_meta_data ( self ) : config = configparser . ConfigParser ( ) config . add_section ( 'MetaData' ) config . set ( 'MetaData' , 'entry_point_process' , self . wf_spec . name ) if self . editor : config . set ( 'MetaData' , 'editor' , self . editor ) for k , v in self . meta_data : config . set ( 'MetaData' , k , v ) if not self . PARSER_CLASS == BpmnParser : config . set ( 'MetaData' , 'parser_class_module' , inspect . getmodule ( self . PARSER_CLASS ) . __name__ ) config . set ( 'MetaData' , 'parser_class' , self . PARSER_CLASS . __name__ ) ini = StringIO ( ) config . write ( ini ) self . write_to_package_zip ( self . METADATA_FILE , ini . getvalue ( ) )

Writes the metadata . ini file to the archive .


def merge_option_and_config_str(cls, option_name, config, options):
        """
        Utility method to merge an option and config, with the option taking "
        precedence
        """

        opt = getattr(options, option_name, None)
        if opt:
            config.set(CONFIG_SECTION_NAME, option_name, opt)
        elif config.has_option(CONFIG_SECTION_NAME, option_name):
            setattr(options, option_name, config.get(
                CONFIG_SECTION_NAME, option_name))

def merge_option_and_config_str ( cls , option_name , config , options ) : opt = getattr ( options , option_name , None ) if opt : config . set ( CONFIG_SECTION_NAME , option_name , opt ) elif config . has_option ( CONFIG_SECTION_NAME , option_name ) : setattr ( options , option_name , config . get ( CONFIG_SECTION_NAME , option_name ) )

Utility method to merge an option and config with the option taking precedence


def parse_node(self, node):
        """
        Parses the specified child task node, and returns the task spec. This
        can be called by a TaskParser instance, that is owned by this
        ProcessParser.
        """

        if node.get('id') in self.parsed_nodes:
            return self.parsed_nodes[node.get('id')]

        (node_parser, spec_class) = self.parser._get_parser_class(node.tag)
        if not node_parser or not spec_class:
            raise ValidationException(
                "There is no support implemented for this task type.",
                node=node, filename=self.filename)
        np = node_parser(self, spec_class, node)
        task_spec = np.parse_node()

        return task_spec

Parses the specified child task node and returns the task spec . This can be called by a TaskParser instance that is owned by this ProcessParser .


def deserialize_assign(self, workflow, start_node):
        """
        Reads the "pre-assign" or "post-assign" tag from the given node.

        start_node -- the xml node (xml.dom.minidom.Node)
        """
        name = start_node.getAttribute('name')
        attrib = start_node.getAttribute('field')
        value = start_node.getAttribute('value')
        kwargs = {}
        if name == '':
            _exc('name attribute required')
        if attrib != '' and value != '':
            _exc('Both, field and right-value attributes found')
        elif attrib == '' and value == '':
            _exc('field or value attribute required')
        elif value != '':
            kwargs['right'] = value
        else:
            kwargs['right_attribute'] = attrib
        return operators.Assign(name, **kwargs)

Reads the pre - assign or post - assign tag from the given node .


def deserialize_data(self, workflow, start_node):
        """
        Reads a "data" or "define" tag from the given node.

        start_node -- the xml node (xml.dom.minidom.Node)
        """
        name = start_node.getAttribute('name')
        value = start_node.getAttribute('value')
        return name, value

def deserialize_data ( self , workflow , start_node ) : name = start_node . getAttribute ( 'name' ) value = start_node . getAttribute ( 'value' ) return name , value

Reads a data or define tag from the given node .


def deserialize_assign_list(self, workflow, start_node):
        """
        Reads a list of assignments from the given node.

        workflow -- the workflow
        start_node -- the xml structure (xml.dom.minidom.Node)
        """
        # Collect all information.
        assignments = []
        for node in start_node.childNodes:
            if node.nodeType != minidom.Node.ELEMENT_NODE:
                continue
            if node.nodeName.lower() == 'assign':
                assignments.append(self.deserialize_assign(workflow, node))
            else:
                _exc('Unknown node: %s' % node.nodeName)
        return assignments

def
deserialize_assign_list
(
self
,
workflow
,
start_node
)
:
assignments
=
[
]
for
node
in
start_node
.
childNodes
:
if
node
.
nodeType
!=
minidom
.
Node
.
ELEMENT_NODE
:
continue
if
node
.
nodeName
.
lower
(
)
==
'assign'
:
assignments
.
append
(
self
.
deserialize_assign
(
workflow
,
node
)
)
else
:
_exc
(
'Unknown node: %s'
%
node
.
nodeName
)
return
assignments

Reads a list of assignments from the given node .


def deserialize_logical(self, node):
        """
        Reads the logical tag from the given node, returns a Condition object.

        node -- the xml node (xml.dom.minidom.Node)
        """
        term1_attrib = node.getAttribute('left-field')
        term1_value = node.getAttribute('left-value')
        op = node.nodeName.lower()
        term2_attrib = node.getAttribute('right-field')
        term2_value = node.getAttribute('right-value')
        if op not in _op_map:
            _exc('Invalid operator')
        if term1_attrib != '' and term1_value != '':
            _exc('Both, left-field and left-value attributes found')
        elif term1_attrib == '' and term1_value == '':
            _exc('left-field or left-value attribute required')
        elif term1_value != '':
            left = term1_value
        else:
            left = operators.Attrib(term1_attrib)
        if term2_attrib != '' and term2_value != '':
            _exc('Both, right-field and right-value attributes found')
        elif term2_attrib == '' and term2_value == '':
            _exc('right-field or right-value attribute required')
        elif term2_value != '':
            right = term2_value
        else:
            right = operators.Attrib(term2_attrib)
        return _op_map[op](left, right)

Reads the logical tag from the given node returns a Condition object .


def deserialize_condition(self, workflow, start_node):
        """
        Reads the conditional statement from the given node.

        workflow -- the workflow with which the concurrence is associated
        start_node -- the xml structure (xml.dom.minidom.Node)
        """
        # Collect all information.
        condition = None
        spec_name = None
        for node in start_node.childNodes:
            if node.nodeType != minidom.Node.ELEMENT_NODE:
                continue
            if node.nodeName.lower() == 'successor':
                if spec_name is not None:
                    _exc('Duplicate task name %s' % spec_name)
                if node.firstChild is None:
                    _exc('Successor tag without a task name')
                spec_name = node.firstChild.nodeValue
            elif node.nodeName.lower() in _op_map:
                if condition is not None:
                    _exc('Multiple conditions are not yet supported')
                condition = self.deserialize_logical(node)
            else:
                _exc('Unknown node: %s' % node.nodeName)

        if condition is None:
            _exc('Missing condition in conditional statement')
        if spec_name is None:
            _exc('A %s has no task specified' % start_node.nodeName)
        return condition, spec_name

Reads the conditional statement from the given node .


def deserialize_workflow_spec(self, s_state, filename=None):
        """
        Reads the workflow from the given XML structure and returns a
        WorkflowSpec instance.
        """
        dom = minidom.parseString(s_state)
        node = dom.getElementsByTagName('process-definition')[0]
        name = node.getAttribute('name')
        if name == '':
            _exc('%s without a name attribute' % node.nodeName)

        # Read all task specs and create a list of successors.
        workflow_spec = specs.WorkflowSpec(name, filename)
        del workflow_spec.task_specs['Start']
        end = specs.Simple(workflow_spec, 'End'), []
        read_specs = dict(end=end)
        for child_node in node.childNodes:
            if child_node.nodeType != minidom.Node.ELEMENT_NODE:
                continue
            if child_node.nodeName == 'name':
                workflow_spec.name = child_node.firstChild.nodeValue
            elif child_node.nodeName == 'description':
                workflow_spec.description = child_node.firstChild.nodeValue
            elif child_node.nodeName.lower() in _spec_map:
                self.deserialize_task_spec(
                    workflow_spec, child_node, read_specs)
            else:
                _exc('Unknown node: %s' % child_node.nodeName)

        # Remove the default start-task from the workflow.
        workflow_spec.start = read_specs['start'][0]

        # Connect all task specs.
        for name in read_specs:
            spec, successors = read_specs[name]
            for condition, successor_name in successors:
                if successor_name not in read_specs:
                    _exc('Unknown successor: "%s"' % successor_name)
                successor, foo = read_specs[successor_name]
                if condition is None:
                    spec.connect(successor)
                else:
                    spec.connect_if(condition, successor)
        return workflow_spec

def
deserialize_workflow_spec
(
self
,
s_state
,
filename
=
)
:
dom
=
minidom
.
parseString
(
s_state
)
node
=
dom
.
getElementsByTagName
(
'process-definition'
)
[
0
]
name
=
node
.
getAttribute
(
'name'
)
if
name
==
''
:
_exc
(
'%s without a name attribute'
%
node
.
nodeName
)
workflow_spec
=
specs
.
WorkflowSpec
(
name
,
filename
)
del
workflow_spec
.
task_specs
[
'Start'
]
end
=
specs
.
Simple
(
workflow_spec
,
'End'
)
,
[
]
read_specs
=
dict
(
end
=
end
)
for
child_node
in
node
.
childNodes
:
if
child_node
.
nodeType
!=
minidom
.
Node
.
ELEMENT_NODE
:
continue
if
child_node
.
nodeName
==
'name'
:
workflow_spec
.
name
=
child_node
.
firstChild
.
nodeValue
elif
child_node
.
nodeName
==
'description'
:
workflow_spec
.
description
=
child_node
.
firstChild
.
nodeValue
elif
child_node
.
nodeName
.
lower
(
)
in
_spec_map
:
self
.
deserialize_task_spec
(
workflow_spec
,
child_node
,
read_specs
)
else
:
_exc
(
'Unknown node: %s'
%
child_node
.
nodeName
)
workflow_spec
.
start
=
read_specs
[
'start'
]
[
0
]
for
name
in
read_specs
:
spec
,
successors
=
read_specs
[
name
]
for
condition
,
successor_name
in
successors
:
if
successor_name
not
in
read_specs
:
_exc
(
'Unknown successor: "%s"'
%
successor_name
)
successor
,
foo
=
read_specs
[
successor_name
]
if
condition
is
:
spec
.
connect
(
successor
)
else
:
spec
.
connect_if
(
condition
,
successor
)
return
workflow_spec

Reads the workflow from the given XML structure and returns a WorkflowSpec instance .


def _add_notify(self, task_spec):
        """
        Called by a task spec when it was added into the workflow.
        """
        if task_spec.name in self.task_specs:
            raise KeyError('Duplicate task spec name: ' + task_spec.name)
        self.task_specs[task_spec.name] = task_spec
        task_spec.id = len(self.task_specs)

def
_add_notify
(
self
,
task_spec
)
:
if
task_spec
.
name
in
self
.
task_specs
:
raise
KeyError
(
'Duplicate task spec name: '
+
task_spec
.
name
)
self
.
task_specs
[
task_spec
.
name
]
=
task_spec
task_spec
.
id
=
len
(
self
.
task_specs
)

Called by a task spec when it was added into the workflow .


def validate(self):
        """Checks integrity of workflow and reports any problems with it.

        Detects:
        - loops (tasks that wait on each other in a loop)
        :returns: empty list if valid, a list of errors if not
        """
        results = []
        from ..specs import Join

        def recursive_find_loop(task, history):
            current = history[:]
            current.append(task)
            if isinstance(task, Join):
                if task in history:
                    msg = "Found loop with '%s': %s then '%s' again" % (
                        task.name, '->'.join([p.name for p in history]),
                        task.name)
                    raise Exception(msg)
                for predecessor in task.inputs:
                    recursive_find_loop(predecessor, current)

            for parent in task.inputs:
                recursive_find_loop(parent, current)

        for task_id, task in list(self.task_specs.items()):
            # Check for cyclic waits
            try:
                recursive_find_loop(task, [])
            except Exception as exc:
                results.append(exc.__str__())

            # Check for disconnected tasks
            if not task.inputs and task.name not in ['Start', 'Root']:
                if task.outputs:
                    results.append("Task '%s' is disconnected (no inputs)" %
                                   task.name)
                else:
                    LOG.debug("Task '%s' is not being used" % task.name)

        return results

Checks integrity of workflow and reports any problems with it .


def accept_message(self, message):
        """
        Indicate to the workflow that a message has been received. The message
        will be processed by any waiting Intermediate or Boundary Message
        Events, that are waiting for the message.
        """
        assert not self.read_only
        self.refresh_waiting_tasks()
        self.do_engine_steps()
        for my_task in Task.Iterator(self.task_tree, Task.WAITING):
            my_task.task_spec.accept_message(my_task, message)

def accept_message ( self , message ) : assert not self . read_only self . refresh_waiting_tasks ( ) self . do_engine_steps ( ) for my_task in Task . Iterator ( self . task_tree , Task . WAITING ) : my_task . task_spec . accept_message ( my_task , message )

Indicate to the workflow that a message has been received . The message will be processed by any waiting Intermediate or Boundary Message Events that are waiting for the message .


def refresh_waiting_tasks(self):
        """
        Refresh the state of all WAITING tasks. This will, for example, update
        Catching Timer Events whose waiting time has passed.
        """
        assert not self.read_only
        for my_task in self.get_tasks(Task.WAITING):
            my_task.task_spec._update(my_task)

def refresh_waiting_tasks ( self ) : assert not self . read_only for my_task in self . get_tasks ( Task . WAITING ) : my_task . task_spec . _update ( my_task )

Refresh the state of all WAITING tasks . This will for example update Catching Timer Events whose waiting time has passed .


def get_ready_user_tasks(self):
        """
        Returns a list of User Tasks that are READY for user action
        """
        return [t for t in self.get_tasks(Task.READY)
                if not self._is_engine_task(t.task_spec)]

def get_ready_user_tasks ( self ) : return [ t for t in self . get_tasks ( Task . READY ) if not self . _is_engine_task ( t . task_spec ) ]


def deserialize(cls, serializer, wf_spec, s_state, **kwargs):
        """
        Deserializes the trigger using the provided serializer.
        """
        return serializer.deserialize_trigger(wf_spec,
                                              s_state,
                                              **kwargs)

def deserialize ( cls , serializer , wf_spec , s_state , ** kwargs ) : return serializer . deserialize_trigger ( wf_spec , s_state , ** kwargs )

Deserializes the trigger using the provided serializer .


def evaluate(self, task, expression):
        """
        Evaluate the given expression, within the context of the given task and
        return the result.
        """
        if isinstance(expression, Operator):
            return expression._matches(task)
        else:
            return self._eval(task, expression, **task.data)

def evaluate ( self , task , expression ) : if isinstance ( expression , Operator ) : return expression . _matches ( task ) else : return self . _eval ( task , expression , ** task . data )

Evaluate the given expression within the context of the given task and return the result .


def execute(self, task, script, **kwargs):
        """
        Execute the script, within the context of the specified task
        """
        locals().update(kwargs)
        exec(script)

def execute ( self , task , script , ** kwargs ) : locals ( ) . update ( kwargs ) exec ( script )

Execute the script within the context of the specified task


def _start(self, my_task, force=False):
        """
        Checks whether the preconditions for going to READY state are met.
        Returns True if the threshold was reached, False otherwise.
        Also returns the list of tasks that yet need to be completed.
        """
        # If the threshold was already reached, there is nothing else to do.
        if my_task._has_state(Task.COMPLETED):
            return True, None
        if my_task._has_state(Task.READY):
            return True, None

        # Check whether we may fire.
        if self.split_task is None:
            return self._check_threshold_unstructured(my_task, force)
        return self._check_threshold_structured(my_task, force)

def _start ( self , my_task , force = False ) : if my_task . _has_state ( Task . COMPLETED ) : return True , None if my_task . _has_state ( Task . READY ) : return True , None if self . split_task is None : return self . _check_threshold_unstructured ( my_task , force ) return self . _check_threshold_structured ( my_task , force )

Checks whether the preconditions for going to READY state are met . Returns True if the threshold was reached False otherwise . Also returns the list of tasks that yet need to be completed .


def _on_trigger(self, my_task):
        """
        May be called to fire the Join before the incoming branches are
        completed.
        """
        for task in my_task.workflow.task_tree._find_any(self):
            if task.thread_id != my_task.thread_id:
                continue
            self._do_join(task)

def _on_trigger ( self , my_task ) : for task in my_task . workflow . task_tree . _find_any ( self ) : if task . thread_id != my_task . thread_id : continue self . _do_join ( task )

May be called to fire the Join before the incoming branches are completed .


def connect(self, task_spec):
        """
        Connects the task spec that is executed if no other condition
        matches.

        :type  task_spec: TaskSpec
        :param task_spec: The following task spec.
        """
        assert self.default_task_spec is None
        self.outputs.append(task_spec)
        self.default_task_spec = task_spec.name
        task_spec._connect_notify(self)

def connect ( self , task_spec ) : assert self . default_task_spec is None self . outputs . append ( task_spec ) self . default_task_spec = task_spec . name task_spec . _connect_notify ( self )

Connects the task spec that is executed if no other condition matches .


def container_id(self, name):
        '''Try to find the container ID with the specified name'''
        container = self._containers.get(name, None)
        if not container is None:
            return container.get('id', None)
        return None

def container_id ( self , name ) : container = self . _containers . get ( name , None ) if not container is None : return container . get ( 'id' , None ) return None


def initialize(self, containers):
        '''
        Initialize a new state file with the given contents.
        This function fails in case the state file already exists.
        '''
        self._containers = deepcopy(containers)
        self.__write(containers, initialize=True)

def initialize ( self , containers ) : self . _containers = deepcopy ( containers ) self . __write ( containers , initialize = True )

Initialize a new state file with the given contents . This function fails in case the state file already exists .


def update(self, containers):
        '''Update the current state file with the specified contents'''
        self._containers = deepcopy(containers)
        self.__write(containers, initialize=False)

def update ( self , containers ) : self . _containers = deepcopy ( containers ) self . __write ( containers , initialize = False )


def load(self):
        '''Try to load a blockade state file in the current directory'''
        try:
            with open(self._state_file) as f:
                state = yaml.safe_load(f)
                self._containers = state['containers']
        except (IOError, OSError) as err:
            if err.errno == errno.ENOENT:
                raise NotInitializedError("No blockade exists in this context")
            raise InconsistentStateError("Failed to load Blockade state: "
                                         + str(err))
        except Exception as err:
            raise InconsistentStateError("Failed to load Blockade state: "
                                         + str(err))

def
load
(
self
)
:
try
:
with
open
(
self
.
_state_file
)
as
f
:
state
=
yaml
.
safe_load
(
f
)
self
.
_containers
=
state
[
'containers'
]
except
(
IOError
,
OSError
)
as
err
:
if
err
.
errno
==
errno
.
ENOENT
:
raise
NotInitializedError
(
"No blockade exists in this context"
)
raise
InconsistentStateError
(
"Failed to load Blockade state: "
+
str
(
err
)
)
except
Exception
as
err
:
raise
InconsistentStateError
(
"Failed to load Blockade state: "
+
str
(
err
)
)


def _get_blockade_id_from_cwd(self, cwd=None):
        '''Generate a new blockade ID based on the CWD'''
        if not cwd:
            cwd = os.getcwd()
        # this follows a similar pattern as docker-compose uses
        parent_dir = os.path.abspath(cwd)
        basename = os.path.basename(parent_dir).lower()
        blockade_id = re.sub(r"[^a-z0-9]", "", basename)
        if not blockade_id:  # if we can't get a valid name from CWD, use "default"
            blockade_id = "default"
        return blockade_id

def _get_blockade_id_from_cwd ( self , cwd = None ) : if not cwd : cwd = os . getcwd ( ) parent_dir = os . path . abspath ( cwd ) basename = os . path . basename ( parent_dir ) . lower ( ) blockade_id = re . sub ( r"[^a-z0-9]" , "" , basename ) if not blockade_id : blockade_id = "default" return blockade_id


def _assure_dir(self):
        '''Make sure the state directory exists'''
        try:
            os.makedirs(self._state_dir)
        except OSError as err:
            if err.errno != errno.EEXIST:
                raise

def _assure_dir ( self ) : try : os . makedirs ( self . _state_dir ) except OSError as err : if err . errno != errno . EEXIST : raise


def _state_delete(self):
        '''Try to delete the state.yml file and the folder .blockade'''
        try:
            os.remove(self._state_file)
        except OSError as err:
            if err.errno not in (errno.EPERM, errno.ENOENT):
                raise

        try:
            os.rmdir(self._state_dir)
        except OSError as err:
            if err.errno not in (errno.ENOTEMPTY, errno.ENOENT):
                raise

def _state_delete ( self ) : try : os . remove ( self . _state_file ) except OSError as err : if err . errno not in ( errno . EPERM , errno . ENOENT ) : raise try : os . rmdir ( self . _state_dir ) except OSError as err : if err . errno not in ( errno . ENOTEMPTY , errno . ENOENT ) : raise

Try to delete the state . yml file and the folder . blockade


def __base_state(self, containers):
        '''
        Convert blockade ID and container information into
        a state dictionary object.
        '''
        return dict(blockade_id=self._blockade_id,
                    containers=containers,
                    version=self._state_version)

def __base_state ( self , containers ) : return dict ( blockade_id = self . _blockade_id , containers = containers , version = self . _state_version )

Convert blockade ID and container information into a state dictionary object .


def __write(self, containers, initialize=True):
        '''Write the given state information into a file'''
        path = self._state_file
        self._assure_dir()
        try:
            flags = os.O_WRONLY | os.O_CREAT
            if initialize:
                flags |= os.O_EXCL
            with os.fdopen(os.open(path, flags), "w") as f:
                yaml.safe_dump(self.__base_state(containers), f)
        except OSError as err:
            if err.errno == errno.EEXIST:
                raise AlreadyInitializedError(
                    "Path %s exists. "
                    "You may need to destroy a previous blockade." % path)
            raise
        except Exception:
            # clean up our created file
            self._state_delete()
            raise


def expand_partitions(containers, partitions):
    '''
    Validate the partitions of containers. If there are any containers
    not in any partition, place them in an new partition.
    '''

    # filter out holy containers that don't belong
    # to any partition at all
    all_names = frozenset(c.name for c in containers if not c.holy)
    holy_names = frozenset(c.name for c in containers if c.holy)
    neutral_names = frozenset(c.name for c in containers if c.neutral)
    partitions = [frozenset(p) for p in partitions]

    unknown = set()
    holy = set()
    union = set()

    for partition in partitions:
        unknown.update(partition - all_names - holy_names)
        holy.update(partition - all_names)
        union.update(partition)

    if unknown:
        raise BlockadeError('Partitions contain unknown containers: %s' %
                            list(unknown))

    if holy:
        raise BlockadeError('Partitions contain holy containers: %s' %
                            list(holy))

    # put any leftover containers in an implicit partition
    leftover = all_names.difference(union)
    if leftover:
        partitions.append(leftover)

    # we create an 'implicit' partition for the neutral containers
    # in case they are not part of the leftover anyways
    if not neutral_names.issubset(leftover):
        partitions.append(neutral_names)

    return partitions

Validate the partitions of containers . If there are any containers not in any partition place them in an new partition .


def get_source_chains(self, blockade_id):
        """Get a map of blockade chains IDs -> list of IPs targeted at them

        For figuring out which container is in which partition
        """
        result = {}
        if not blockade_id:
            raise ValueError("invalid blockade_id")
        lines = self.get_chain_rules("FORWARD")

        for line in lines:
            parts = line.split()
            if len(parts) < 4:
                continue
            try:
                partition_index = parse_partition_index(blockade_id, parts[0])
            except ValueError:
                continue  # not a rule targetting a blockade chain

            source = parts[3]
            if source:
                result[source] = partition_index
        return result

def
get_source_chains
(
self
,
blockade_id
)
:
result
=
{
}
if
not
blockade_id
:
raise
ValueError
(
"invalid blockade_id"
)
lines
=
self
.
get_chain_rules
(
"FORWARD"
)
for
line
in
lines
:
parts
=
line
.
split
(
)
if
len
(
parts
)
<
4
:
continue
try
:
partition_index
=
parse_partition_index
(
blockade_id
,
parts
[
0
]
)
except
ValueError
:
continue
source
=
parts
[
3
]
if
source
:
result
[
source
]
=
partition_index
return
result

Get a map of blockade chains IDs - > list of IPs targeted at them


def insert_rule(self, chain, src=None, dest=None, target=None):
        """Insert a new rule in the chain
        """
        if not chain:
            raise ValueError("Invalid chain")
        if not target:
            raise ValueError("Invalid target")
        if not (src or dest):
            raise ValueError("Need src, dest, or both")

        args = ["-I", chain]
        if src:
            args += ["-s", src]
        if dest:
            args += ["-d", dest]
        args += ["-j", target]
        self.call(*args)

def
insert_rule
(
self
,
chain
,
src
=
,
dest
=
,
target
=
)
:
if
not
chain
:
raise
ValueError
(
"Invalid chain"
)
if
not
target
:
raise
ValueError
(
"Invalid target"
)
if
not
(
src
or
dest
)
:
raise
ValueError
(
"Need src, dest, or both"
)
args
=
[
"-I"
,
chain
]
if
src
:
args
+=
[
"-s"
,
src
]
if
dest
:
args
+=
[
"-d"
,
dest
]
args
+=
[
"-j"
,
target
]
self
.
call
(
*
args
)


def _sm_start(self, *args, **kwargs):
        """
        Start the timer waiting for pain
        """
        millisec = random.randint(self._start_min_delay, self._start_max_delay)
        self._timer = threading.Timer(millisec / 1000.0, self.event_timeout)
        self._timer.start()

def _sm_start ( self , * args , ** kwargs ) : millisec = random . randint ( self . _start_min_delay , self . _start_max_delay ) self . _timer = threading . Timer ( millisec / 1000.0 , self . event_timeout ) self . _timer . start ( )


def _sm_to_pain(self, *args, **kwargs):
        """
        Start the blockade event
        """
        _logger.info("Starting chaos for blockade %s" % self._blockade_name)
        self._do_blockade_event()
        # start the timer to end the pain
        millisec = random.randint(self._run_min_time, self._run_max_time)
        self._timer = threading.Timer(millisec / 1000.0, self.event_timeout)
        self._timer.start()

def
_sm_to_pain
(
self
,
*
args
,
**
kwargs
)
:
_logger
.
info
(
"Starting chaos for blockade %s"
%
self
.
_blockade_name
)
self
.
_do_blockade_event
(
)
millisec
=
random
.
randint
(
self
.
_run_min_time
,
self
.
_run_max_time
)
self
.
_timer
=
threading
.
Timer
(
millisec
/
1000.0
,
self
.
event_timeout
)
self
.
_timer
.
start
(
)


def _sm_stop_from_no_pain(self, *args, **kwargs):
        """
        Stop chaos when there is no current blockade operation
        """
        # Just stop the timer.  It is possible that it was too late and the
        # timer is about to run
        _logger.info("Stopping chaos for blockade %s" % self._blockade_name)
        self._timer.cancel()

def
_sm_stop_from_no_pain
(
self
,
*
args
,
**
kwargs
)
:
_logger
.
info
(
"Stopping chaos for blockade %s"
%
self
.
_blockade_name
)
self
.
_timer
.
cancel
(
)


def _sm_relieve_pain(self, *args, **kwargs):
        """
        End the blockade event and return to a steady state
        """
        _logger.info(
                "Ending the degradation for blockade %s" % self._blockade_name)
        self._do_reset_all()
        # set a timer for the next pain event
        millisec = random.randint(self._start_min_delay, self._start_max_delay)
        self._timer = threading.Timer(millisec/1000.0, self.event_timeout)
        self._timer.start()


def _sm_stop_from_pain(self, *args, **kwargs):
        """
        Stop chaos while there is a blockade event in progress
        """
        _logger.info("Stopping chaos for blockade %s" % self._blockade_name)
        self._do_reset_all()

def
_sm_stop_from_pain
(
self
,
*
args
,
**
kwargs
)
:
_logger
.
info
(
"Stopping chaos for blockade %s"
%
self
.
_blockade_name
)
self
.
_do_reset_all
(
)


def _sm_cleanup(self, *args, **kwargs):
        """
        Delete all state associated with the chaos session
        """
        if self._done_notification_func is not None:
            self._done_notification_func()
        self._timer.cancel()

def _sm_cleanup ( self , * args , ** kwargs ) : if self . _done_notification_func is not None : self . _done_notification_func ( ) self . _timer . cancel ( )


def dependency_sorted(containers):
    """Sort a dictionary or list of containers into dependency order

    Returns a sequence
    """
    if not isinstance(containers, collections.Mapping):
        containers = dict((c.name, c) for c in containers)

    container_links = dict((name, set(c.links.keys()))
                           for name, c in containers.items())
    sorted_names = _resolve(container_links)
    return [containers[name] for name in sorted_names]

def dependency_sorted ( containers ) : if not isinstance ( containers , collections . Mapping ) : containers = dict ( ( c . name , c ) for c in containers ) container_links = dict ( ( name , set ( c . links . keys ( ) ) ) for name , c in containers . items ( ) ) sorted_names = _resolve ( container_links ) return [ containers [ name ] for name in sorted_names ]


def from_dict(name, values):
        '''
        Convert a dictionary of configuration values
        into a sequence of BlockadeContainerConfig instances
        '''

        # determine the number of instances of this container
        count = 1
        count_value = values.get('count', 1)
        if isinstance(count_value, int):
            count = max(count_value, 1)

        def with_index(name, idx):
            if name and idx:
                return '%s_%d' % (name, idx)
            return name

        def get_instance(n, idx=None):
            return BlockadeContainerConfig(
                with_index(n, idx),
                values['image'],
                command=values.get('command'),
                links=values.get('links'),
                volumes=values.get('volumes'),
                publish_ports=values.get('ports'),
                expose_ports=values.get('expose'),
                environment=values.get('environment'),
                hostname=values.get('hostname'),
                dns=values.get('dns'),
                start_delay=values.get('start_delay', 0),
                neutral=values.get('neutral', False),
                holy=values.get('holy', False),
                container_name=with_index(values.get('container_name'), idx),
                cap_add=values.get('cap_add'))

        if count == 1:
            yield get_instance(name)
        else:
            for idx in range(1, count+1):
                # TODO: configurable name/index format
                yield get_instance(name, idx)

def from_dict ( name , values ) : count = 1 count_value = values . get ( 'count' , 1 ) if isinstance ( count_value , int ) : count = max ( count_value , 1 ) def with_index ( name , idx ) : if name and idx : return '%s_%d' % ( name , idx ) return name def get_instance ( n , idx = None ) : return BlockadeContainerConfig ( with_index ( n , idx ) , values [ 'image' ] , command = values . get ( 'command' ) , links = values . get ( 'links' ) , volumes = values . get ( 'volumes' ) , publish_ports = values . get ( 'ports' ) , expose_ports = values . get ( 'expose' ) , environment = values . get ( 'environment' ) , hostname = values . get ( 'hostname' ) , dns = values . get ( 'dns' ) , start_delay = values . get ( 'start_delay' , 0 ) , neutral = values . get ( 'neutral' , False ) , holy = values . get ( 'holy' , False ) , container_name = with_index ( values . get ( 'container_name' ) , idx ) , cap_add = values . get ( 'cap_add' ) ) if count == 1 : yield get_instance ( name ) else : for idx in range ( 1 , count + 1 ) : yield get_instance ( name , idx )


def from_dict(values):
        '''
        Instantiate a BlockadeConfig instance based on
        a given dictionary of configuration values
        '''
        try:
            containers = values['containers']
            parsed_containers = {}
            for name, container_dict in containers.items():
                try:
                    # one config entry might result in many container
                    # instances (indicated by the 'count' config value)
                    for cnt in BlockadeContainerConfig.from_dict(name, container_dict):
                        # check for duplicate 'container_name' definitions
                        if cnt.container_name:
                            cname = cnt.container_name
                            existing = [c for c in parsed_containers.values() if c.container_name == cname]
                            if existing:
                                raise BlockadeConfigError("Duplicate 'container_name' definition: %s" % (cname))
                        parsed_containers[cnt.name] = cnt
                except Exception as err:
                    raise BlockadeConfigError(
                        "Container '%s' config problem: %s" % (name, err))

            network = values.get('network')
            if network:
                defaults = _DEFAULT_NETWORK_CONFIG.copy()
                defaults.update(network)
                network = defaults
            else:
                network = _DEFAULT_NETWORK_CONFIG.copy()

            return BlockadeConfig(parsed_containers, network=network)

        except KeyError as err:
            raise BlockadeConfigError("Config missing value: " + str(err))

        except Exception as err:
            # TODO log this to some debug stream?
            raise BlockadeConfigError("Failed to load config: " + str(err))


def cmd_up(opts):
    """Start the containers and link them together
    """
    config = load_config(opts.config)
    b = get_blockade(config, opts)
    containers = b.create(verbose=opts.verbose, force=opts.force)
    print_containers(containers, opts.json)

def cmd_up ( opts ) : config = load_config ( opts . config ) b = get_blockade ( config , opts ) containers = b . create ( verbose = opts . verbose , force = opts . force ) print_containers ( containers , opts . json )