From 8843bea315e29b1f82d5e7c5c2a1bfbe3e4f9660 Mon Sep 17 00:00:00 2001 From: Tony Breeds Date: Tue, 12 Sep 2017 15:55:50 -0600 Subject: [PATCH] Retire Packaging Deb project repos This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: Id78bcb20d483466d39e68f1cfa2eb8b4645ca53f --- .coveragerc | 8 - .gitignore | 60 --- .gitreview | 4 - .testr.conf | 8 - CONTRIBUTING.rst | 28 - HACKING.rst | 5 - LICENSE | 202 -------- README | 14 + README.rst | 23 - automaton/__init__.py | 0 automaton/_utils.py | 48 -- automaton/converters/__init__.py | 0 automaton/converters/pydot.py | 111 ---- automaton/exceptions.py | 40 -- automaton/machines.py | 543 -------------------- automaton/runners.py | 188 ------- automaton/tests/__init__.py | 0 automaton/tests/test_fsm.py | 465 ----------------- babel.cfg | 2 - doc/source/conf.py | 84 --- doc/source/contributor/index.rst | 5 - doc/source/index.rst | 20 - doc/source/install/index.rst | 12 - doc/source/reference/index.rst | 51 -- doc/source/user/examples.rst | 462 ----------------- doc/source/user/features.rst | 17 - doc/source/user/history.rst | 2 - doc/source/user/index.rst | 10 - releasenotes/notes/.placeholder | 0 releasenotes/source/_static/.placeholder | 0 releasenotes/source/_templates/.placeholder | 0 releasenotes/source/conf.py | 279 ---------- releasenotes/source/index.rst | 9 - releasenotes/source/ocata.rst | 6 - releasenotes/source/unreleased.rst | 5 - requirements.txt | 15 - setup.cfg | 42 -- setup.py | 29 -- test-requirements.txt | 15 - tools/tox_install.sh | 30 -- tox.ini | 39 -- 41 files changed, 14 insertions(+), 2867 deletions(-) delete mode 100644 .coveragerc delete mode 100644 .gitignore delete mode 100644 .gitreview delete mode 100644 .testr.conf delete mode 100644 CONTRIBUTING.rst delete mode 100644 HACKING.rst delete mode 100644 LICENSE create mode 100644 README delete mode 100644 README.rst delete mode 100644 automaton/__init__.py delete mode 100644 automaton/_utils.py delete mode 100644 automaton/converters/__init__.py delete mode 100644 automaton/converters/pydot.py delete mode 100644 automaton/exceptions.py delete mode 100644 automaton/machines.py delete mode 100644 automaton/runners.py delete mode 100644 automaton/tests/__init__.py delete mode 100644 automaton/tests/test_fsm.py delete mode 100644 babel.cfg delete mode 100755 doc/source/conf.py delete mode 100644 doc/source/contributor/index.rst delete mode 100644 doc/source/index.rst delete mode 100644 doc/source/install/index.rst delete mode 100644 doc/source/reference/index.rst delete mode 100644 doc/source/user/examples.rst delete mode 100644 doc/source/user/features.rst delete mode 100644 doc/source/user/history.rst delete mode 100644 doc/source/user/index.rst delete mode 100644 releasenotes/notes/.placeholder delete mode 100644 releasenotes/source/_static/.placeholder delete mode 100644 releasenotes/source/_templates/.placeholder delete mode 100644 releasenotes/source/conf.py delete mode 100644 releasenotes/source/index.rst delete mode 100644 releasenotes/source/ocata.rst delete mode 100644 releasenotes/source/unreleased.rst delete mode 100644 requirements.txt delete mode 100644 setup.cfg delete mode 100644 setup.py delete mode 100644 test-requirements.txt delete mode 100755 tools/tox_install.sh delete mode 100644 tox.ini diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index bbbf748..0000000 --- a/.coveragerc +++ /dev/null @@ -1,8 +0,0 @@ -[run] -branch = True -source = automaton -omit = automaton/tests/* - -[report] -ignore_errors = True -precision = 2 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index f678959..0000000 --- a/.gitignore +++ /dev/null @@ -1,60 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.testrepository/ -.tox/ -.coverage -cover -.cache -nosetests.xml -coverage.xml - -# Translations -*.mo -*.pot - -# Django stuff: -*.log - -# Sphinx documentation -doc/build/ - -# pbr generates these -AUTHORS -ChangeLog - -# PyBuilder -target/ diff --git a/.gitreview b/.gitreview deleted file mode 100644 index c8cf195..0000000 --- a/.gitreview +++ /dev/null @@ -1,4 +0,0 @@ -[gerrit] -host=review.openstack.org -port=29418 -project=openstack/automaton.git diff --git a/.testr.conf b/.testr.conf deleted file mode 100644 index 25e2f18..0000000 --- a/.testr.conf +++ /dev/null @@ -1,8 +0,0 @@ -[DEFAULT] -test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ - OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ - OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ - ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list - diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index 7d41a85..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,28 +0,0 @@ -If you would like to contribute to the development of OpenStack, you must -follow the steps in this page: - - http://docs.openstack.org/infra/manual/developers.html - -If you already have a good understanding of how the system works and your -OpenStack accounts are set up, you can skip to the development workflow -section of this documentation to learn how changes to OpenStack should be -submitted for review via the Gerrit tool: - - http://docs.openstack.org/infra/manual/developers.html#development-workflow - -The code is hosted at: - - http://git.openstack.org/cgit/openstack/automaton. - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed on Launchpad, not GitHub: - - https://bugs.launchpad.net/automaton - -The mailing list is (prefix subjects with "[Oslo][Automaton]"): - - http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev - -Questions and discussions take place in #openstack-state-management on -irc.freenode.net. diff --git a/HACKING.rst b/HACKING.rst deleted file mode 100644 index 71cfe4d..0000000 --- a/HACKING.rst +++ /dev/null @@ -1,5 +0,0 @@ -Automaton Style Commandments -=============================================== - -Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ - diff --git a/LICENSE b/LICENSE deleted file mode 100644 index e06d208..0000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/README b/README new file mode 100644 index 0000000..8fcd2b2 --- /dev/null +++ b/README @@ -0,0 +1,14 @@ +This project is no longer maintained. + +The contents of this repository are still available in the Git +source code management system. To see the contents of this +repository before it reached its end of life, please check out the +previous commit with "git checkout HEAD^1". + +For ongoing work on maintaining OpenStack packages in the Debian +distribution, please see the Debian OpenStack packaging team at +https://wiki.debian.org/OpenStack/. + +For any further questions, please email +openstack-dev@lists.openstack.org or join #openstack-dev on +Freenode. diff --git a/README.rst b/README.rst deleted file mode 100644 index e03db29..0000000 --- a/README.rst +++ /dev/null @@ -1,23 +0,0 @@ -========= -Automaton -========= - -.. image:: https://img.shields.io/pypi/v/automaton.svg - :target: https://pypi.python.org/pypi/automaton/ - :alt: Latest Version - -.. image:: https://img.shields.io/pypi/dm/automaton.svg - :target: https://pypi.python.org/pypi/automaton/ - :alt: Downloads - -Friendly state machines for python. The goal of this library is to provide -well documented state machine classes and associated utilities. The state -machine pattern (or the implemented variation there-of) is a commonly -used pattern and has a multitude of various usages. Some of the usages -for this library include providing state & transition validation and -running/scheduling/analyzing the execution of tasks. - -* Free software: Apache license -* Documentation: https://docs.openstack.org/automaton/latest/ -* Source: https://git.openstack.org/cgit/openstack/automaton -* Bugs: https://bugs.launchpad.net/automaton diff --git a/automaton/__init__.py b/automaton/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/automaton/_utils.py b/automaton/_utils.py deleted file mode 100644 index 8cd338b..0000000 --- a/automaton/_utils.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (C) 2015 Yahoo! Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import inspect - - -def get_callback_name(cb): - """Tries to get a callbacks fully-qualified name. - - If no name can be produced ``repr(cb)`` is called and returned. - """ - segments = [] - try: - segments.append(cb.__qualname__) - except AttributeError: - try: - segments.append(cb.__name__) - if inspect.ismethod(cb): - try: - # This attribute doesn't exist on py3.x or newer, so - # we optionally ignore it... (on those versions of - # python `__qualname__` should have been found anyway). - segments.insert(0, cb.im_class.__name__) - except AttributeError: - pass - except AttributeError: - pass - if not segments: - return repr(cb) - else: - try: - # When running under sphinx it appears this can be none? - if cb.__module__: - segments.insert(0, cb.__module__) - except AttributeError: - pass - return ".".join(segments) diff --git a/automaton/converters/__init__.py b/automaton/converters/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/automaton/converters/pydot.py b/automaton/converters/pydot.py deleted file mode 100644 index 415adc5..0000000 --- a/automaton/converters/pydot.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (C) 2015 Yahoo! Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from __future__ import absolute_import - -try: - import pydot - PYDOT_AVAILABLE = True -except ImportError: - PYDOT_AVAILABLE = False - - -def convert(machine, graph_name, - graph_attrs=None, node_attrs_cb=None, edge_attrs_cb=None, - add_start_state=True, name_translations=None): - """Translates the state machine into a pydot graph. - - :param machine: state machine to convert - :type machine: FiniteMachine - :param graph_name: name of the graph to be created - :type graph_name: string - :param graph_attrs: any initial graph attributes to set - (see http://www.graphviz.org/doc/info/attrs.html for - what these can be) - :type graph_attrs: dict - :param node_attrs_cb: a callback that takes one argument ``state`` - and is expected to return a dict of node attributes - (see http://www.graphviz.org/doc/info/attrs.html for - what these can be) - :type node_attrs_cb: callback - :param edge_attrs_cb: a callback that takes three arguments ``start_state, - event, end_state`` and is expected to return a dict - of edge attributes (see - http://www.graphviz.org/doc/info/attrs.html for - what these can be) - :type edge_attrs_cb: callback - :param add_start_state: when enabled this creates a *private* start state - with the name ``__start__`` that will be a point - node that will have a dotted edge to the - ``default_start_state`` that your machine may have - defined (if your machine has no actively defined - ``default_start_state`` then this does nothing, - even if enabled) - :type add_start_state: bool - :param name_translations: a dict that provides alternative ``state`` - string names for each state - :type name_translations: dict - """ - if not PYDOT_AVAILABLE: - raise RuntimeError("pydot (or pydot2 or equivalent) is required" - " to convert a state machine into a pydot" - " graph") - if not name_translations: - name_translations = {} - graph_kwargs = { - 'rankdir': 'LR', - 'nodesep': '0.25', - 'overlap': 'false', - 'ranksep': '0.5', - 'size': "11x8.5", - 'splines': 'true', - 'ordering': 'in', - } - if graph_attrs is not None: - graph_kwargs.update(graph_attrs) - graph_kwargs['graph_name'] = graph_name - g = pydot.Dot(**graph_kwargs) - node_attrs = { - 'fontsize': '11', - } - nodes = {} - for (start_state, event, end_state) in machine: - if start_state not in nodes: - start_node_attrs = node_attrs.copy() - if node_attrs_cb is not None: - start_node_attrs.update(node_attrs_cb(start_state)) - pretty_start_state = name_translations.get(start_state, - start_state) - nodes[start_state] = pydot.Node(pretty_start_state, - **start_node_attrs) - g.add_node(nodes[start_state]) - if end_state not in nodes: - end_node_attrs = node_attrs.copy() - if node_attrs_cb is not None: - end_node_attrs.update(node_attrs_cb(end_state)) - pretty_end_state = name_translations.get(end_state, end_state) - nodes[end_state] = pydot.Node(pretty_end_state, **end_node_attrs) - g.add_node(nodes[end_state]) - edge_attrs = {} - if edge_attrs_cb is not None: - edge_attrs.update(edge_attrs_cb(start_state, event, end_state)) - g.add_edge(pydot.Edge(nodes[start_state], nodes[end_state], - **edge_attrs)) - if add_start_state and machine.default_start_state: - start = pydot.Node("__start__", shape="point", width="0.1", - xlabel='start', fontcolor='green', **node_attrs) - g.add_node(start) - g.add_edge(pydot.Edge(start, nodes[machine.default_start_state], - style='dotted')) - return g diff --git a/automaton/exceptions.py b/automaton/exceptions.py deleted file mode 100644 index 069b98b..0000000 --- a/automaton/exceptions.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (C) 2014 Yahoo! Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -class AutomatonException(Exception): - """Base class for *most* exceptions emitted from this library.""" - - -class InvalidState(AutomatonException): - """Raised when a invalid state transition is attempted while executing.""" - - -class NotInitialized(AutomatonException): - """Error raised when an action is attempted on a not inited machine.""" - - -class NotFound(AutomatonException): - """Raised when some entry in some object doesn't exist.""" - - -class Duplicate(AutomatonException): - """Raised when a duplicate entry is found.""" - - -class FrozenMachine(AutomatonException): - """Exception raised when a frozen machine is modified.""" - - def __init__(self): - super(FrozenMachine, self).__init__("Frozen machine can't be modified") diff --git a/automaton/machines.py b/automaton/machines.py deleted file mode 100644 index 6a9d3b5..0000000 --- a/automaton/machines.py +++ /dev/null @@ -1,543 +0,0 @@ -# Copyright (C) 2014 Yahoo! Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import collections - -from debtcollector import removals -import prettytable -import six - -from automaton import _utils as utils -from automaton import exceptions as excp - - -class State(object): - """Container that defines needed components of a single state. - - Usage of this and the :meth:`~.FiniteMachine.build` make creating finite - state machines that much easier. - - :ivar name: The name of the state. - :ivar is_terminal: Whether this state is terminal (or not). - :ivar next_states: Dictionary of 'event' -> 'next state name' (or none). - :ivar on_enter: callback that will be called when the state is entered. - :ivar on_exit: callback that will be called when the state is exited. - """ - - def __init__(self, name, - is_terminal=False, next_states=None, - on_enter=None, on_exit=None): - self.name = name - self.is_terminal = bool(is_terminal) - self.next_states = next_states - self.on_enter = on_enter - self.on_exit = on_exit - - -def _convert_to_states(state_space): - # NOTE(harlowja): if provided dicts, convert them... - for state in state_space: - if isinstance(state, dict): - state = State(**state) - yield state - - -def _orderedkeys(data, sort=True): - if sort: - return sorted(six.iterkeys(data)) - else: - return list(six.iterkeys(data)) - - -class _Jump(object): - """A FSM transition tracks this data while jumping.""" - def __init__(self, name, on_enter, on_exit): - self.name = name - self.on_enter = on_enter - self.on_exit = on_exit - - -class FiniteMachine(object): - """A finite state machine. - - This state machine can be used to automatically run a given set of - transitions and states in response to events (either from callbacks or from - generator/iterator send() values, see PEP 342). On each triggered event, a - ``on_enter`` and ``on_exit`` callback can also be provided which will be - called to perform some type of action on leaving a prior state and before - entering a new state. - - NOTE(harlowja): reactions will *only* be called when the generator/iterator - from :py:meth:`~automaton.runners.Runner.run_iter` does *not* send - back a new event (they will always be called if the - :py:meth:`~automaton.runners.Runner.run` method is used). This allows - for two unique ways (these ways can also be intermixed) to use this state - machine when using :py:meth:`~automaton.runners.Runner.run`; one - where *external* event trigger the next state transition and one - where *internal* reaction callbacks trigger the next state - transition. The other way to use this - state machine is to skip using :py:meth:`~automaton.runners.Runner.run` - or :py:meth:`~automaton.runners.Runner.run_iter` - completely and use the :meth:`~.FiniteMachine.process_event` method - explicitly and trigger the events via - some *external* functionality/triggers... - """ - - #: The result of processing an event (cause and effect...) - Effect = collections.namedtuple('Effect', 'reaction,terminal') - - @classmethod - def _effect_builder(cls, new_state, event): - return cls.Effect(new_state['reactions'].get(event), - new_state["terminal"]) - - @removals.removed_kwarg('default_start_state', - message="The usage of 'default_start_state' via" - " the machine constructor is deprecated and will" - " be removed in a future version; usage of" - " the 'default_start_state' property setter is" - " recommended.") - def __init__(self, default_start_state=None): - self._transitions = {} - self._states = collections.OrderedDict() - self._default_start_state = default_start_state - self._current = None - self.frozen = False - - @property - def default_start_state(self): - """Sets the *default* start state that the machine should use. - - NOTE(harlowja): this will be used by ``initialize`` but only if that - function is not given its own ``start_state`` that overrides this - default. - """ - return self._default_start_state - - @default_start_state.setter - def default_start_state(self, state): - if self.frozen: - raise excp.FrozenMachine() - if state not in self._states: - raise excp.NotFound("Can not set the default start state to" - " undefined state '%s'" % (state)) - self._default_start_state = state - - @classmethod - def build(cls, state_space): - """Builds a machine from a state space listing. - - Each element of this list must be an instance - of :py:class:`.State` or a ``dict`` with equivalent keys that - can be used to construct a :py:class:`.State` instance. - """ - state_space = list(_convert_to_states(state_space)) - m = cls() - for state in state_space: - m.add_state(state.name, - terminal=state.is_terminal, - on_enter=state.on_enter, - on_exit=state.on_exit) - for state in state_space: - if state.next_states: - for event, next_state in state.next_states.items(): - if isinstance(next_state, State): - next_state = next_state.name - m.add_transition(state.name, next_state, event) - return m - - @property - def current_state(self): - """The current state the machine is in (or none if not initialized).""" - if self._current is not None: - return self._current.name - return None - - @property - def terminated(self): - """Returns whether the state machine is in a terminal state.""" - if self._current is None: - return False - return self._states[self._current.name]['terminal'] - - def add_state(self, state, terminal=False, on_enter=None, on_exit=None): - """Adds a given state to the state machine. - - The ``on_enter`` and ``on_exit`` callbacks, if provided will be - expected to take two positional parameters, these being the state - being exited (for ``on_exit``) or the state being entered (for - ``on_enter``) and a second parameter which is the event that is - being processed that caused the state transition. - """ - if self.frozen: - raise excp.FrozenMachine() - if state in self._states: - raise excp.Duplicate("State '%s' already defined" % state) - if on_enter is not None: - if not six.callable(on_enter): - raise ValueError("On enter callback must be callable") - if on_exit is not None: - if not six.callable(on_exit): - raise ValueError("On exit callback must be callable") - self._states[state] = { - 'terminal': bool(terminal), - 'reactions': {}, - 'on_enter': on_enter, - 'on_exit': on_exit, - } - self._transitions[state] = collections.OrderedDict() - - def is_actionable_event(self, event): - """Check whether the event is actionable in the current state.""" - current = self._current - if current is None: - return False - if event not in self._transitions[current.name]: - return False - return True - - def add_reaction(self, state, event, reaction, *args, **kwargs): - """Adds a reaction that may get triggered by the given event & state. - - Reaction callbacks may (depending on how the state machine is ran) be - used after an event is processed (and a transition occurs) to cause the - machine to react to the newly arrived at stable state. - - These callbacks are expected to accept three default positional - parameters (although more can be passed in via *args and **kwargs, - these will automatically get provided to the callback when it is - activated *ontop* of the three default). The three default parameters - are the last stable state, the new stable state and the event that - caused the transition to this new stable state to be arrived at. - - The expected result of a callback is expected to be a new event that - the callback wants the state machine to react to. This new event - may (depending on how the state machine is ran) get processed (and - this process typically repeats) until the state machine reaches a - terminal state. - """ - if self.frozen: - raise excp.FrozenMachine() - if state not in self._states: - raise excp.NotFound("Can not add a reaction to event '%s' for an" - " undefined state '%s'" % (event, state)) - if not six.callable(reaction): - raise ValueError("Reaction callback must be callable") - if event not in self._states[state]['reactions']: - self._states[state]['reactions'][event] = (reaction, args, kwargs) - else: - raise excp.Duplicate("State '%s' reaction to event '%s'" - " already defined" % (state, event)) - - def add_transition(self, start, end, event, replace=False): - """Adds an allowed transition from start -> end for the given event. - - :param start: starting state - :param end: ending state - :param event: event that causes start state to - transition to end state - :param replace: replace existing event instead of raising a - :py:class:`~automaton.exceptions.Duplicate` exception - when the transition already exists. - """ - if self.frozen: - raise excp.FrozenMachine() - if start not in self._states: - raise excp.NotFound("Can not add a transition on event '%s' that" - " starts in a undefined state '%s'" - % (event, start)) - if end not in self._states: - raise excp.NotFound("Can not add a transition on event '%s' that" - " ends in a undefined state '%s'" - % (event, end)) - if self._states[start]['terminal']: - raise excp.InvalidState("Can not add a transition on event '%s'" - " that starts in the terminal state '%s'" - % (event, start)) - if event in self._transitions[start] and not replace: - target = self._transitions[start][event] - if target.name != end: - raise excp.Duplicate("Cannot add transition from" - " '%(start_state)s' to '%(end_state)s'" - " on event '%(event)s' because a" - " transition from '%(start_state)s'" - " to '%(existing_end_state)s' on" - " event '%(event)s' already exists." - % {'existing_end_state': target.name, - 'end_state': end, 'event': event, - 'start_state': start}) - else: - target = _Jump(end, self._states[end]['on_enter'], - self._states[start]['on_exit']) - self._transitions[start][event] = target - - def _pre_process_event(self, event): - current = self._current - if current is None: - raise excp.NotInitialized("Can not process event '%s'; the state" - " machine hasn't been initialized" - % event) - if self._states[current.name]['terminal']: - raise excp.InvalidState("Can not transition from terminal" - " state '%s' on event '%s'" - % (current.name, event)) - if event not in self._transitions[current.name]: - raise excp.NotFound("Can not transition from state '%s' on" - " event '%s' (no defined transition)" - % (current.name, event)) - - def _post_process_event(self, event, result): - return result - - def process_event(self, event): - """Trigger a state change in response to the provided event. - - :returns: Effect this is either a :py:class:`.FiniteMachine.Effect` or - an ``Effect`` from a subclass of :py:class:`.FiniteMachine`. - See the appropriate named tuple for a description of the - actual items in the tuple. For - example, :py:class:`.FiniteMachine.Effect`'s - first item is ``reaction``: one could invoke this reaction's - callback to react to the new stable state. - :rtype: namedtuple - """ - self._pre_process_event(event) - current = self._current - replacement = self._transitions[current.name][event] - if current.on_exit is not None: - current.on_exit(current.name, event) - if replacement.on_enter is not None: - replacement.on_enter(replacement.name, event) - self._current = replacement - result = self._effect_builder(self._states[replacement.name], event) - return self._post_process_event(event, result) - - def initialize(self, start_state=None): - """Sets up the state machine (sets current state to start state...). - - :param start_state: explicit start state to use to initialize the - state machine to. If ``None`` is provided then - the machine's default start state will be used - instead. - """ - if start_state is None: - start_state = self._default_start_state - if start_state not in self._states: - raise excp.NotFound("Can not start from a undefined" - " state '%s'" % (start_state)) - if self._states[start_state]['terminal']: - raise excp.InvalidState("Can not start from a terminal" - " state '%s'" % (start_state)) - # No on enter will be called, since we are priming the state machine - # and have not really transitioned from anything to get here, we will - # though allow on_exit to be called on the event that causes this - # to be moved from... - self._current = _Jump(start_state, None, - self._states[start_state]['on_exit']) - - def copy(self, shallow=False, unfreeze=False): - """Copies the current state machine. - - NOTE(harlowja): the copy will be left in an *uninitialized* state. - - NOTE(harlowja): when a shallow copy is requested the copy will share - the same transition table and state table as the - source; this can be advantageous if you have a machine - and transitions + states that is defined somewhere - and want to use copies to run with (the copies have - the current state that is different between machines). - """ - c = type(self)() - c._default_start_state = self._default_start_state - if unfreeze and self.frozen: - c.frozen = False - else: - c.frozen = self.frozen - if not shallow: - for state, data in self._states.items(): - copied_data = data.copy() - copied_data['reactions'] = copied_data['reactions'].copy() - c._states[state] = copied_data - for state, data in self._transitions.items(): - c._transitions[state] = data.copy() - else: - c._transitions = self._transitions - c._states = self._states - return c - - def __contains__(self, state): - """Returns if this state exists in the machines known states.""" - return state in self._states - - def freeze(self): - """Freezes & stops addition of states, transitions, reactions...""" - self.frozen = True - - @property - def states(self): - """Returns the state names.""" - return list(six.iterkeys(self._states)) - - @property - def events(self): - """Returns how many events exist.""" - c = 0 - for state in six.iterkeys(self._states): - c += len(self._transitions[state]) - return c - - def __iter__(self): - """Iterates over (start, event, end) transition tuples.""" - for state in six.iterkeys(self._states): - for event, target in self._transitions[state].items(): - yield (state, event, target.name) - - def pformat(self, sort=True, empty='.'): - """Pretty formats the state + transition table into a string. - - NOTE(harlowja): the sort parameter can be provided to sort the states - and transitions by sort order; with it being provided as false the rows - will be iterated in addition order instead. - """ - tbl = prettytable.PrettyTable(["Start", "Event", "End", - "On Enter", "On Exit"]) - for state in _orderedkeys(self._states, sort=sort): - prefix_markings = [] - if self.current_state == state: - prefix_markings.append("@") - postfix_markings = [] - if self.default_start_state == state: - postfix_markings.append("^") - if self._states[state]['terminal']: - postfix_markings.append("$") - pretty_state = "%s%s" % ("".join(prefix_markings), state) - if postfix_markings: - pretty_state += "[%s]" % "".join(postfix_markings) - if self._transitions[state]: - for event in _orderedkeys(self._transitions[state], - sort=sort): - target = self._transitions[state][event] - row = [pretty_state, event, target.name] - if target.on_enter is not None: - row.append(utils.get_callback_name(target.on_enter)) - else: - row.append(empty) - if target.on_exit is not None: - row.append(utils.get_callback_name(target.on_exit)) - else: - row.append(empty) - tbl.add_row(row) - else: - on_enter = self._states[state]['on_enter'] - if on_enter is not None: - on_enter = utils.get_callback_name(on_enter) - else: - on_enter = empty - on_exit = self._states[state]['on_exit'] - if on_exit is not None: - on_exit = utils.get_callback_name(on_exit) - else: - on_exit = empty - tbl.add_row([pretty_state, empty, empty, on_enter, on_exit]) - return tbl.get_string() - - -class HierarchicalFiniteMachine(FiniteMachine): - """A fsm that understands how to run in a hierarchical mode.""" - - #: The result of processing an event (cause and effect...) - Effect = collections.namedtuple('Effect', - 'reaction,terminal,machine') - - def __init__(self, default_start_state=None): - super(HierarchicalFiniteMachine, self).__init__( - default_start_state=default_start_state) - self._nested_machines = {} - - @classmethod - def _effect_builder(cls, new_state, event): - return cls.Effect(new_state['reactions'].get(event), - new_state["terminal"], new_state.get('machine')) - - def add_state(self, state, - terminal=False, on_enter=None, on_exit=None, machine=None): - """Adds a given state to the state machine. - - :param machine: the nested state machine that will be transitioned - into when this state is entered - :type machine: :py:class:`.FiniteMachine` - - Further arguments are interpreted as - for :py:meth:`.FiniteMachine.add_state`. - """ - if machine is not None and not isinstance(machine, FiniteMachine): - raise ValueError( - "Nested state machines must themselves be state machines") - super(HierarchicalFiniteMachine, self).add_state( - state, terminal=terminal, on_enter=on_enter, on_exit=on_exit) - if machine is not None: - self._states[state]['machine'] = machine - self._nested_machines[state] = machine - - def copy(self, shallow=False, unfreeze=False): - c = super(HierarchicalFiniteMachine, self).copy(shallow=shallow, - unfreeze=unfreeze) - if shallow: - c._nested_machines = self._nested_machines - else: - c._nested_machines = self._nested_machines.copy() - return c - - def initialize(self, start_state=None, - nested_start_state_fetcher=None): - """Sets up the state machine (sets current state to start state...). - - :param start_state: explicit start state to use to initialize the - state machine to. If ``None`` is provided then the - machine's default start state will be used - instead. - :param nested_start_state_fetcher: A callback that can return start - states for any nested machines - **only**. If not ``None`` then it - will be provided a single argument, - the machine to provide a starting - state for and it is expected to - return a starting state (or - ``None``) for each machine called - with. Do note that this callback - will also be passed to other nested - state machines as well, so it will - also be used to initialize any state - machines they contain (recursively). - """ - super(HierarchicalFiniteMachine, self).initialize( - start_state=start_state) - for data in six.itervalues(self._states): - if 'machine' in data: - nested_machine = data['machine'] - nested_start_state = None - if nested_start_state_fetcher is not None: - nested_start_state = nested_start_state_fetcher( - nested_machine) - if isinstance(nested_machine, HierarchicalFiniteMachine): - nested_machine.initialize( - start_state=nested_start_state, - nested_start_state_fetcher=nested_start_state_fetcher) - else: - nested_machine.initialize(start_state=nested_start_state) - - @property - def nested_machines(self): - """Dictionary of **all** nested state machines this machine may use.""" - return self._nested_machines diff --git a/automaton/runners.py b/automaton/runners.py deleted file mode 100644 index af8d463..0000000 --- a/automaton/runners.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (C) 2015 Yahoo! Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import abc - -import six - -from automaton import exceptions as excp -from automaton import machines - - -_JUMPER_NOT_FOUND_TPL = ("Unable to progress since no reaction (or" - " sent event) has been made available in" - " new state '%s' (moved to from state '%s'" - " in response to event '%s')") - - -@six.add_metaclass(abc.ABCMeta) -class Runner(object): - """Machine runner used to run a state machine. - - Only **one** runner per machine should be active at the same time (aka - there should not be multiple runners using the same machine instance at - the same time). - """ - def __init__(self, machine): - self._machine = machine - - @abc.abstractmethod - def run(self, event, initialize=True): - """Runs the state machine, using reactions only.""" - - @abc.abstractmethod - def run_iter(self, event, initialize=True): - """Returns a iterator/generator that will run the state machine. - - NOTE(harlowja): only one runner iterator/generator should be active for - a machine, if this is not observed then it is possible for - initialization and other local state to be corrupted and cause issues - when running... - """ - - -class FiniteRunner(Runner): - """Finite machine runner used to run a finite machine. - - Only **one** runner per machine should be active at the same time (aka - there should not be multiple runners using the same machine instance at - the same time). - """ - - def __init__(self, machine): - """Create a runner for the given machine.""" - if not isinstance(machine, (machines.FiniteMachine,)): - raise TypeError("FiniteRunner only works with FiniteMachine(s)") - super(FiniteRunner, self).__init__(machine) - - def run(self, event, initialize=True): - for transition in self.run_iter(event, initialize=initialize): - pass - - def run_iter(self, event, initialize=True): - if initialize: - self._machine.initialize() - while True: - old_state = self._machine.current_state - reaction, terminal = self._machine.process_event(event) - new_state = self._machine.current_state - try: - sent_event = yield (old_state, new_state) - except GeneratorExit: - break - if terminal: - break - if reaction is None and sent_event is None: - raise excp.NotFound(_JUMPER_NOT_FOUND_TPL % (new_state, - old_state, - event)) - elif sent_event is not None: - event = sent_event - else: - cb, args, kwargs = reaction - event = cb(old_state, new_state, event, *args, **kwargs) - - -class HierarchicalRunner(Runner): - """Hierarchical machine runner used to run a hierarchical machine. - - Only **one** runner per machine should be active at the same time (aka - there should not be multiple runners using the same machine instance at - the same time). - """ - - def __init__(self, machine): - """Create a runner for the given machine.""" - if not isinstance(machine, (machines.HierarchicalFiniteMachine,)): - raise TypeError("HierarchicalRunner only works with" - " HierarchicalFiniteMachine(s)") - super(HierarchicalRunner, self).__init__(machine) - - def run(self, event, initialize=True): - for transition in self.run_iter(event, initialize=initialize): - pass - - @staticmethod - def _process_event(machines, event): - """Matches a event to the machine hierarchy. - - If the lowest level machine does not handle the event, then the - parent machine is referred to and so on, until there is only one - machine left which *must* handle the event. - - The machine whose ``process_event`` does not throw invalid state or - not found exceptions is expected to be the machine that should - continue handling events... - """ - while True: - machine = machines[-1] - try: - result = machine.process_event(event) - except (excp.InvalidState, excp.NotFound): - if len(machines) == 1: - raise - else: - current = machine._current - if current is not None and current.on_exit is not None: - current.on_exit(current.name, event) - machine._current = None - machines.pop() - else: - return result - - def run_iter(self, event, initialize=True): - """Returns a iterator/generator that will run the state machine. - - This will keep a stack (hierarchy) of machines active and jumps through - them as needed (depending on which machine handles which event) during - the running lifecycle. - - NOTE(harlowja): only one runner iterator/generator should be active for - a machine hierarchy, if this is not observed then it is possible for - initialization and other local state to be corrupted and causes issues - when running... - """ - machines = [self._machine] - if initialize: - machines[-1].initialize() - while True: - old_state = machines[-1].current_state - effect = self._process_event(machines, event) - new_state = machines[-1].current_state - try: - machine = effect.machine - except AttributeError: - pass - else: - if machine is not None and machine is not machines[-1]: - machine.initialize() - machines.append(machine) - try: - sent_event = yield (old_state, new_state) - except GeneratorExit: - break - if len(machines) == 1 and effect.terminal: - # Only allow the top level machine to actually terminate the - # execution, the rest of the nested machines must not handle - # events if they wish to have the root machine terminate... - break - if effect.reaction is None and sent_event is None: - raise excp.NotFound(_JUMPER_NOT_FOUND_TPL % (new_state, - old_state, - event)) - elif sent_event is not None: - event = sent_event - else: - cb, args, kwargs = effect.reaction - event = cb(old_state, new_state, event, *args, **kwargs) diff --git a/automaton/tests/__init__.py b/automaton/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/automaton/tests/test_fsm.py b/automaton/tests/test_fsm.py deleted file mode 100644 index bbdb9a8..0000000 --- a/automaton/tests/test_fsm.py +++ /dev/null @@ -1,465 +0,0 @@ -# Copyright (C) 2014 Yahoo! Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import collections -import functools -import random - -from automaton import exceptions as excp -from automaton import machines -from automaton import runners - -import six -from testtools import testcase - - -class FSMTest(testcase.TestCase): - - @staticmethod - def _create_fsm(start_state, add_start=True, add_states=None): - m = machines.FiniteMachine() - if add_start: - m.add_state(start_state) - m.default_start_state = start_state - if add_states: - for s in add_states: - if s in m: - continue - m.add_state(s) - return m - - def setUp(self): - super(FSMTest, self).setUp() - # NOTE(harlowja): this state machine will never stop if run() is used. - self.jumper = self._create_fsm("down", add_states=['up', 'down']) - self.jumper.add_transition('down', 'up', 'jump') - self.jumper.add_transition('up', 'down', 'fall') - self.jumper.add_reaction('up', 'jump', lambda *args: 'fall') - self.jumper.add_reaction('down', 'fall', lambda *args: 'jump') - - def test_build(self): - space = [] - for a in 'abc': - space.append(machines.State(a)) - m = machines.FiniteMachine.build(space) - for a in 'abc': - self.assertIn(a, m) - - def test_build_transitions(self): - space = [ - machines.State('down', is_terminal=False, - next_states={'jump': 'up'}), - machines.State('up', is_terminal=False, - next_states={'fall': 'down'}), - ] - m = machines.FiniteMachine.build(space) - m.default_start_state = 'down' - expected = [('down', 'jump', 'up'), ('up', 'fall', 'down')] - self.assertEqual(expected, list(m)) - - def test_build_transitions_with_callbacks(self): - entered = collections.defaultdict(list) - exitted = collections.defaultdict(list) - - def on_enter(state, event): - entered[state].append(event) - - def on_exit(state, event): - exitted[state].append(event) - - space = [ - machines.State('down', is_terminal=False, - next_states={'jump': 'up'}, - on_enter=on_enter, on_exit=on_exit), - machines.State('up', is_terminal=False, - next_states={'fall': 'down'}, - on_enter=on_enter, on_exit=on_exit), - ] - m = machines.FiniteMachine.build(space) - m.default_start_state = 'down' - expected = [('down', 'jump', 'up'), ('up', 'fall', 'down')] - self.assertEqual(expected, list(m)) - - m.initialize() - m.process_event('jump') - - self.assertEqual({'down': ['jump']}, dict(exitted)) - self.assertEqual({'up': ['jump']}, dict(entered)) - - m.process_event('fall') - - self.assertEqual({'down': ['jump'], 'up': ['fall']}, dict(exitted)) - self.assertEqual({'up': ['jump'], 'down': ['fall']}, dict(entered)) - - def test_build_transitions_dct(self): - space = [ - { - 'name': 'down', 'is_terminal': False, - 'next_states': {'jump': 'up'}, - }, - { - 'name': 'up', 'is_terminal': False, - 'next_states': {'fall': 'down'}, - }, - ] - m = machines.FiniteMachine.build(space) - m.default_start_state = 'down' - expected = [('down', 'jump', 'up'), ('up', 'fall', 'down')] - self.assertEqual(expected, list(m)) - - def test_build_terminal(self): - space = [ - machines.State('down', is_terminal=False, - next_states={'jump': 'fell_over'}), - machines.State('fell_over', is_terminal=True), - ] - m = machines.FiniteMachine.build(space) - m.default_start_state = 'down' - m.initialize() - m.process_event('jump') - self.assertTrue(m.terminated) - - def test_actionable(self): - self.jumper.initialize() - self.assertTrue(self.jumper.is_actionable_event('jump')) - self.assertFalse(self.jumper.is_actionable_event('fall')) - - def test_bad_start_state(self): - m = self._create_fsm('unknown', add_start=False) - r = runners.FiniteRunner(m) - self.assertRaises(excp.NotFound, r.run, 'unknown') - - def test_contains(self): - m = self._create_fsm('unknown', add_start=False) - self.assertNotIn('unknown', m) - m.add_state('unknown') - self.assertIn('unknown', m) - - def test_no_add_transition_terminal(self): - m = self._create_fsm('up') - m.add_state('down', terminal=True) - self.assertRaises(excp.InvalidState, - m.add_transition, 'down', 'up', 'jump') - - def test_duplicate_state(self): - m = self._create_fsm('unknown') - self.assertRaises(excp.Duplicate, m.add_state, 'unknown') - - def test_duplicate_transition(self): - m = self.jumper - m.add_state('side_ways') - self.assertRaises(excp.Duplicate, - m.add_transition, 'up', 'side_ways', 'fall') - - def test_duplicate_transition_replace(self): - m = self.jumper - m.add_state('side_ways') - m.add_transition('up', 'side_ways', 'fall', replace=True) - - def test_duplicate_transition_same_transition(self): - m = self.jumper - m.add_transition('up', 'down', 'fall') - - def test_duplicate_reaction(self): - self.assertRaises( - # Currently duplicate reactions are not allowed... - excp.Duplicate, - self.jumper.add_reaction, 'down', 'fall', lambda *args: 'skate') - - def test_bad_transition(self): - m = self._create_fsm('unknown') - m.add_state('fire') - self.assertRaises(excp.NotFound, m.add_transition, - 'unknown', 'something', 'boom') - self.assertRaises(excp.NotFound, m.add_transition, - 'something', 'unknown', 'boom') - - def test_bad_reaction(self): - m = self._create_fsm('unknown') - self.assertRaises(excp.NotFound, m.add_reaction, 'something', 'boom', - lambda *args: 'cough') - - def test_run(self): - m = self._create_fsm('down', add_states=['up', 'down']) - m.add_state('broken', terminal=True) - m.add_transition('down', 'up', 'jump') - m.add_transition('up', 'broken', 'hit-wall') - m.add_reaction('up', 'jump', lambda *args: 'hit-wall') - self.assertEqual(['broken', 'down', 'up'], sorted(m.states)) - self.assertEqual(2, m.events) - m.initialize() - self.assertEqual('down', m.current_state) - self.assertFalse(m.terminated) - r = runners.FiniteRunner(m) - r.run('jump') - self.assertTrue(m.terminated) - self.assertEqual('broken', m.current_state) - self.assertRaises(excp.InvalidState, r.run, - 'jump', initialize=False) - - def test_on_enter_on_exit(self): - enter_transitions = [] - exit_transitions = [] - - def on_exit(state, event): - exit_transitions.append((state, event)) - - def on_enter(state, event): - enter_transitions.append((state, event)) - - m = self._create_fsm('start', add_start=False) - m.add_state('start', on_exit=on_exit) - m.add_state('down', on_enter=on_enter, on_exit=on_exit) - m.add_state('up', on_enter=on_enter, on_exit=on_exit) - m.add_transition('start', 'down', 'beat') - m.add_transition('down', 'up', 'jump') - m.add_transition('up', 'down', 'fall') - - m.initialize('start') - m.process_event('beat') - m.process_event('jump') - m.process_event('fall') - self.assertEqual([('down', 'beat'), - ('up', 'jump'), ('down', 'fall')], enter_transitions) - self.assertEqual([('start', 'beat'), ('down', 'jump'), ('up', 'fall')], - exit_transitions) - - def test_run_iter(self): - up_downs = [] - runner = runners.FiniteRunner(self.jumper) - for (old_state, new_state) in runner.run_iter('jump'): - up_downs.append((old_state, new_state)) - if len(up_downs) >= 3: - break - self.assertEqual([('down', 'up'), ('up', 'down'), ('down', 'up')], - up_downs) - self.assertFalse(self.jumper.terminated) - self.assertEqual('up', self.jumper.current_state) - self.jumper.process_event('fall') - self.assertEqual('down', self.jumper.current_state) - - def test_run_send(self): - up_downs = [] - runner = runners.FiniteRunner(self.jumper) - it = runner.run_iter('jump') - while True: - up_downs.append(it.send(None)) - if len(up_downs) >= 3: - it.close() - break - self.assertEqual('up', self.jumper.current_state) - self.assertFalse(self.jumper.terminated) - self.assertEqual([('down', 'up'), ('up', 'down'), ('down', 'up')], - up_downs) - self.assertRaises(StopIteration, six.next, it) - - def test_run_send_fail(self): - up_downs = [] - runner = runners.FiniteRunner(self.jumper) - it = runner.run_iter('jump') - up_downs.append(six.next(it)) - self.assertRaises(excp.NotFound, it.send, 'fail') - it.close() - self.assertEqual([('down', 'up')], up_downs) - - def test_not_initialized(self): - self.assertRaises(excp.NotInitialized, - self.jumper.process_event, 'jump') - - def test_copy_states(self): - c = self._create_fsm('down', add_start=False) - self.assertEqual(0, len(c.states)) - d = c.copy() - c.add_state('up') - c.add_state('down') - self.assertEqual(2, len(c.states)) - self.assertEqual(0, len(d.states)) - - def test_copy_reactions(self): - c = self._create_fsm('down', add_start=False) - d = c.copy() - - c.add_state('down') - c.add_state('up') - c.add_reaction('down', 'jump', lambda *args: 'up') - c.add_transition('down', 'up', 'jump') - - self.assertEqual(1, c.events) - self.assertEqual(0, d.events) - self.assertNotIn('down', d) - self.assertNotIn('up', d) - self.assertEqual([], list(d)) - self.assertEqual([('down', 'jump', 'up')], list(c)) - - def test_copy_initialized(self): - j = self.jumper.copy() - self.assertIsNone(j.current_state) - r = runners.FiniteRunner(self.jumper) - - for i, transition in enumerate(r.run_iter('jump')): - if i == 4: - break - - self.assertIsNone(j.current_state) - self.assertIsNotNone(self.jumper.current_state) - - def test_iter(self): - transitions = list(self.jumper) - self.assertEqual(2, len(transitions)) - self.assertIn(('up', 'fall', 'down'), transitions) - self.assertIn(('down', 'jump', 'up'), transitions) - - def test_freeze(self): - self.jumper.freeze() - self.assertRaises(excp.FrozenMachine, self.jumper.add_state, 'test') - self.assertRaises(excp.FrozenMachine, - self.jumper.add_transition, 'test', 'test', 'test') - self.assertRaises(excp.FrozenMachine, - self.jumper.add_reaction, - 'test', 'test', lambda *args: 'test') - - def test_freeze_copy_unfreeze(self): - self.jumper.freeze() - self.assertTrue(self.jumper.frozen) - cp = self.jumper.copy(unfreeze=True) - self.assertTrue(self.jumper.frozen) - self.assertFalse(cp.frozen) - - def test_invalid_callbacks(self): - m = self._create_fsm('working', add_states=['working', 'broken']) - self.assertRaises(ValueError, m.add_state, 'b', on_enter=2) - self.assertRaises(ValueError, m.add_state, 'b', on_exit=2) - - -class HFSMTest(FSMTest): - - @staticmethod - def _create_fsm(start_state, - add_start=True, hierarchical=False, add_states=None): - if hierarchical: - m = machines.HierarchicalFiniteMachine() - else: - m = machines.FiniteMachine() - if add_start: - m.add_state(start_state) - m.default_start_state = start_state - if add_states: - for s in add_states: - if s not in m: - m.add_state(s) - return m - - def _make_phone_call(self, talk_time=1.0): - - def phone_reaction(old_state, new_state, event, chat_iter): - try: - six.next(chat_iter) - except StopIteration: - return 'finish' - else: - # Talk until the iterator expires... - return 'chat' - - talker = self._create_fsm("talk") - talker.add_transition("talk", "talk", "pickup") - talker.add_transition("talk", "talk", "chat") - talker.add_reaction("talk", "pickup", lambda *args: 'chat') - chat_iter = iter(list(range(0, 10))) - talker.add_reaction("talk", "chat", phone_reaction, chat_iter) - - handler = self._create_fsm('begin', hierarchical=True) - handler.add_state("phone", machine=talker) - handler.add_state('hangup', terminal=True) - handler.add_transition("begin", "phone", "call") - handler.add_reaction("phone", 'call', lambda *args: 'pickup') - handler.add_transition("phone", "hangup", "finish") - - return handler - - def _make_phone_dialer(self): - dialer = self._create_fsm("idle", hierarchical=True) - digits = self._create_fsm("idle") - - dialer.add_state("pickup", machine=digits) - dialer.add_transition("idle", "pickup", "dial") - dialer.add_reaction("pickup", "dial", lambda *args: 'press') - dialer.add_state("hangup", terminal=True) - - def react_to_press(last_state, new_state, event, number_calling): - if len(number_calling) >= 10: - return 'call' - else: - return 'press' - - digit_maker = functools.partial(random.randint, 0, 9) - number_calling = [] - digits.add_state( - "accumulate", - on_enter=lambda *args: number_calling.append(digit_maker())) - digits.add_transition("idle", "accumulate", "press") - digits.add_transition("accumulate", "accumulate", "press") - digits.add_reaction("accumulate", "press", - react_to_press, number_calling) - digits.add_state("dial", terminal=True) - digits.add_transition("accumulate", "dial", "call") - digits.add_reaction("dial", "call", lambda *args: 'ringing') - dialer.add_state("talk") - dialer.add_transition("pickup", "talk", "ringing") - dialer.add_reaction("talk", "ringing", lambda *args: 'hangup') - dialer.add_transition("talk", "hangup", 'hangup') - return dialer, number_calling - - def test_nested_machines(self): - dialer, _number_calling = self._make_phone_dialer() - self.assertEqual(1, len(dialer.nested_machines)) - - def test_nested_machine_initializers(self): - dialer, _number_calling = self._make_phone_dialer() - queried_for = [] - - def init_with(nested_machine): - queried_for.append(nested_machine) - return None - - dialer.initialize(nested_start_state_fetcher=init_with) - self.assertEqual(1, len(queried_for)) - - def test_phone_dialer_iter(self): - dialer, number_calling = self._make_phone_dialer() - self.assertEqual(0, len(number_calling)) - r = runners.HierarchicalRunner(dialer) - transitions = list(r.run_iter('dial')) - self.assertEqual(('talk', 'hangup'), transitions[-1]) - self.assertEqual(len(number_calling), - sum(1 if new_state == 'accumulate' else 0 - for (old_state, new_state) in transitions)) - self.assertEqual(10, len(number_calling)) - - def test_phone_call(self): - handler = self._make_phone_call() - r = runners.HierarchicalRunner(handler) - r.run('call') - self.assertTrue(handler.terminated) - - def test_phone_call_iter(self): - handler = self._make_phone_call() - r = runners.HierarchicalRunner(handler) - transitions = list(r.run_iter('call')) - self.assertEqual(('talk', 'hangup'), transitions[-1]) - self.assertEqual(("begin", 'phone'), transitions[0]) - talk_talk = 0 - for transition in transitions: - if transition == ("talk", "talk"): - talk_talk += 1 - self.assertGreater(talk_talk, 0) diff --git a/babel.cfg b/babel.cfg deleted file mode 100644 index 15cd6cb..0000000 --- a/babel.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[python: **.py] - diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100755 index c6ff40f..0000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,84 +0,0 @@ -# -*- coding: utf-8 -*- -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import sys - -sys.path.insert(0, os.path.abspath('../..')) -# -- General configuration ---------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.inheritance_diagram', - 'sphinx.ext.viewcode', - 'openstackdocstheme', -] - -# openstackdocstheme options -repository_name = 'openstack/automaton' -bug_project = 'automaton' -bug_tag = '' -html_last_updated_fmt = '%Y-%m-%d %H:%M' - -# autodoc generation is a bit aggressive and a nuisance when doing heavy -# text edit cycles. -# execute "export SPHINX_DEBUG=1" in your terminal to disable - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'automaton' -copyright = u'2013, OpenStack Foundation' - -# If true, '()' will be appended to :func: etc. cross-reference text. -add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -add_module_names = True - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -# html_theme_path = ["."] -# html_theme = '_theme' -# html_static_path = ['static'] -html_theme = 'openstackdocs' - -# Output file base name for HTML help builder. -htmlhelp_basename = '%sdoc' % project - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto/manual]). -latex_documents = [ - ('index', - '%s.tex' % project, - u'%s Documentation' % project, - u'OpenStack Foundation', 'manual'), -] - -# Example configuration for intersphinx: refer to the Python standard library. -#intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst deleted file mode 100644 index 3d4ceb4..0000000 --- a/doc/source/contributor/index.rst +++ /dev/null @@ -1,5 +0,0 @@ -============ -Contributing -============ - -.. include:: ../../../CONTRIBUTING.rst diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index 09a487c..0000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -======================================= - Welcome to automaton's documentation! -======================================= - -Friendly state machines for python. - -.. toctree:: - :maxdepth: 2 - - user/index - reference/index - install/index - contributor/index - -.. rubric:: Indices and tables - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/doc/source/install/index.rst b/doc/source/install/index.rst deleted file mode 100644 index 3620983..0000000 --- a/doc/source/install/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -============ -Installation -============ - -At the command line:: - - $ pip install automaton - -Or, if you have virtualenvwrapper installed:: - - $ mkvirtualenv automaton - $ pip install automaton diff --git a/doc/source/reference/index.rst b/doc/source/reference/index.rst deleted file mode 100644 index cf60b2b..0000000 --- a/doc/source/reference/index.rst +++ /dev/null @@ -1,51 +0,0 @@ -=== -API -=== - --------- -Machines --------- - -.. autoclass:: automaton.machines.State - :members: - -.. autoclass:: automaton.machines.FiniteMachine - :members: - :special-members: __iter__, __contains__ - -.. autoclass:: automaton.machines.HierarchicalFiniteMachine - :members: - -------- -Runners -------- - -.. autoclass:: automaton.runners.Runner - :members: - -.. autoclass:: automaton.runners.FiniteRunner - :members: - -.. autoclass:: automaton.runners.HierarchicalRunner - :members: - ----------- -Converters ----------- - -.. automodule:: automaton.converters.pydot - :members: - ----------- -Exceptions ----------- - -.. automodule:: automaton.exceptions - :members: - -Hierarchy ---------- - -.. inheritance-diagram:: - automaton.exceptions - :parts: 1 diff --git a/doc/source/user/examples.rst b/doc/source/user/examples.rst deleted file mode 100644 index 4da3c36..0000000 --- a/doc/source/user/examples.rst +++ /dev/null @@ -1,462 +0,0 @@ -======== -Examples -======== - -------------------------- -Creating a simple machine -------------------------- - -.. testcode:: - - from automaton import machines - m = machines.FiniteMachine() - m.add_state('up') - m.add_state('down') - m.add_transition('down', 'up', 'jump') - m.add_transition('up', 'down', 'fall') - m.default_start_state = 'down' - print(m.pformat()) - -**Expected output:** - -.. testoutput:: - - +---------+-------+------+----------+---------+ - | Start | Event | End | On Enter | On Exit | - +---------+-------+------+----------+---------+ - | down[^] | jump | up | . | . | - | up | fall | down | . | . | - +---------+-------+------+----------+---------+ - ------------------------------- -Transitioning a simple machine ------------------------------- - -.. testcode:: - - m.initialize() - m.process_event('jump') - print(m.pformat()) - print(m.current_state) - print(m.terminated) - m.process_event('fall') - print(m.pformat()) - print(m.current_state) - print(m.terminated) - -**Expected output:** - -.. testoutput:: - - +---------+-------+------+----------+---------+ - | Start | Event | End | On Enter | On Exit | - +---------+-------+------+----------+---------+ - | down[^] | jump | up | . | . | - | @up | fall | down | . | . | - +---------+-------+------+----------+---------+ - up - False - +----------+-------+------+----------+---------+ - | Start | Event | End | On Enter | On Exit | - +----------+-------+------+----------+---------+ - | @down[^] | jump | up | . | . | - | up | fall | down | . | . | - +----------+-------+------+----------+---------+ - down - False - - -------------------------------------- -Running a complex dog-barking machine -------------------------------------- - -.. testcode:: - - from automaton import machines - from automaton import runners - - - # These reaction functions will get triggered when the registered state - # and event occur, it is expected to provide a new event that reacts to the - # new stable state (so that the state-machine can transition to a new - # stable state, and repeat, until the machine ends up in a terminal - # state, whereby it will stop...) - - def react_to_squirrel(old_state, new_state, event_that_triggered): - return "gets petted" - - - def react_to_wagging(old_state, new_state, event_that_triggered): - return "gets petted" - - - m = machines.FiniteMachine() - - m.add_state("sits") - m.add_state("lies down", terminal=True) - m.add_state("barks") - m.add_state("wags tail") - - m.default_start_state = 'sits' - - m.add_transition("sits", "barks", "squirrel!") - m.add_transition("barks", "wags tail", "gets petted") - m.add_transition("wags tail", "lies down", "gets petted") - - m.add_reaction("barks", "squirrel!", react_to_squirrel) - m.add_reaction('wags tail', "gets petted", react_to_wagging) - - print(m.pformat()) - r = runners.FiniteRunner(m) - for (old_state, new_state) in r.run_iter("squirrel!"): - print("Leaving '%s'" % old_state) - print("Entered '%s'" % new_state) - -**Expected output:** - -.. testoutput:: - - +--------------+-------------+-----------+----------+---------+ - | Start | Event | End | On Enter | On Exit | - +--------------+-------------+-----------+----------+---------+ - | barks | gets petted | wags tail | . | . | - | lies down[$] | . | . | . | . | - | sits[^] | squirrel! | barks | . | . | - | wags tail | gets petted | lies down | . | . | - +--------------+-------------+-----------+----------+---------+ - Leaving 'sits' - Entered 'barks' - Leaving 'barks' - Entered 'wags tail' - Leaving 'wags tail' - Entered 'lies down' - - ------------------------------------- -Creating a complex CD-player machine ------------------------------------- - -.. testcode:: - - from automaton import machines - - - def print_on_enter(new_state, triggered_event): - print("Entered '%s' due to '%s'" % (new_state, triggered_event)) - - - def print_on_exit(old_state, triggered_event): - print("Exiting '%s' due to '%s'" % (old_state, triggered_event)) - - - m = machines.FiniteMachine() - - m.add_state('stopped', on_enter=print_on_enter, on_exit=print_on_exit) - m.add_state('opened', on_enter=print_on_enter, on_exit=print_on_exit) - m.add_state('closed', on_enter=print_on_enter, on_exit=print_on_exit) - m.add_state('playing', on_enter=print_on_enter, on_exit=print_on_exit) - m.add_state('paused', on_enter=print_on_enter, on_exit=print_on_exit) - - m.add_transition('stopped', 'playing', 'play') - m.add_transition('stopped', 'opened', 'open_close') - m.add_transition('stopped', 'stopped', 'stop') - - m.add_transition('opened', 'closed', 'open_close') - - m.add_transition('closed', 'opened', 'open_close') - m.add_transition('closed', 'stopped', 'cd_detected') - - m.add_transition('playing', 'stopped', 'stop') - m.add_transition('playing', 'paused', 'pause') - m.add_transition('playing', 'opened', 'open_close') - - m.add_transition('paused', 'playing', 'play') - m.add_transition('paused', 'stopped', 'stop') - m.add_transition('paused', 'opened', 'open_close') - - m.default_start_state = 'closed' - - m.initialize() - print(m.pformat()) - - for event in ['cd_detected', 'play', 'pause', 'play', 'stop', - 'open_close', 'open_close']: - m.process_event(event) - print(m.pformat()) - print("=============") - print("Current state => %s" % m.current_state) - print("=============") - - - -**Expected output:** - -.. testoutput:: - - +------------+-------------+---------+----------------+---------------+ - | Start | Event | End | On Enter | On Exit | - +------------+-------------+---------+----------------+---------------+ - | @closed[^] | cd_detected | stopped | print_on_enter | print_on_exit | - | @closed[^] | open_close | opened | print_on_enter | print_on_exit | - | opened | open_close | closed | print_on_enter | print_on_exit | - | paused | open_close | opened | print_on_enter | print_on_exit | - | paused | play | playing | print_on_enter | print_on_exit | - | paused | stop | stopped | print_on_enter | print_on_exit | - | playing | open_close | opened | print_on_enter | print_on_exit | - | playing | pause | paused | print_on_enter | print_on_exit | - | playing | stop | stopped | print_on_enter | print_on_exit | - | stopped | open_close | opened | print_on_enter | print_on_exit | - | stopped | play | playing | print_on_enter | print_on_exit | - | stopped | stop | stopped | print_on_enter | print_on_exit | - +------------+-------------+---------+----------------+---------------+ - Exiting 'closed' due to 'cd_detected' - Entered 'stopped' due to 'cd_detected' - +-----------+-------------+---------+----------------+---------------+ - | Start | Event | End | On Enter | On Exit | - +-----------+-------------+---------+----------------+---------------+ - | closed[^] | cd_detected | stopped | print_on_enter | print_on_exit | - | closed[^] | open_close | opened | print_on_enter | print_on_exit | - | opened | open_close | closed | print_on_enter | print_on_exit | - | paused | open_close | opened | print_on_enter | print_on_exit | - | paused | play | playing | print_on_enter | print_on_exit | - | paused | stop | stopped | print_on_enter | print_on_exit | - | playing | open_close | opened | print_on_enter | print_on_exit | - | playing | pause | paused | print_on_enter | print_on_exit | - | playing | stop | stopped | print_on_enter | print_on_exit | - | @stopped | open_close | opened | print_on_enter | print_on_exit | - | @stopped | play | playing | print_on_enter | print_on_exit | - | @stopped | stop | stopped | print_on_enter | print_on_exit | - +-----------+-------------+---------+----------------+---------------+ - ============= - Current state => stopped - ============= - Exiting 'stopped' due to 'play' - Entered 'playing' due to 'play' - +-----------+-------------+---------+----------------+---------------+ - | Start | Event | End | On Enter | On Exit | - +-----------+-------------+---------+----------------+---------------+ - | closed[^] | cd_detected | stopped | print_on_enter | print_on_exit | - | closed[^] | open_close | opened | print_on_enter | print_on_exit | - | opened | open_close | closed | print_on_enter | print_on_exit | - | paused | open_close | opened | print_on_enter | print_on_exit | - | paused | play | playing | print_on_enter | print_on_exit | - | paused | stop | stopped | print_on_enter | print_on_exit | - | @playing | open_close | opened | print_on_enter | print_on_exit | - | @playing | pause | paused | print_on_enter | print_on_exit | - | @playing | stop | stopped | print_on_enter | print_on_exit | - | stopped | open_close | opened | print_on_enter | print_on_exit | - | stopped | play | playing | print_on_enter | print_on_exit | - | stopped | stop | stopped | print_on_enter | print_on_exit | - +-----------+-------------+---------+----------------+---------------+ - ============= - Current state => playing - ============= - Exiting 'playing' due to 'pause' - Entered 'paused' due to 'pause' - +-----------+-------------+---------+----------------+---------------+ - | Start | Event | End | On Enter | On Exit | - +-----------+-------------+---------+----------------+---------------+ - | closed[^] | cd_detected | stopped | print_on_enter | print_on_exit | - | closed[^] | open_close | opened | print_on_enter | print_on_exit | - | opened | open_close | closed | print_on_enter | print_on_exit | - | @paused | open_close | opened | print_on_enter | print_on_exit | - | @paused | play | playing | print_on_enter | print_on_exit | - | @paused | stop | stopped | print_on_enter | print_on_exit | - | playing | open_close | opened | print_on_enter | print_on_exit | - | playing | pause | paused | print_on_enter | print_on_exit | - | playing | stop | stopped | print_on_enter | print_on_exit | - | stopped | open_close | opened | print_on_enter | print_on_exit | - | stopped | play | playing | print_on_enter | print_on_exit | - | stopped | stop | stopped | print_on_enter | print_on_exit | - +-----------+-------------+---------+----------------+---------------+ - ============= - Current state => paused - ============= - Exiting 'paused' due to 'play' - Entered 'playing' due to 'play' - +-----------+-------------+---------+----------------+---------------+ - | Start | Event | End | On Enter | On Exit | - +-----------+-------------+---------+----------------+---------------+ - | closed[^] | cd_detected | stopped | print_on_enter | print_on_exit | - | closed[^] | open_close | opened | print_on_enter | print_on_exit | - | opened | open_close | closed | print_on_enter | print_on_exit | - | paused | open_close | opened | print_on_enter | print_on_exit | - | paused | play | playing | print_on_enter | print_on_exit | - | paused | stop | stopped | print_on_enter | print_on_exit | - | @playing | open_close | opened | print_on_enter | print_on_exit | - | @playing | pause | paused | print_on_enter | print_on_exit | - | @playing | stop | stopped | print_on_enter | print_on_exit | - | stopped | open_close | opened | print_on_enter | print_on_exit | - | stopped | play | playing | print_on_enter | print_on_exit | - | stopped | stop | stopped | print_on_enter | print_on_exit | - +-----------+-------------+---------+----------------+---------------+ - ============= - Current state => playing - ============= - Exiting 'playing' due to 'stop' - Entered 'stopped' due to 'stop' - +-----------+-------------+---------+----------------+---------------+ - | Start | Event | End | On Enter | On Exit | - +-----------+-------------+---------+----------------+---------------+ - | closed[^] | cd_detected | stopped | print_on_enter | print_on_exit | - | closed[^] | open_close | opened | print_on_enter | print_on_exit | - | opened | open_close | closed | print_on_enter | print_on_exit | - | paused | open_close | opened | print_on_enter | print_on_exit | - | paused | play | playing | print_on_enter | print_on_exit | - | paused | stop | stopped | print_on_enter | print_on_exit | - | playing | open_close | opened | print_on_enter | print_on_exit | - | playing | pause | paused | print_on_enter | print_on_exit | - | playing | stop | stopped | print_on_enter | print_on_exit | - | @stopped | open_close | opened | print_on_enter | print_on_exit | - | @stopped | play | playing | print_on_enter | print_on_exit | - | @stopped | stop | stopped | print_on_enter | print_on_exit | - +-----------+-------------+---------+----------------+---------------+ - ============= - Current state => stopped - ============= - Exiting 'stopped' due to 'open_close' - Entered 'opened' due to 'open_close' - +-----------+-------------+---------+----------------+---------------+ - | Start | Event | End | On Enter | On Exit | - +-----------+-------------+---------+----------------+---------------+ - | closed[^] | cd_detected | stopped | print_on_enter | print_on_exit | - | closed[^] | open_close | opened | print_on_enter | print_on_exit | - | @opened | open_close | closed | print_on_enter | print_on_exit | - | paused | open_close | opened | print_on_enter | print_on_exit | - | paused | play | playing | print_on_enter | print_on_exit | - | paused | stop | stopped | print_on_enter | print_on_exit | - | playing | open_close | opened | print_on_enter | print_on_exit | - | playing | pause | paused | print_on_enter | print_on_exit | - | playing | stop | stopped | print_on_enter | print_on_exit | - | stopped | open_close | opened | print_on_enter | print_on_exit | - | stopped | play | playing | print_on_enter | print_on_exit | - | stopped | stop | stopped | print_on_enter | print_on_exit | - +-----------+-------------+---------+----------------+---------------+ - ============= - Current state => opened - ============= - Exiting 'opened' due to 'open_close' - Entered 'closed' due to 'open_close' - +------------+-------------+---------+----------------+---------------+ - | Start | Event | End | On Enter | On Exit | - +------------+-------------+---------+----------------+---------------+ - | @closed[^] | cd_detected | stopped | print_on_enter | print_on_exit | - | @closed[^] | open_close | opened | print_on_enter | print_on_exit | - | opened | open_close | closed | print_on_enter | print_on_exit | - | paused | open_close | opened | print_on_enter | print_on_exit | - | paused | play | playing | print_on_enter | print_on_exit | - | paused | stop | stopped | print_on_enter | print_on_exit | - | playing | open_close | opened | print_on_enter | print_on_exit | - | playing | pause | paused | print_on_enter | print_on_exit | - | playing | stop | stopped | print_on_enter | print_on_exit | - | stopped | open_close | opened | print_on_enter | print_on_exit | - | stopped | play | playing | print_on_enter | print_on_exit | - | stopped | stop | stopped | print_on_enter | print_on_exit | - +------------+-------------+---------+----------------+---------------+ - ============= - Current state => closed - ============= - ----------------------------------------------------------- -Creating a complex CD-player machine (using a state-space) ----------------------------------------------------------- - -This example is equivalent to the prior one but creates a machine in -a more declarative manner. Instead of calling ``add_state`` -and ``add_transition`` a explicit and declarative format can be used. For -example to create the same machine: - -.. testcode:: - - from automaton import machines - - - def print_on_enter(new_state, triggered_event): - print("Entered '%s' due to '%s'" % (new_state, triggered_event)) - - - def print_on_exit(old_state, triggered_event): - print("Exiting '%s' due to '%s'" % (old_state, triggered_event)) - - # This will contain all the states and transitions that our machine will - # allow, the format is relatively simple and designed to be easy to use. - state_space = [ - { - 'name': 'stopped', - 'next_states': { - # On event 'play' transition to the 'playing' state. - 'play': 'playing', - 'open_close': 'opened', - 'stop': 'stopped', - }, - 'on_enter': print_on_enter, - 'on_exit': print_on_exit, - }, - { - 'name': 'opened', - 'next_states': { - 'open_close': 'closed', - }, - 'on_enter': print_on_enter, - 'on_exit': print_on_exit, - }, - { - 'name': 'closed', - 'next_states': { - 'open_close': 'opened', - 'cd_detected': 'stopped', - }, - 'on_enter': print_on_enter, - 'on_exit': print_on_exit, - }, - { - 'name': 'playing', - 'next_states': { - 'stop': 'stopped', - 'pause': 'paused', - 'open_close': 'opened', - }, - 'on_enter': print_on_enter, - 'on_exit': print_on_exit, - }, - { - 'name': 'paused', - 'next_states': { - 'play': 'playing', - 'stop': 'stopped', - 'open_close': 'opened', - }, - 'on_enter': print_on_enter, - 'on_exit': print_on_exit, - }, - ] - - m = machines.FiniteMachine.build(state_space) - m.default_start_state = 'closed' - print(m.pformat()) - -**Expected output:** - -.. testoutput:: - - +-----------+-------------+---------+----------------+---------------+ - | Start | Event | End | On Enter | On Exit | - +-----------+-------------+---------+----------------+---------------+ - | closed[^] | cd_detected | stopped | print_on_enter | print_on_exit | - | closed[^] | open_close | opened | print_on_enter | print_on_exit | - | opened | open_close | closed | print_on_enter | print_on_exit | - | paused | open_close | opened | print_on_enter | print_on_exit | - | paused | play | playing | print_on_enter | print_on_exit | - | paused | stop | stopped | print_on_enter | print_on_exit | - | playing | open_close | opened | print_on_enter | print_on_exit | - | playing | pause | paused | print_on_enter | print_on_exit | - | playing | stop | stopped | print_on_enter | print_on_exit | - | stopped | open_close | opened | print_on_enter | print_on_exit | - | stopped | play | playing | print_on_enter | print_on_exit | - | stopped | stop | stopped | print_on_enter | print_on_exit | - +-----------+-------------+---------+----------------+---------------+ - -.. note:: - - As can be seen the two tables from this example and the prior one are - exactly the same. diff --git a/doc/source/user/features.rst b/doc/source/user/features.rst deleted file mode 100644 index 67a0d23..0000000 --- a/doc/source/user/features.rst +++ /dev/null @@ -1,17 +0,0 @@ -======== -Features -======== - -Machines --------- - -* A :py:class:`.automaton.machines.FiniteMachine` state machine. -* A :py:class:`.automaton.machines.HierarchicalFiniteMachine` hierarchical - state machine. - -Runners -------- - -* A :py:class:`.automaton.runners.FiniteRunner` state machine runner. -* A :py:class:`.automaton.runners.HierarchicalRunner` hierarchical state - machine runner. diff --git a/doc/source/user/history.rst b/doc/source/user/history.rst deleted file mode 100644 index 2632296..0000000 --- a/doc/source/user/history.rst +++ /dev/null @@ -1,2 +0,0 @@ -.. include:: ../../../ChangeLog - diff --git a/doc/source/user/index.rst b/doc/source/user/index.rst deleted file mode 100644 index f109e35..0000000 --- a/doc/source/user/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -====================== - automaton User Guide -====================== - -.. toctree:: - :maxdepth: 2 - - features - examples - history diff --git a/releasenotes/notes/.placeholder b/releasenotes/notes/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/releasenotes/source/_static/.placeholder b/releasenotes/source/_static/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/releasenotes/source/_templates/.placeholder b/releasenotes/source/_templates/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py deleted file mode 100644 index 68ed7e6..0000000 --- a/releasenotes/source/conf.py +++ /dev/null @@ -1,279 +0,0 @@ -# -*- coding: utf-8 -*- -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# automaton Release Notes documentation build configuration file, created by -# sphinx-quickstart on Tue Nov 3 17:40:50 2015. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'openstackdocstheme', - 'reno.sphinxext', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'automaton Release Notes' -copyright = u'2016, automaton Developers' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -import pkg_resources -release = pkg_resources.get_distribution('automaton').version -# The short X.Y version. -version = release - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'openstackdocs' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'automatonReleaseNotesdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # 'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'automatonReleaseNotes.tex', - u'automaton Release Notes Documentation', - u'automaton Developers', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'automatonreleasenotes', - u'automaton Release Notes Documentation', - [u'automaton Developers'], 1) -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'automatonReleaseNotes', - u'automaton Release Notes Documentation', - u'automaton Developers', 'automatonReleaseNotes', - 'An OpenStack library for parsing configuration options from the command' - ' line and configuration files.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# texinfo_no_detailmenu = False - -# -- Options for Internationalization output ------------------------------ -locale_dirs = ['locale/'] diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst deleted file mode 100644 index 14c3d77..0000000 --- a/releasenotes/source/index.rst +++ /dev/null @@ -1,9 +0,0 @@ -=========================== - automaton Release Notes -=========================== - - .. toctree:: - :maxdepth: 1 - - unreleased - ocata diff --git a/releasenotes/source/ocata.rst b/releasenotes/source/ocata.rst deleted file mode 100644 index ebe62f4..0000000 --- a/releasenotes/source/ocata.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Ocata Series Release Notes -=================================== - -.. release-notes:: - :branch: origin/stable/ocata diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst deleted file mode 100644 index 5860a46..0000000 --- a/releasenotes/source/unreleased.rst +++ /dev/null @@ -1,5 +0,0 @@ -========================== - Unreleased Release Notes -========================== - -.. release-notes:: diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 35f7353..0000000 --- a/requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -# See: https://bugs.launchpad.net/pbr/+bug/1384919 for why this is here... -pbr!=2.1.0,>=2.0.0 # Apache-2.0 - -# Python 2->3 compatibility library. -six>=1.9.0 # MIT - -# For deprecation of things -debtcollector>=1.2.0 # Apache-2.0 - -# For pretty formatting machines/state tables... -PrettyTable<0.8,>=0.7.1 # BSD diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 5f1b0f4..0000000 --- a/setup.cfg +++ /dev/null @@ -1,42 +0,0 @@ -[metadata] -name = automaton -summary = Friendly state machines for python. -author = OpenStack -author-email = openstack-dev@lists.openstack.org -home-page = https://docs.openstack.org/automaton/latest/ -description-file = - README.rst -classifier = - Development Status :: 3 - Alpha - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Operating System :: POSIX - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 - Topic :: Software Development :: Libraries - -[global] -setup-hooks = - pbr.hooks.setup_hook - -[files] -packages = - automaton - -[nosetests] -cover-erase = true -verbosity = 2 - -[wheel] -universal = 1 - -[build_sphinx] -source-dir = doc/source -build-dir = doc/build -all_files = 1 -warning-is-error = 1 - -[upload_sphinx] -upload-dir = doc/build/html diff --git a/setup.py b/setup.py deleted file mode 100644 index 566d844..0000000 --- a/setup.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT -import setuptools - -# In python < 2.7.4, a lazy loading of package `pbr` will break -# setuptools if some other modules registered functions in `atexit`. -# solution from: http://bugs.python.org/issue15881#msg170215 -try: - import multiprocessing # noqa -except ImportError: - pass - -setuptools.setup( - setup_requires=['pbr>=2.0.0'], - pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index b04212a..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -hacking<0.11,>=0.10.0 - -doc8 # Apache-2.0 -coverage!=4.4,>=4.0 # Apache-2.0 -python-subunit>=0.0.18 # Apache-2.0/BSD -sphinx>=1.6.2 # BSD -openstackdocstheme>=1.11.0 # Apache-2.0 -oslotest>=1.10.0 # Apache-2.0 -testrepository>=0.0.18 # Apache-2.0/BSD -testtools>=1.4.0 # MIT -reno!=2.3.1,>=1.8.0 # Apache-2.0 diff --git a/tools/tox_install.sh b/tools/tox_install.sh deleted file mode 100755 index e61b63a..0000000 --- a/tools/tox_install.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -# Client constraint file contains this client version pin that is in conflict -# with installing the client from source. We should remove the version pin in -# the constraints file before applying it for from-source installation. - -CONSTRAINTS_FILE="$1" -shift 1 - -set -e - -# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get -# published to logs.openstack.org for easy debugging. -localfile="$VIRTUAL_ENV/log/upper-constraints.txt" - -if [[ "$CONSTRAINTS_FILE" != http* ]]; then - CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE" -fi -# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep -curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile" - -pip install -c"$localfile" openstack-requirements - -# This is the main purpose of the script: Allow local installation of -# the current repo. It is listed in constraints file and thus any -# install will be constrained and we need to unconstrain it. -edit-constraints "$localfile" -- "$CLIENT_NAME" - -pip install -c"$localfile" -U "$@" -exit $? diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 4a2877a..0000000 --- a/tox.ini +++ /dev/null @@ -1,39 +0,0 @@ -[tox] -minversion = 2.0 -envlist = py35,py27,pypy,docs,pep8,venv - -[testenv:docs] -basepython = python2.7 -commands = python setup.py build_sphinx - -[testenv] -setenv = - VIRTUAL_ENV={envdir} - BRANCH_NAME=master - CLIENT_NAME=automaton -install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} -deps = -r{toxinidir}/test-requirements.txt -commands = python setup.py test --slowest --testr-args='{posargs}' - -[testenv:pep8] -commands = flake8 {posargs} - -[testenv:py27] -commands = - python setup.py testr --slowest --testr-args='{posargs}' - sphinx-build -b doctest doc/source doc/build - doc8 --ignore-path "doc/source/history.rst" doc/source - -[testenv:venv] -basepython = python2.7 -commands = {posargs} - -[flake8] -show-source = True -exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build - -[testenv:cover] -commands = python setup.py test --coverage --testr-args="{posargs}" - -[testenv:releasenotes] -commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html