Metadata-Version: 1.1
Name: python-statemachine
Version: 0.9.0
Summary: Python Finite State Machines made easy.
Home-page: https://github.com/fgmacedo/python-statemachine
Author: Fernando Macedo
Author-email: fgmacedo@gmail.com
License: MIT license
Description: ====================
        Python State Machine
        ====================
        
        
        .. image:: https://img.shields.io/pypi/v/python-statemachine.svg
                :target: https://pypi.python.org/pypi/python-statemachine
        
        .. image:: https://img.shields.io/pypi/dm/python-statemachine.svg
                :target: https://pypi.python.org/pypi/python-statemachine
        
        .. image:: https://travis-ci.org/fgmacedo/python-statemachine.svg?branch=develop
                :target: https://travis-ci.org/fgmacedo/python-statemachine
                :alt: Build status
        
        .. image:: https://codecov.io/gh/fgmacedo/python-statemachine/branch/develop/graph/badge.svg
                :target: https://codecov.io/gh/fgmacedo/python-statemachine
                :alt: Coverage report
        
        .. image:: https://readthedocs.org/projects/python-statemachine/badge/?version=latest
                :target: https://python-statemachine.readthedocs.io/en/latest/?badge=latest
                :alt: Documentation Status
        
        
        Python `finite-state machines <https://en.wikipedia.org/wiki/Finite-state_machine>`_ made easy.
        
        
        * Free software: MIT license
        * Documentation: https://python-statemachine.readthedocs.io.
        
        
        Getting started
        ===============
        
        To install Python State Machine, run this command in your terminal:
        
        .. code-block:: console
        
            $ pip install python-statemachine
        
        Define your state machine:
        
        >>> from statemachine import StateMachine, State
        
        >>> class TrafficLightMachine(StateMachine):
        ...    green = State('Green', initial=True)
        ...    yellow = State('Yellow')
        ...    red = State('Red')
        ...
        ...    slowdown = green.to(yellow)
        ...    stop = yellow.to(red)
        ...    go = red.to(green)
        
        
        You can now create an instance:
        
        >>> traffic_light = TrafficLightMachine()
        
        And inspect about the current state:
        
        >>> traffic_light.current_state
        State('Green', identifier='green', value='green', initial=True, final=False)
        >>> traffic_light.current_state == TrafficLightMachine.green == traffic_light.green
        True
        
        For each state, there's a dynamically created property in the form ``is_<state.identifier>``, that
        returns ``True`` if the current status matches the query:
        
        >>> traffic_light.is_green
        True
        >>> traffic_light.is_yellow
        False
        >>> traffic_light.is_red
        False
        
        Query about metadata:
        
        >>> [s.identifier for s in traffic_light.states]
        ['green', 'red', 'yellow']
        >>> [t.identifier for t in traffic_light.transitions]
        ['go', 'slowdown', 'stop']
        
        Call a transition:
        
        >>> traffic_light.slowdown()
        
        And check for the current status:
        
        >>> traffic_light.current_state
        State('Yellow', identifier='yellow', value='yellow', initial=False, final=False)
        >>> traffic_light.is_yellow
        True
        
        You can't run a transition from an invalid state:
        
        >>> traffic_light.is_yellow
        True
        >>> traffic_light.slowdown()
        Traceback (most recent call last):
        statemachine.exceptions.TransitionNotAllowed: Can't slowdown when in Yellow.
        
        You can also trigger events in an alternative way, calling the ``run(<transition.identificer>)`` method:
        
        >>> traffic_light.is_yellow
        True
        >>> traffic_light.run('stop')
        >>> traffic_light.is_red
        True
        
        A state machine can be instantiated with an initial value:
        
        >>> machine = TrafficLightMachine(start_value='red')
        >>> traffic_light.is_red
        True
        
        
        Models
        ------
        
        If you need to persist the current state on another object, or you're using the
        state machine to control the flow of another object, you can pass this object
        to the ``StateMachine`` constructor:
        
        >>> class MyModel(object):
        ...     def __init__(self, state):
        ...         self.state = state
        ...
        >>> obj = MyModel(state='red')
        >>> traffic_light = TrafficLightMachine(obj)
        >>> traffic_light.is_red
        True
        >>> obj.state
        'red'
        >>> obj.state = 'green'
        >>> traffic_light.is_green
        True
        >>> traffic_light.slowdown()
        >>> obj.state
        'yellow'
        >>> traffic_light.is_yellow
        True
        
        
        Callbacks
        ---------
        
        Callbacks when running events:
        
        
        >>> from statemachine import StateMachine, State
        
        >>> class TrafficLightMachine(StateMachine):
        ...     "A traffic light machine"
        ...     green = State('Green', initial=True)
        ...     yellow = State('Yellow')
        ...     red = State('Red')
        ...
        ...     slowdown = green.to(yellow)
        ...     stop = yellow.to(red)
        ...     go = red.to(green)
        ...
        ...     def on_slowdown(self):
        ...         print('Calma, lá!')
        ...
        ...     def on_stop(self):
        ...         print('Parou.')
        ...
        ...     def on_go(self):
        ...         print('Valendo!')
        
        
        >>> stm = TrafficLightMachine()
        >>> stm.slowdown()
        Calma, lá!
        >>> stm.stop()
        Parou.
        >>> stm.go()
        Valendo!
        
        
        Or when entering/exiting states:
        
        >>> from statemachine import StateMachine, State
        
        >>> class TrafficLightMachine(StateMachine):
        ...    "A traffic light machine"
        ...    green = State('Green', initial=True)
        ...    yellow = State('Yellow')
        ...    red = State('Red')
        ...
        ...    cycle = green.to(yellow) | yellow.to(red) | red.to(green)
        ...
        ...    def on_enter_green(self):
        ...        print('Valendo!')
        ...
        ...    def on_enter_yellow(self):
        ...        print('Calma, lá!')
        ...
        ...    def on_enter_red(self):
        ...        print('Parou.')
        
        >>> stm = TrafficLightMachine()
        >>> stm.cycle()
        Calma, lá!
        >>> stm.cycle()
        Parou.
        >>> stm.cycle()
        Valendo!
        
        Mixins
        ------
        
        Your model can inherited from a custom mixin to auto-instantiate a state machine.
        
        >>> from statemachine.mixins import MachineMixin
        
        >>> class CampaignMachineWithKeys(StateMachine):
        ...     "A workflow machine"
        ...     draft = State('Draft', initial=True, value=1)
        ...     producing = State('Being produced', value=2)
        ...     closed = State('Closed', value=3)
        ...     cancelled = State('Cancelled', value=4)
        ...
        ...     add_job = draft.to.itself() | producing.to.itself()
        ...     produce = draft.to(producing)
        ...     deliver = producing.to(closed)
        ...     cancel = cancelled.from_(draft, producing)
        
        
        >>> class MyModel(MachineMixin):
        ...     state_machine_name = 'CampaignMachineWithKeys'
        ...
        ...     def __init__(self, **kwargs):
        ...         for k, v in kwargs.items():
        ...             setattr(self, k, v)
        ...         super(MyModel, self).__init__()
        ...
        ...     def __repr__(self):
        ...         return "{}({!r})".format(type(self).__name__, self.__dict__)
        
        >>> model = MyModel(state=1)
        >>> assert isinstance(model.statemachine, CampaignMachineWithKeys)
        >>> assert model.state == 1
        >>> assert model.statemachine.current_state == model.statemachine.draft
        >>> model.statemachine.cancel()
        >>> assert model.state == 4
        >>> assert model.statemachine.current_state == model.statemachine.cancelled
        
        Final States
        ------------
        
        You can explicitly set final states.
        Transitions from these states are not allowed and will raise exception.
        
        >>> class CampaignMachine(StateMachine):
        ...     "A workflow machine"
        ...     draft = State('Draft', initial=True, value=1)
        ...     producing = State('Being produced', value=2)
        ...     closed = State('Closed', final=True, value=3)
        ...
        ...     add_job = draft.to.itself() | producing.to.itself() | closed.to(producing)
        ...     produce = draft.to(producing)
        ...     deliver = producing.to(closed)
        
        
        >>> from statemachine.exceptions import InvalidDefinition
        >>> try:
        ...     machine = CampaignMachine(model)
        ... except InvalidDefinition as err:
        ...     print(err)
        Final state does not should have defined transitions starting from that state
        
        
        You can retrieve all final states.
        
        >>> class CampaignMachine(StateMachine):
        ...     "A workflow machine"
        ...     draft = State('Draft', initial=True, value=1)
        ...     producing = State('Being produced', value=2)
        ...     closed = State('Closed', final=True, value=3)
        ...
        ...     add_job = draft.to.itself() | producing.to.itself()
        ...     produce = draft.to(producing)
        ...     deliver = producing.to(closed)
        
        >>> model = MyModel(state=3)
        >>> machine = CampaignMachine(model)
        >>> machine.final_states
        [State('Closed', identifier='closed', value=3, initial=False, final=True)]
        
        
        
        History
        =======
        
        0.9.0 (2022-12-21)
        ------------------
        
        * This is the last release supporting Python2.X series.
        * Doctesting all documentation including README (with issues on examples fixed).
        * Fix state value misjudged when state value is an "boolean False value" (tks @the5fire)
        * Fix returning dict as result of transitions callback.
        * State machine declarations now with final states.
        * Args and kwargs now are passed to bounded transitions.
        
        
        0.8.0 (2020-01-23)
        ------------------
        
        * Add support for Python 3.7 and 3.8 (adding to test matrix).
        * Update development requirements.
        * State machine names should now be fully qualified for mixins, simple names are deprecated and
          will no longer be supported on a future version.
        * Development: Adding mypy linter.
        * Add support for State machine inheritance. Thanks @rschrader.
        * Add support for reverse transitions: ``transition = state_a.from_(state_b)``.
          Thanks @romulorosa.
        * Fix current state equal to destination on enter events. Thanks @robnils and @joshuacc1.
        * Check: StateMachine now validates if it's states/transitions graph has only one component.
          Thanks @rafaelrds.
        
        Breaking changes:
        
        * Drop official support for Python 3.4 (removing from test matrix, code may still work).
        
        
        0.7.1 (2019-01-18)
        ------------------
        
        * Fix Django integration for registry loading statemachine modules on Django1.7+.
        
        
        0.7.0 (2018-04-01)
        ------------------
        
        * New event callbacks: `on_enter_<state>` and `on_exit_<state>`.
        
        0.6.2 (2017-08-25)
        ------------------
        
        * Fix README.
        
        
        0.6.1 (2017-08-25)
        ------------------
        
        * Fix deploy issues.
        
        
        0.6.0 (2017-08-25)
        ------------------
        
        * Auto-discovering `statemachine`/`statemachines` under a Django project when
          they are requested using the mixin/registry feature.
        
        0.5.1 (2017-07-24)
        ------------------
        
        * Fix bug on ``CombinedTransition._can_run`` not allowing transitions to run if there are more than
          two transitions combined.
        
        0.5.0 (2017-07-13)
        ------------------
        
        * Custom exceptions.
        * Duplicated definition of ``on_execute`` callback is not allowed.
        * Fix bug on ``StateMachine.on_<transition.identifier>`` being called with extra ``self`` param.
        
        0.4.2 (2017-07-10)
        ------------------
        
        * Python 3.6 support.
        * Drop official support for Python 3.3.
        * `Transition` can be used as decorator for `on_execute` callback definition.
        * `Transition` can point to multiple destination states.
        
        
        0.3.0 (2017-03-22)
        ------------------
        
        * README getting started section.
        * Tests to state machine without model.
        
        
        0.2.0 (2017-03-22)
        ------------------
        
        * ``State`` can hold a value that will be assigned to the model as the state value.
        * Travis-CI integration.
        * RTD integration.
        
        
        0.1.0 (2017-03-21)
        ------------------
        
        * First release on PyPI.
        
Keywords: statemachine
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Topic :: Software Development :: Libraries
