{"id":1339,"library":"automat","title":"Automat Finite State Machines","description":"Automat provides a declarative, self-service API for defining finite-state machines directly within your Python classes. It helps manage complex state transitions and actions in a structured, testable way. The current version is 25.4.16, and it maintains a relatively stable release cadence with minor updates and bug fixes.","status":"active","version":"25.4.16","language":"en","source_language":"en","source_url":"https://github.com/glyph/automat/","tags":["state-machine","fsm","declarative","design-pattern"],"install":[{"cmd":"pip install automat","lang":"bash","label":"Install stable version"}],"dependencies":[],"imports":[{"symbol":"MethodicalMachine","correct":"from automat import MethodicalMachine"}],"quickstart":{"code":"from automat import MethodicalMachine\n\nclass Door(object):\n    _machine = MethodicalMachine()\n\n    @_machine.state()\n    def closed(self):\n        \"The door is closed.\"\n\n    @_machine.state()\n    def open(self):\n        \"The door is open.\"\n\n    @_machine.input()\n    def open_door(self):\n        \"Open the door.\"\n\n    @_machine.input()\n    def close_door(self):\n        \"Close the door.\"\n\n    @_machine.output()\n    def _actuallyOpen(self):\n        print(\"The door opens.\")\n\n    @_machine.output()\n    def _actuallyClose(self):\n        print(\"The door closes.\")\n\n    closed.upon(open_door, enter=open, outputs=[_actuallyOpen])\n    open.upon(close_door, enter=closed, outputs=[_actuallyClose])\n\n    def __init__(self):\n        # Crucial: Initialize the machine with an initial state\n        self._machine.initial(self, self.closed)\n\n# Example usage:\nd = Door()\nprint(f\"Initial state: {d._machine.current_state(d)._name}\")\nd.open_door()\nprint(f\"After open_door: {d._machine.current_state(d)._name}\")\nd.close_door()\nprint(f\"After close_door: {d._machine.current_state(d)._name}\")","lang":"python","description":"This quickstart demonstrates how to define a simple state machine for a door with 'closed' and 'open' states, and 'open_door'/'close_door' inputs. It also shows how to associate outputs (actions) with state transitions and how to initialize and interact with the machine."},"warnings":[{"fix":"Always ensure your class's `__init__` explicitly calls `self._machine.initial(self, self.your_initial_state)` to set the starting state.","message":"Failing to call `_machine.initial(self, self.initial_state)` in your class's `__init__` method will result in an uninitialized state machine, preventing any transitions.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Always explicitly specify the `enter` argument in `state.upon` if you intend for the machine to transition to a new state. If you mean to stay in the current state, `enter=self.current_state` (or omitting `enter`) is correct.","message":"When defining transitions with `state.upon(input, ...)` if `enter` is omitted, the machine will remain in the *current* state by default. This can be unexpected if you intended a state change.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Design your output methods to accept `self` and use it to interact with the object's internal state or perform side effects directly related to the object.","message":"Output methods (decorated with `@_machine.output()`) are instance methods and receive `self` as their first argument. They are called in the context of the instance, allowing them to access and modify instance attributes.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Always drive state changes through the defined `@_machine.input()` methods. If you need to expose internal state, do so via read-only properties or specific queries, not direct manipulation.","message":"Automat discourages directly modifying the machine's state outside of defined inputs and transitions. Attempting to do so breaks the FSM's contract and can lead to unpredictable behavior.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-09T00:00:00.000Z","next_check":"2026-07-08T00:00:00.000Z"}