Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #623 testing #632

Merged
merged 8 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["2.7", "3.8", "3.9", "3.10"]
python-version: ["3.8", "3.9", "3.10", "3.11"]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ pylint.log
sandbox*
coverage.xml
.dmypy.json
.venv
6 changes: 4 additions & 2 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -930,13 +930,15 @@ def a_condition(event_data):
def check_prepare_repr(event_data):
self.assertRegex(
str(event_data),
r"<EventData\('<State\('A'\)@\d+>', "
r"<EventData\(<Event\('do_strcheck'\)@\d+>, "
r"<State\('A'\)@\d+>, "
r"None\)@\d+>")

def check_before_repr(event_data):
self.assertRegex(
str(event_data),
r"<EventData\('<State\('A'\)@\d+>', "
r"<EventData\(<Event\('do_strcheck'\)@\d+>, "
r"<State\('A'\)@\d+>, "
r"<Transition\('A', 'B'\)@\d+>\)@\d+>")
m.checked = True

Expand Down
8 changes: 7 additions & 1 deletion tests/test_graphviz.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,17 @@ def test_graphviz_fallback(self):
m = self.machine_cls(states=['A', 'B', 'C'], initial='A', use_pygraphviz=True)
# make sure to reload after test is done to avoid side effects with other tests
reload(diagrams_pygraphviz)
print(m.graph_cls, pgv)
# print(m.graph_cls, pgv)
self.assertTrue(issubclass(m.graph_cls, Graph))
except ImportError:
pass

def test_function_callbacks_annotation(self):
m = self.machine_cls(states=['A', 'B'], initial='A', use_pygraphviz=self.use_pygraphviz, show_conditions=True)
m.add_transition('advance', 'A', 'B', conditions=m.is_A, unless=m.is_B)
_, nodes, edges = self.parse_dot(m.get_graph())
self.assertIn("[is_state(A", edges[0])


@skipIf(pgv is None, 'Graph diagram test requires graphviz')
class TestDiagramsLocked(TestDiagrams):
Expand Down
11 changes: 11 additions & 0 deletions tests/test_nesting.py
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,17 @@ class CustomHierarchicalMachine(HierarchicalMachine):
self.state_cls = CustomNestedState
self.stuff = Stuff(self.states, self.machine_cls)

def test_add_nested_state(self):
m = self.machine_cls(states=['A'], initial='A')
m.add_state('B{0}1{0}a'.format(self.state_cls.separator))
m.add_state('B{0}2{0}b'.format(self.state_cls.separator))
self.assertIn('B', m.states)
self.assertIn('1', m.states['B'].states)
self.assertIn('a', m.states['B'].states['1'].states)

with self.assertRaises(ValueError):
m.add_state(m.states['A'])

def test_enter_exit_nested(self):
separator = self.state_cls.separator
s = self.stuff
Expand Down
10 changes: 5 additions & 5 deletions transitions/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@

try:
from builtins import object
except ImportError:
except ImportError: # pragma: no cover
# python2
pass

try:
# Enums are supported for Python 3.4+ and Python 2.7 with enum34 package installed
from enum import Enum, EnumMeta
except ImportError:
except ImportError: # pragma: no cover
# If enum is not available, create dummy classes for type checks
class Enum: # type:ignore
""" This is just an Enum stub for Python 2 and Python 3.3 and before without Enum support. """
Expand Down Expand Up @@ -351,8 +351,8 @@ def update(self, state):
self.state = self.machine.get_state(state)

def __repr__(self):
return "<%s('%s', %s)@%s>" % (type(self).__name__, self.state,
getattr(self, 'transition'), id(self))
return "<%s(%s, %s, %s)@%s>" % (type(self).__name__, self.event, self.state,
getattr(self, 'transition'), id(self))


