Skip to content

Commit

Permalink
Reorganize model module (no external change)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandreDecan committed Jan 20, 2016
1 parent 2b832b0 commit 4c50811
Show file tree
Hide file tree
Showing 12 changed files with 403 additions and 388 deletions.
2 changes: 2 additions & 0 deletions docs/api/code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ Module *code*
:members:
:member-order: bysource
:show-inheritance:
:inherited-members:


1 change: 1 addition & 0 deletions docs/api/exceptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Module *exceptions*
:members:
:member-order: bysource
:show-inheritance:
:inherited-members:
2 changes: 2 additions & 0 deletions docs/api/interpreter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ Module *interpreter*
:members:
:member-order: bysource
:show-inheritance:
:inherited-members:


1 change: 1 addition & 0 deletions docs/api/io.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ Module *io*
:members:
:member-order: bysource
:show-inheritance:
:inherited-members:

2 changes: 2 additions & 0 deletions docs/api/model.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ Module *model*
:members:
:member-order: bysource
:show-inheritance:
:inherited-members:


2 changes: 2 additions & 0 deletions docs/api/stories.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ Module *stories*
:members:
:member-order: bysource
:show-inheritance:
:inherited-members:


2 changes: 2 additions & 0 deletions docs/api/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ Module *testing*
:members:
:member-order: bysource
:show-inheritance:
:inherited-members:


6 changes: 6 additions & 0 deletions sismic/model/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .elements import ContractMixin, StateMixin, ActionStateMixin, TransitionStateMixin, CompositeStateMixin, HistoryStateMixin
from .elements import BasicState, CompoundState, OrthogonalState, ShallowHistoryState, DeepHistoryState, FinalState
from .elements import Transition
from .statechart import Statechart
from .steps import MicroStep, MacroStep
from .events import Event, InternalEvent
234 changes: 234 additions & 0 deletions sismic/model/elements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
class ContractMixin:
"""
Mixin with a contract: preconditions, postconditions and invariants.
"""

def __init__(self):
self.preconditions = []
self.postconditions = []
self.invariants = []


class StateMixin:
"""
State element with a name.
:param name: name of the state
"""

def __init__(self, name: str):
self._name = name

@property
def name(self):
return self._name

def __repr__(self):
return '{}({})'.format(self.__class__.__name__, self.name)

def __eq__(self, other):
return isinstance(other, StateMixin) and self.name == other.name

def __hash__(self):
return hash(self.name)


class ActionStateMixin:
"""
State that can define actions on entry and on exit.
:param on_entry: code to execute when state is entered
:param on_exit: code to execute when state is exited
"""

def __init__(self, on_entry: str = None, on_exit: str = None):
self.on_entry = on_entry
self.on_exit = on_exit


class TransitionStateMixin:
"""
A simple state can host transitions
"""
pass


class CompositeStateMixin:
"""
Composite state can have children states.
"""
pass


class HistoryStateMixin:
"""
History state has a memory that can be resumed.
:param memory: name of the initial state
"""

def __init__(self, memory: str = None):
self.memory = memory


class BasicState(ContractMixin, StateMixin, ActionStateMixin, TransitionStateMixin):
"""
A basic state, with a name, transitions, actions, etc. but no child state.
:param name: name of this state
:param on_entry: code to execute when state is entered
:param on_exit: code to execute when state is exited
"""

def __init__(self, name: str, on_entry: str = None, on_exit: str = None):
ContractMixin.__init__(self)
StateMixin.__init__(self, name)
ActionStateMixin.__init__(self, on_entry, on_exit)
TransitionStateMixin.__init__(self)


class CompoundState(ContractMixin, StateMixin, ActionStateMixin, TransitionStateMixin, CompositeStateMixin):
"""
Compound states must have children states.
:param name: name of this state
:param initial: name of the initial state
:param on_entry: code to execute when state is entered
:param on_exit: code to execute when state is exited
"""

def __init__(self, name: str, initial: str = None, on_entry: str = None, on_exit: str = None):
ContractMixin.__init__(self)
StateMixin.__init__(self, name)
ActionStateMixin.__init__(self, on_entry, on_exit)
TransitionStateMixin.__init__(self)
CompositeStateMixin.__init__(self)
self.initial = initial


class OrthogonalState(ContractMixin, StateMixin, ActionStateMixin, TransitionStateMixin, CompositeStateMixin):
"""
Orthogonal states run their children simultaneously.
:param name: name of this state
:param on_entry: code to execute when state is entered
:param on_exit: code to execute when state is exited
"""