class Event(object):
Expand Down Expand Up @@ -576,7 +576,7 @@ def __init__(self, model=self_literal, states=None, initial='initial', transitio
self._initial = None

self.states = OrderedDict()
self.events = {}
self.events = OrderedDict()
self.send_event = send_event
self.auto_transitions = auto_transitions
self.ignore_invalid_triggers = ignore_invalid_triggers
Expand Down
2 changes: 1 addition & 1 deletion transitions/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@
# only available for Python 3
from .asyncio import AsyncMachine, HierarchicalAsyncMachine
from .factory import AsyncGraphMachine, HierarchicalAsyncGraphMachine
except (ImportError, SyntaxError):
except (ImportError, SyntaxError): # pragma: no cover
pass
15 changes: 10 additions & 5 deletions transitions/extensions/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"""

from functools import partial
import itertools
from six import iteritems

from ..core import Machine, Transition

Expand All @@ -18,7 +20,7 @@
try:
from transitions.extensions.asyncio import AsyncMachine, AsyncTransition
from transitions.extensions.asyncio import HierarchicalAsyncMachine, NestedAsyncTransition
except (ImportError, SyntaxError):
except (ImportError, SyntaxError): # pragma: no cover
class AsyncMachine(Machine): # type: ignore
""" A mock of AsyncMachine for Python 3.6 and earlier. """

Expand Down Expand Up @@ -71,7 +73,12 @@ class LockedGraphMachine(GraphMachine, LockedMachine):
@staticmethod
def format_references(func):
if isinstance(func, partial) and func.func.__name__.startswith('_locked_method'):
func = func.args[0]
return "%s(%s)" % (
func.args[0].__name__,
", ".join(itertools.chain(
(str(_) for _ in func.args[1:]),
("%s=%s" % (key, value)
for key, value in iteritems(func.keywords if func.keywords else {})))))
return GraphMachine.format_references(func)


Expand All @@ -85,9 +92,7 @@ class LockedHierarchicalGraphMachine(GraphMachine, LockedHierarchicalMachine):

@staticmethod
def format_references(func):
if isinstance(func, partial) and func.func.__name__.startswith('_locked_method'):
func = func.args[0]
return GraphMachine.format_references(func)
return LockedGraphMachine.format_references(func)


class AsyncGraphMachine(GraphMachine, AsyncMachine):
Expand Down
4 changes: 2 additions & 2 deletions transitions/extensions/locking.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@

try:
from contextlib import nested # Python 2
from thread import get_ident
from thread import get_ident # pragma: no cover
# with nested statements now raise a DeprecationWarning. Should be replaced with ExitStack-like approaches.
warnings.simplefilter('ignore', DeprecationWarning)
warnings.simplefilter('ignore', DeprecationWarning) # pragma: no cover

except ImportError:
from contextlib import ExitStack, contextmanager
Expand Down
2 changes: 1 addition & 1 deletion transitions/extensions/markup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
try:
# Enums are supported for Python 3.4+ and Python 2.7 with enum34 package installed
from enum import Enum, EnumMeta
except ImportError:
except ImportError: # pragma: no cover
# If enum is not available, create dummy classes for type checks
# typing must be prevent redefinition issues with mypy
class Enum: # type:ignore
Expand Down
31 changes: 13 additions & 18 deletions transitions/extensions/nesting.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
try:
# Enums are supported for Python 3.4+ and Python 2.7 with enum34 package installed
from enum import Enum, EnumMeta
except ImportError:
except ImportError: # pragma: no cover
# If enum is not available, create dummy classes for type checks
class Enum: # type: ignore
""" This is just an Enum stub for Python 2 and Python 3.3 and before without Enum support. """
Expand Down Expand Up @@ -71,17 +71,13 @@ def resolve_order(state_tree):
class FunctionWrapper(object):
""" A wrapper to enable transitions' convenience function to_<state> for nested states.
This allows to call model.to_A.s1.C() in case a custom separator has been chosen."""
def __init__(self, func, path):
def __init__(self, func):
"""
Args:
func: Function to be called at the end of the path.
path: If path is an empty string, assign function
"""
if path:
self.add(func, path)
self._func = None
else:
self._func = func
self._func = func

def add(self, func, path):
""" Assigns a `FunctionWrapper` as an attribute named like the next segment of the substates
Expand All @@ -90,16 +86,14 @@ def add(self, func, path):
func (callable): Function to be called at the end of the path.
path (list of strings): Remaining segment of the substate path.
"""
if path:
name = path[0]
if name[0].isdigit():
name = 's' + name
if hasattr(self, name):
getattr(self, name).add(func, path[1:])
else:
setattr(self, name, FunctionWrapper(func, path[1:]))
name = path[0]
if name[0].isdigit():
name = 's' + name
if hasattr(self, name):
getattr(self, name).add(func, path[1:])
else:
self._func = func
assert not path[1:], "nested path should be empty"
setattr(self, name, FunctionWrapper(func))

def __call__(self, *args, **kwargs):
return self._func(*args, **kwargs)
Expand Down Expand Up @@ -854,7 +848,8 @@ def _add_model_to_state(self, state, model):
if hasattr(model, 'is_' + path[0]):
getattr(model, 'is_' + path[0]).add(trig_func, path[1:])
else:
self._checked_assignment(model, 'is_' + path[0], FunctionWrapper(trig_func, path[1:]))
assert not path[1:], "nested path should be empty"
self._checked_assignment(model, 'is_' + path[0], FunctionWrapper(trig_func))
with self(state.name):
for event in self.events.values():
if not hasattr(model, event.name):
Expand Down Expand Up @@ -946,7 +941,7 @@ def _add_trigger_to_model(self, trigger, model):
getattr(model, 'to_' + path[0]).add(trig_func, path[1:])
else:
# create a new function wrapper
self._checked_assignment(model, 'to_' + path[0], FunctionWrapper(trig_func, path[1:]))
self._checked_assignment(model, 'to_' + path[0], FunctionWrapper(trig_func))
else:
self._checked_assignment(model, trigger, trig_func)

Expand Down