def __init__(self, name: str, on_entry: str = None, on_exit: str = None):
ContractMixin.__init__(self)
StateMixin.__init__(self, name)
ActionStateMixin.__init__(self, on_entry, on_exit)
TransitionStateMixin.__init__(self)
CompositeStateMixin.__init__(self)


class ShallowHistoryState(ContractMixin, StateMixin, ActionStateMixin, HistoryStateMixin):
"""
A shallow history state resumes the execution of its parent.
It activates the latest visited state of its parent.
:param name: name of this state
:param on_entry: code to execute when state is entered
:param on_exit: code to execute when state is exited
:param memory: name of the initial state
"""
def __init__(self, name: str, on_entry: str=None, on_exit: str=None, memory: str=None):
ContractMixin.__init__(self)
StateMixin.__init__(self, name)
ActionStateMixin.__init__(self, on_entry, on_exit)
HistoryStateMixin.__init__(self, memory)


class DeepHistoryState(ContractMixin, StateMixin, ActionStateMixin, HistoryStateMixin):
"""
A deep history state resumes the execution of its parent, and of every nested
active states in its parent.
:param name: name of this state
:param on_entry: code to execute when state is entered
:param on_exit: code to execute when state is exited
:param memory: name of the initial state
"""
def __init__(self, name: str, on_entry: str=None, on_exit: str=None, memory: str=None):
ContractMixin.__init__(self)
StateMixin.__init__(self, name)
ActionStateMixin.__init__(self, on_entry, on_exit)
HistoryStateMixin.__init__(self, memory)


class FinalState(ContractMixin, StateMixin, ActionStateMixin):
"""
Final state has NO transition and is used to detect state machine termination.
:param name: name of this state
:param on_entry: code to execute when state is entered
:param on_exit: code to execute when state is exited
"""

def __init__(self, name: str, on_entry: str = None, on_exit: str = None):
ContractMixin.__init__(self)
StateMixin.__init__(self, name)
ActionStateMixin.__init__(self, on_entry, on_exit)


class Transition(ContractMixin):
"""
Represent a transition from a source state to a target state.
A transition can be eventless (no event) or internal (no target).
A condition (code as string) can be specified as a guard.
:param source: name of the source state
:param target: name of the target state (if transition is not internal)
:param event: event name (if any)
:param guard: condition as code (if any)
:param action: action as code (if any)
"""

def __init__(self, source: str, target: str = None, event: str = None, guard: str = None, action: str = None):
ContractMixin.__init__(self)
self._source = source
self._target = target
self.event = event
self.guard = guard
self.action = action

@property
def source(self):
return self._source

@property
def target(self):
return self._target

@property
def internal(self):
"""
Boolean indicating whether this transition is an internal transition.
"""
return self._target is None

@property
def eventless(self):
"""
Boolean indicating whether this transition is an eventless transition.
"""
return self.event is None

def __eq__(self, other):
return (isinstance(other, Transition) and
self.source == other.source and
self.target == other.target and
self.event == other.event and
self.guard == other.guard and
self.action == other.action)

def __repr__(self):
return 'Transition({0}, {1}, {2})'.format(self.source, self.target, self.event)

def __str__(self):
return '{} [{}] -> {}'.format(self.source, self.event, self.target if self.target else '')

def __hash__(self):
return hash(self.source)
40 changes: 40 additions & 0 deletions sismic/model/events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class Event:
"""
Simple event with a name and (optionally) some data.
Unless the attribute already exists, each key from *data* is exposed as an attribute
of this class.
:param name: Name of the event
:param data: additional data (mapping, dict-like)
"""

def __init__(self, name: str, **additional_parameters):
self.name = name
self.data = additional_parameters

def __eq__(self, other):
return (isinstance(other, Event) and
self.name == other.name and
self.data == other.data)

def __getattr__(self, attr):
try:
return self.data[attr]
except:
raise AttributeError('{} has no attribute {}'.format(self, attr))

def __hash__(self):
return hash(self.name)

def __repr__(self):
if self.data:
return '{}({}, {})'.format(self.__class__.__name__, self.name, ', '.join('{}={}'.format(k, v) for k,v in self.data.items()))
else:
return '{}({})'.format(self.__class__.__name__, self.name)


class InternalEvent(Event):
"""
Subclass of Event that represents an internal event.
"""
pass

0 comments on commit 4c50811

Please sign in to comment.