python-statemachine 2.4.0__tar.gz → 2.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. python_statemachine-2.6.0/.git-blame-ignore-revs +4 -0
  2. python_statemachine-2.6.0/.github/FUNDING.yml +1 -0
  3. python_statemachine-2.6.0/.github/ISSUE_TEMPLATE.md +15 -0
  4. python_statemachine-2.6.0/.github/workflows/python-package.yml +64 -0
  5. python_statemachine-2.6.0/.github/workflows/release.yml +68 -0
  6. python_statemachine-2.6.0/.gitignore +81 -0
  7. python_statemachine-2.6.0/.pre-commit-config.yaml +33 -0
  8. python_statemachine-2.6.0/.readthedocs.yaml +23 -0
  9. python_statemachine-2.6.0/AGENTS.md +114 -0
  10. python_statemachine-2.6.0/CLAUDE.md +1 -0
  11. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/PKG-INFO +19 -16
  12. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/README.md +6 -4
  13. python_statemachine-2.6.0/conftest.py +46 -0
  14. python_statemachine-2.6.0/contributing.md +1 -0
  15. python_statemachine-2.6.0/docs/_static/custom_machine.css +59 -0
  16. python_statemachine-2.6.0/docs/actions.md +463 -0
  17. python_statemachine-2.6.0/docs/api.md +81 -0
  18. python_statemachine-2.6.0/docs/async.md +186 -0
  19. python_statemachine-2.6.0/docs/authors.md +19 -0
  20. python_statemachine-2.6.0/docs/conf.py +299 -0
  21. python_statemachine-2.6.0/docs/contributing.md +161 -0
  22. python_statemachine-2.6.0/docs/diagram.md +154 -0
  23. python_statemachine-2.6.0/docs/guards.md +277 -0
  24. python_statemachine-2.6.0/docs/images/_oc_machine_processing.svg +90 -0
  25. python_statemachine-2.6.0/docs/images/lab_approval_machine_accepted.png +0 -0
  26. python_statemachine-2.6.0/docs/images/oc_machine_processing.svg +90 -0
  27. python_statemachine-2.6.0/docs/images/order_control_machine_initial.png +0 -0
  28. python_statemachine-2.6.0/docs/images/order_control_machine_initial_300dpi.png +0 -0
  29. python_statemachine-2.6.0/docs/images/order_control_machine_processing.png +0 -0
  30. python_statemachine-2.6.0/docs/images/python-statemachine.png +0 -0
  31. python_statemachine-2.6.0/docs/images/readme_trafficlightmachine.png +0 -0
  32. python_statemachine-2.6.0/docs/images/test_state_machine_internal.png +0 -0
  33. python_statemachine-2.6.0/docs/images/traffic_light_machine.png +0 -0
  34. python_statemachine-2.6.0/docs/index.md +33 -0
  35. python_statemachine-2.6.0/docs/installation.md +54 -0
  36. python_statemachine-2.6.0/docs/integrations.md +90 -0
  37. python_statemachine-2.6.0/docs/listeners.md +105 -0
  38. python_statemachine-2.6.0/docs/mixins.md +93 -0
  39. python_statemachine-2.6.0/docs/models.md +27 -0
  40. python_statemachine-2.6.0/docs/processing_model.md +138 -0
  41. python_statemachine-2.6.0/docs/readme.md +2 -0
  42. python_statemachine-2.6.0/docs/releases/0.1.0.md +5 -0
  43. python_statemachine-2.6.0/docs/releases/0.2.0.md +8 -0
  44. python_statemachine-2.6.0/docs/releases/0.3.0.md +7 -0
  45. python_statemachine-2.6.0/docs/releases/0.4.2.md +12 -0
  46. python_statemachine-2.6.0/docs/releases/0.5.0.md +7 -0
  47. python_statemachine-2.6.0/docs/releases/0.5.1.md +7 -0
  48. python_statemachine-2.6.0/docs/releases/0.6.0.md +7 -0
  49. python_statemachine-2.6.0/docs/releases/0.6.1.md +6 -0
  50. python_statemachine-2.6.0/docs/releases/0.6.2.md +6 -0
  51. python_statemachine-2.6.0/docs/releases/0.7.0.md +6 -0
  52. python_statemachine-2.6.0/docs/releases/0.7.1.md +6 -0
  53. python_statemachine-2.6.0/docs/releases/0.8.0.md +21 -0
  54. python_statemachine-2.6.0/docs/releases/0.9.0.md +65 -0
  55. python_statemachine-2.6.0/docs/releases/1.0.0.md +6 -0
  56. python_statemachine-2.6.0/docs/releases/1.0.1.md +229 -0
  57. python_statemachine-2.6.0/docs/releases/1.0.2.md +14 -0
  58. python_statemachine-2.6.0/docs/releases/1.0.3.md +14 -0
  59. python_statemachine-2.6.0/docs/releases/2.0.0.md +366 -0
  60. python_statemachine-2.6.0/docs/releases/2.1.0.md +43 -0
  61. python_statemachine-2.6.0/docs/releases/2.1.1.md +10 -0
  62. python_statemachine-2.6.0/docs/releases/2.1.2.md +20 -0
  63. python_statemachine-2.6.0/docs/releases/2.2.0.md +71 -0
  64. python_statemachine-2.6.0/docs/releases/2.3.0.md +49 -0
  65. python_statemachine-2.6.0/docs/releases/2.3.1.md +8 -0
  66. python_statemachine-2.6.0/docs/releases/2.3.2.md +95 -0
  67. python_statemachine-2.6.0/docs/releases/2.3.3.md +20 -0
  68. python_statemachine-2.6.0/docs/releases/2.3.4.md +8 -0
  69. python_statemachine-2.6.0/docs/releases/2.3.5.md +12 -0
  70. python_statemachine-2.6.0/docs/releases/2.3.6.md +9 -0
  71. python_statemachine-2.6.0/docs/releases/2.4.0.md +89 -0
  72. python_statemachine-2.6.0/docs/releases/2.5.0.md +188 -0
  73. python_statemachine-2.6.0/docs/releases/2.6.0.md +128 -0
  74. python_statemachine-2.6.0/docs/releases/index.md +70 -0
  75. python_statemachine-2.6.0/docs/states.md +166 -0
  76. python_statemachine-2.6.0/docs/transitions.md +378 -0
  77. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/pyproject.toml +102 -84
  78. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/__init__.py +1 -1
  79. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/callbacks.py +74 -111
  80. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/contrib/diagram.py +4 -4
  81. python_statemachine-2.6.0/statemachine/dispatcher.py +232 -0
  82. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/engines/async_.py +55 -34
  83. python_statemachine-2.6.0/statemachine/engines/base.py +40 -0
  84. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/engines/sync.py +55 -37
  85. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/event.py +23 -6
  86. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/events.py +1 -2
  87. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/factory.py +7 -11
  88. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/locale/en/LC_MESSAGES/statemachine.po +35 -20
  89. python_statemachine-2.6.0/statemachine/locale/hi_IN/LC_MESSAGES/statemachine.po +119 -0
  90. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/locale/pt_BR/LC_MESSAGES/statemachine.po +53 -26
  91. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/locale/zh_CN/LC_MESSAGES/statemachine.po +34 -20
  92. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/mixins.py +11 -0
  93. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/signature.py +85 -32
  94. python_statemachine-2.6.0/statemachine/spec_parser.py +204 -0
  95. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/state.py +59 -27
  96. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/statemachine.py +65 -58
  97. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/transition.py +22 -2
  98. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/transition_list.py +12 -78
  99. python_statemachine-2.6.0/statemachine/transition_mixin.py +89 -0
  100. python_statemachine-2.6.0/tests/__init__.py +0 -0
  101. python_statemachine-2.6.0/tests/conftest.py +213 -0
  102. python_statemachine-2.6.0/tests/django_project/app.py +11 -0
  103. python_statemachine-2.6.0/tests/django_project/core/__init__,.py +0 -0
  104. python_statemachine-2.6.0/tests/django_project/core/settings.py +28 -0
  105. python_statemachine-2.6.0/tests/django_project/core/wsgi.py +16 -0
  106. python_statemachine-2.6.0/tests/django_project/manage.py +23 -0
  107. python_statemachine-2.6.0/tests/django_project/workflow/__init__.py +0 -0
  108. python_statemachine-2.6.0/tests/django_project/workflow/apps.py +6 -0
  109. python_statemachine-2.6.0/tests/django_project/workflow/models.py +22 -0
  110. python_statemachine-2.6.0/tests/django_project/workflow/statemachines.py +15 -0
  111. python_statemachine-2.6.0/tests/django_project/workflow/tests.py +66 -0
  112. python_statemachine-2.6.0/tests/examples/README.rst +9 -0
  113. python_statemachine-2.6.0/tests/examples/__init__.py +0 -0
  114. python_statemachine-2.6.0/tests/examples/air_conditioner_machine.py +69 -0
  115. python_statemachine-2.6.0/tests/examples/all_actions_machine.py +201 -0
  116. python_statemachine-2.6.0/tests/examples/async_guess_the_number_machine.py +173 -0
  117. python_statemachine-2.6.0/tests/examples/async_without_loop_machine.py +45 -0
  118. python_statemachine-2.6.0/tests/examples/enum_campaign_machine.py +61 -0
  119. python_statemachine-2.6.0/tests/examples/guess_the_number_machine.py +123 -0
  120. python_statemachine-2.6.0/tests/examples/lor_machine.py +104 -0
  121. python_statemachine-2.6.0/tests/examples/order_control_machine.py +47 -0
  122. python_statemachine-2.6.0/tests/examples/order_control_rich_model_machine.py +143 -0
  123. python_statemachine-2.6.0/tests/examples/persistent_model_machine.py +142 -0
  124. python_statemachine-2.6.0/tests/examples/recursive_event_machine.py +39 -0
  125. python_statemachine-2.6.0/tests/examples/reusing_transitions_machine.py +97 -0
  126. python_statemachine-2.6.0/tests/examples/traffic_light_machine.py +64 -0
  127. python_statemachine-2.6.0/tests/examples/user_machine.py +131 -0
  128. python_statemachine-2.6.0/tests/helpers.py +10 -0
  129. python_statemachine-2.6.0/tests/models.py +10 -0
  130. python_statemachine-2.6.0/tests/scrape_images.py +64 -0
  131. python_statemachine-2.6.0/tests/test_actions.py +22 -0
  132. python_statemachine-2.6.0/tests/test_async.py +279 -0
  133. python_statemachine-2.6.0/tests/test_callbacks.py +348 -0
  134. python_statemachine-2.6.0/tests/test_callbacks_isolation.py +73 -0
  135. python_statemachine-2.6.0/tests/test_conditions_algebra.py +65 -0
  136. python_statemachine-2.6.0/tests/test_contrib_diagram.py +90 -0
  137. python_statemachine-2.6.0/tests/test_copy.py +232 -0
  138. python_statemachine-2.6.0/tests/test_dispatcher.py +163 -0
  139. python_statemachine-2.6.0/tests/test_events.py +310 -0
  140. python_statemachine-2.6.0/tests/test_examples.py +38 -0
  141. python_statemachine-2.6.0/tests/test_listener.py +106 -0
  142. python_statemachine-2.6.0/tests/test_mixins.py +38 -0
  143. python_statemachine-2.6.0/tests/test_mock_compatibility.py +24 -0
  144. python_statemachine-2.6.0/tests/test_multiple_destinations.py +232 -0
  145. python_statemachine-2.6.0/tests/test_profiling.py +62 -0
  146. python_statemachine-2.6.0/tests/test_registry.py +45 -0
  147. python_statemachine-2.6.0/tests/test_rtc.py +262 -0
  148. python_statemachine-2.6.0/tests/test_signature.py +292 -0
  149. python_statemachine-2.6.0/tests/test_signature_positional_only.py +33 -0
  150. python_statemachine-2.6.0/tests/test_spec_parser.py +356 -0
  151. python_statemachine-2.6.0/tests/test_state.py +41 -0
  152. python_statemachine-2.6.0/tests/test_state_callbacks.py +102 -0
  153. python_statemachine-2.6.0/tests/test_statemachine.py +636 -0
  154. python_statemachine-2.6.0/tests/test_statemachine_bounded_transitions.py +58 -0
  155. python_statemachine-2.6.0/tests/test_statemachine_inheritance.py +110 -0
  156. python_statemachine-2.6.0/tests/test_threading.py +188 -0
  157. python_statemachine-2.6.0/tests/test_transition_list.py +63 -0
  158. python_statemachine-2.6.0/tests/test_transitions.py +387 -0
  159. python_statemachine-2.6.0/tests/testcases/issue308.md +121 -0
  160. python_statemachine-2.6.0/tests/testcases/issue384_multiple_observers.md +57 -0
  161. python_statemachine-2.6.0/tests/testcases/issue434.md +87 -0
  162. python_statemachine-2.6.0/tests/testcases/issue449.md +55 -0
  163. python_statemachine-2.6.0/tests/testcases/issue480.md +43 -0
  164. python_statemachine-2.6.0/uv.lock +2081 -0
  165. python_statemachine-2.4.0/statemachine/dispatcher.py +0 -161
  166. python_statemachine-2.4.0/statemachine/locale/hi_IN/LC_MESSAGES/statemachine.po +0 -93
  167. python_statemachine-2.4.0/statemachine/spec_parser.py +0 -79
  168. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/LICENSE +0 -0
  169. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/contrib/__init__.py +0 -0
  170. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/engines/__init__.py +0 -0
  171. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/event_data.py +0 -0
  172. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/exceptions.py +0 -0
  173. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/graph.py +0 -0
  174. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/i18n.py +0 -0
  175. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/model.py +0 -0
  176. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/py.typed +0 -0
  177. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/registry.py +0 -0
  178. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/states.py +0 -0
  179. {python_statemachine-2.4.0 → python_statemachine-2.6.0}/statemachine/utils.py +0 -0
@@ -0,0 +1,4 @@
1
+ 37fcf9818178587635fffe1bb67a9fd5024a0a45
2
+ 345d82390af35d5d70ddd39c612faa4a64b11080
3
+ d7738e9ad0a3e50bc5c87d4a75c436fb771c96f6
4
+ 5bf10afae2b214900aa58dd44b0a91e469c70631
@@ -0,0 +1 @@
1
+ github: fgmacedo
@@ -0,0 +1,15 @@
1
+ * Python State Machine version:
2
+ * Python version:
3
+ * Operating System:
4
+
5
+ ### Description
6
+
7
+ Describe what you were trying to get done.
8
+ Tell us what happened, what went wrong, and what you expected to happen.
9
+
10
+ ### What I Did
11
+
12
+ ```
13
+ Paste the command(s) you ran and the output.
14
+ If there was a crash, please include the traceback here.
15
+ ```
@@ -0,0 +1,64 @@
1
+ # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2
+ # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3
+
4
+ name: Python checks
5
+
6
+ on:
7
+ push:
8
+ branches: [ "develop" ]
9
+ pull_request:
10
+ branches: [ "develop" ]
11
+
12
+ jobs:
13
+ build:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - run: git fetch origin develop
23
+ - name: Set up Python ${{ matrix.python-version }}
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: ${{ matrix.python-version }}
27
+ - name: Setup Graphviz
28
+ uses: ts-graphviz/setup-graphviz@v2
29
+ - name: Install uv
30
+ uses: astral-sh/setup-uv@v3
31
+ with:
32
+ enable-cache: true
33
+ cache-suffix: "python${{ matrix.python-version }}"
34
+ - name: Install the project
35
+ run: uv sync --all-extras --dev
36
+ #----------------------------------------------
37
+ # run ruff
38
+ #----------------------------------------------
39
+ - name: Linter with ruff
40
+ if: matrix.python-version == 3.14
41
+ run: |
42
+ uv run ruff check .
43
+ uv run ruff format --check .
44
+ #----------------------------------------------
45
+ # run pytest
46
+ #----------------------------------------------
47
+ - name: Test with pytest
48
+ run: |
49
+ uv run pytest --cov-report=xml:coverage.xml
50
+ uv run coverage xml
51
+ #----------------------------------------------
52
+ # upload coverage
53
+ #----------------------------------------------
54
+ - name: Upload coverage to Codecov
55
+ uses: codecov/codecov-action@v4
56
+ if: matrix.python-version == 3.14
57
+ with:
58
+ token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
59
+ directory: .
60
+ env_vars: OS,PYTHON
61
+ fail_ci_if_error: true
62
+ flags: unittests
63
+ name: codecov-umbrella
64
+ verbose: true
@@ -0,0 +1,68 @@
1
+ on:
2
+ push:
3
+ tags: [ 'v?*.*.*' ]
4
+ name: release
5
+
6
+ jobs:
7
+ release-build:
8
+ name: Build release artifacts
9
+ runs-on: ubuntu-latest
10
+
11
+ permissions:
12
+ id-token: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - run: git fetch origin develop
17
+
18
+ - name: Setup Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: '3.14'
22
+
23
+ - name: Setup Graphviz
24
+ uses: ts-graphviz/setup-graphviz@v2
25
+
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@v3
28
+ with:
29
+ enable-cache: true
30
+
31
+ - name: Install the project
32
+ run: uv sync --all-extras --dev
33
+
34
+ - name: Test
35
+ run: |
36
+ uv run pytest
37
+
38
+ - name: Build
39
+ run: |
40
+ uv build
41
+
42
+ - name: Upload dists
43
+ uses: actions/upload-artifact@v4
44
+ with:
45
+ name: release-dists
46
+ path: dist/
47
+
48
+
49
+ pypi-publish:
50
+ # by a dedicated job to publish we avoid the risk of
51
+ # running code with access to PyPI credentials
52
+ name: Upload release to PyPI
53
+ runs-on: ubuntu-latest
54
+ needs:
55
+ - release-build
56
+ environment: release
57
+ permissions:
58
+ id-token: write
59
+
60
+ steps:
61
+ - name: Retrieve release distributions
62
+ uses: actions/download-artifact@v4
63
+ with:
64
+ name: release-dists
65
+ path: dist/
66
+
67
+ - name: Publish package distributions to PyPI
68
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,81 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ env/
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+ .mypy_cache
27
+
28
+ # jupyter
29
+ .ipynb_checkpoints/
30
+ .jupyterlite.doit.db
31
+
32
+ # PyInstaller
33
+ # Usually these files are written by a python script from a template
34
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
35
+ *.manifest
36
+ *.spec
37
+
38
+ # Installer logs
39
+ pip-log.txt
40
+ pip-delete-this-directory.txt
41
+
42
+ # Unit test / coverage reports
43
+ prof/
44
+ .benchmarks/
45
+ htmlcov/
46
+ .tox/
47
+ .coverage
48
+ .coverage.*
49
+ .cache
50
+ .pytest_cache
51
+ nosetests.xml
52
+ coverage.xml
53
+ *,cover
54
+ .hypothesis/
55
+
56
+ # Translations
57
+ *.mo
58
+ *.pot
59
+
60
+ # Django stuff:
61
+ *.log
62
+
63
+ # Sphinx documentation
64
+ docs/_build/
65
+ docs/auto_examples/
66
+
67
+ # PyBuilder
68
+ target/
69
+
70
+ # pyenv python configuration file
71
+ .python-version
72
+
73
+ # IDEs and editors
74
+ *.sublime*
75
+ .idea/
76
+ .vscode/
77
+
78
+ # Sphinx-galery
79
+ docs/auto_examples/sg_execution_times.*
80
+ docs/auto_examples/*.pickle
81
+ docs/sg_execution_times.rst
@@ -0,0 +1,33 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.6.0
4
+ hooks:
5
+ - id: check-yaml
6
+ - id: end-of-file-fixer
7
+ exclude: docs/auto_examples
8
+ - id: trailing-whitespace
9
+ exclude: docs/auto_examples
10
+ - repo: https://github.com/charliermarsh/ruff-pre-commit
11
+ # Ruff version.
12
+ rev: v0.15.0
13
+ hooks:
14
+ # Run the linter.
15
+ - id: ruff
16
+ args: [ --fix ]
17
+ # Run the formatter.
18
+ - id: ruff-format
19
+
20
+ - repo: local
21
+ hooks:
22
+ - id: mypy
23
+ name: Mypy
24
+ entry: uv run mypy --namespace-packages --explicit-package-bases statemachine/ tests/
25
+ types: [python]
26
+ language: system
27
+ pass_filenames: false
28
+ - id: pytest
29
+ name: Pytest
30
+ entry: uv run pytest
31
+ types: [python]
32
+ language: system
33
+ pass_filenames: false
@@ -0,0 +1,23 @@
1
+ # .readthedocs.yaml
2
+ # Read the Docs configuration file
3
+ # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4
+
5
+ # Required
6
+ version: 2
7
+
8
+ build:
9
+ os: "ubuntu-22.04"
10
+ tools:
11
+ python: "3.14"
12
+ apt_packages:
13
+ - graphviz
14
+ jobs:
15
+ post_create_environment:
16
+ - asdf plugin add uv
17
+ - asdf install uv latest
18
+ - asdf global uv latest
19
+ - UV_PROJECT_ENVIRONMENT=$READTHEDOCS_VIRTUALENV_PATH uv sync --all-extras --frozen
20
+
21
+ # Build documentation in the docs/ directory with Sphinx
22
+ sphinx:
23
+ configuration: docs/conf.py
@@ -0,0 +1,114 @@
1
+ # python-statemachine
2
+
3
+ Python Finite State Machines made easy.
4
+
5
+ ## Project overview
6
+
7
+ A library for building finite state machines in Python, with support for sync and async engines,
8
+ Django integration, diagram generation, and a flexible callback/listener system.
9
+
10
+ - **Source code:** `statemachine/`
11
+ - **Tests:** `tests/`
12
+ - **Documentation:** `docs/` (Sphinx + MyST Markdown, hosted on ReadTheDocs)
13
+
14
+ ## Architecture
15
+
16
+ - `statemachine.py` — Core `StateMachine` class
17
+ - `factory.py` — `StateMachineMetaclass` handles class construction, state/transition validation
18
+ - `state.py` / `event.py` — Descriptor-based `State` and `Event` definitions
19
+ - `transition.py` / `transition_list.py` — Transition logic and composition (`|` operator)
20
+ - `callbacks.py` — Priority-based callback registry (`CallbackPriority`, `CallbackGroup`)
21
+ - `dispatcher.py` — Listener/observer pattern, `callable_method` wraps callables with signature adaptation
22
+ - `signature.py` — `SignatureAdapter` for dependency injection into callbacks
23
+ - `engines/sync.py`, `engines/async_.py` — Sync and async run-to-completion engines
24
+ - `registry.py` — Global state machine registry (used by `MachineMixin`)
25
+ - `mixins.py` — `MachineMixin` for domain model integration (e.g., Django models)
26
+ - `spec_parser.py` — Boolean expression parser for condition guards
27
+ - `contrib/diagram.py` — Diagram generation via pydot/Graphviz
28
+
29
+ ## Environment setup
30
+
31
+ ```bash
32
+ uv sync --all-extras --dev
33
+ pre-commit install
34
+ ```
35
+
36
+ ## Running tests
37
+
38
+ Always use `uv` to run commands:
39
+
40
+ ```bash
41
+ # Run all tests (parallel)
42
+ uv run pytest -n auto
43
+
44
+ # Run a specific test file
45
+ uv run pytest tests/test_signature.py
46
+
47
+ # Run a specific test
48
+ uv run pytest tests/test_signature.py::TestSignatureAdapter::test_wrap_fn_single_positional_parameter
49
+
50
+ # Skip slow tests
51
+ uv run pytest -m "not slow"
52
+ ```
53
+
54
+ Tests include doctests from both source modules (`--doctest-modules`) and markdown docs
55
+ (`--doctest-glob=*.md`). Coverage is enabled by default.
56
+
57
+ ## Linting and formatting
58
+
59
+ ```bash
60
+ # Lint
61
+ uv run ruff check .
62
+
63
+ # Auto-fix lint issues
64
+ uv run ruff check --fix .
65
+
66
+ # Format
67
+ uv run ruff format .
68
+
69
+ # Type check
70
+ uv run mypy statemachine/ tests/
71
+ ```
72
+
73
+ ## Code style
74
+
75
+ - **Formatter/Linter:** ruff (line length 99, target Python 3.9)
76
+ - **Rules:** pycodestyle, pyflakes, isort, pyupgrade, flake8-comprehensions, flake8-bugbear, flake8-pytest-style
77
+ - **Imports:** single-line, sorted by isort
78
+ - **Docstrings:** Google convention
79
+ - **Naming:** PascalCase for classes, snake_case for functions/methods, UPPER_SNAKE_CASE for constants
80
+ - **Type hints:** used throughout; `TYPE_CHECKING` for circular imports
81
+ - Pre-commit hooks enforce ruff + mypy + pytest
82
+
83
+ ## Design principles
84
+
85
+ - **Decouple infrastructure from domain:** Modules like `signature.py` and `dispatcher.py` are
86
+ general-purpose (signature adaptation, listener/observer pattern) and intentionally not coupled
87
+ to the state machine domain. Prefer this separation even for modules that are only used
88
+ internally — it keeps responsibilities clear and the code easier to reason about.
89
+ - **Favor small, focused modules:** When adding new functionality, consider whether it can live in
90
+ its own module with a well-defined boundary, rather than growing an existing one.
91
+
92
+ ## Building documentation
93
+
94
+ ```bash
95
+ # Build HTML docs
96
+ uv run sphinx-build docs docs/_build/html
97
+
98
+ # Live reload for development
99
+ uv run sphinx-autobuild docs docs/_build/html --re-ignore "auto_examples/.*"
100
+ ```
101
+
102
+ ## Git workflow
103
+
104
+ - Main branch: `develop`
105
+ - PRs target `develop`
106
+ - Releases are tagged as `v*.*.*`
107
+ - Signed commits preferred (`git commit -s`)
108
+ - Use [Conventional Commits](https://www.conventionalcommits.org/) messages
109
+ (e.g., `feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`, `perf:`)
110
+
111
+ ## Security
112
+
113
+ - Do not commit secrets, credentials, or `.env` files
114
+ - Validate at system boundaries; trust internal code
@@ -0,0 +1 @@
1
+ AGENTS.md
@@ -1,19 +1,18 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python-statemachine
3
- Version: 2.4.0
3
+ Version: 2.6.0
4
4
  Summary: Python Finite State Machines made easy.
5
- Home-page: https://github.com/fgmacedo/python-statemachine
6
- License: MIT
7
- Author: Fernando Macedo
8
- Author-email: fgmacedo@gmail.com
9
- Maintainer: Fernando Macedo
10
- Maintainer-email: fgmacedo@gmail.com
11
- Requires-Python: >=3.7
5
+ Project-URL: homepage, https://github.com/fgmacedo/python-statemachine
6
+ Author-email: Fernando Macedo <fgmacedo@gmail.com>
7
+ Maintainer-email: Fernando Macedo <fgmacedo@gmail.com>
8
+ License: MIT License
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 5 - Production/Stable
12
11
  Classifier: Framework :: AsyncIO
12
+ Classifier: Framework :: Django
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Natural Language :: English
16
- Classifier: Programming Language :: Python :: 3
17
16
  Classifier: Programming Language :: Python :: 3.7
18
17
  Classifier: Programming Language :: Python :: 3.8
19
18
  Classifier: Programming Language :: Python :: 3.9
@@ -21,9 +20,12 @@ Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
22
21
  Classifier: Programming Language :: Python :: 3.12
23
22
  Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: Home Automation
24
25
  Classifier: Topic :: Software Development :: Libraries
26
+ Requires-Python: >=3.7
25
27
  Provides-Extra: diagrams
26
- Requires-Dist: pydot (>=2.0.0) ; (python_full_version > "3.8.0") and (extra == "diagrams")
28
+ Requires-Dist: pydot>=2.0.0; extra == 'diagrams'
27
29
  Description-Content-Type: text/markdown
28
30
 
29
31
  # Python StateMachine
@@ -127,6 +129,8 @@ You can now create an instance:
127
129
  This state machine can be represented graphically as follows:
128
130
 
129
131
  ```py
132
+ >>> # This example will only run on automated tests if dot is present
133
+ >>> getfixture("requires_dot_installed")
130
134
  >>> img_path = "docs/images/readme_trafficlightmachine.png"
131
135
  >>> sm._graph().write_png(img_path)
132
136
 
@@ -196,19 +200,19 @@ Easily iterate over all states:
196
200
 
197
201
  ```py
198
202
  >>> [s.id for s in sm.states]
199
- ['green', 'red', 'yellow']
203
+ ['green', 'yellow', 'red']
200
204
 
201
205
  ```
202
206
 
203
207
  Or over events:
204
208
 
205
209
  ```py
206
- >>> [t.name for t in sm.events]
210
+ >>> [t.id for t in sm.events]
207
211
  ['cycle']
208
212
 
209
213
  ```
210
214
 
211
- Call an event by its name:
215
+ Call an event by its id:
212
216
 
213
217
  ```py
214
218
  >>> sm.cycle()
@@ -216,7 +220,7 @@ Don't move.
216
220
  'Running cycle from yellow to red'
217
221
 
218
222
  ```
219
- Or send an event with the event name:
223
+ Or send an event with the event id:
220
224
 
221
225
  ```py
222
226
  >>> sm.send('cycle')
@@ -427,4 +431,3 @@ request. For more information on how to contribute, please see our [contributing
427
431
  - **Promote the project**: Help spread the word by sharing on social media,
428
432
  writing a blog post, or giving a talk about it. Tag me on Twitter
429
433
  [@fgmacedo](https://twitter.com/fgmacedo) so I can share it too!
430
-
@@ -99,6 +99,8 @@ You can now create an instance:
99
99
  This state machine can be represented graphically as follows:
100
100
 
101
101
  ```py
102
+ >>> # This example will only run on automated tests if dot is present
103
+ >>> getfixture("requires_dot_installed")
102
104
  >>> img_path = "docs/images/readme_trafficlightmachine.png"
103
105
  >>> sm._graph().write_png(img_path)
104
106
 
@@ -168,19 +170,19 @@ Easily iterate over all states:
168
170
 
169
171
  ```py
170
172
  >>> [s.id for s in sm.states]
171
- ['green', 'red', 'yellow']
173
+ ['green', 'yellow', 'red']
172
174
 
173
175
  ```
174
176
 
175
177
  Or over events:
176
178
 
177
179
  ```py
178
- >>> [t.name for t in sm.events]
180
+ >>> [t.id for t in sm.events]
179
181
  ['cycle']
180
182
 
181
183
  ```
182
184
 
183
- Call an event by its name:
185
+ Call an event by its id:
184
186
 
185
187
  ```py
186
188
  >>> sm.cycle()
@@ -188,7 +190,7 @@ Don't move.
188
190
  'Running cycle from yellow to red'
189
191
 
190
192
  ```
191
- Or send an event with the event name:
193
+ Or send an event with the event id:
192
194
 
193
195
  ```py
194
196
  >>> sm.send('cycle')
@@ -0,0 +1,46 @@
1
+ import shutil
2
+ import sys
3
+
4
+ import pytest
5
+
6
+
7
+ @pytest.fixture(autouse=True, scope="session")
8
+ def add_doctest_context(doctest_namespace): # noqa: PT004
9
+ from statemachine.utils import run_async_from_sync
10
+
11
+ from statemachine import State
12
+ from statemachine import StateMachine
13
+
14
+ class ContribAsyncio:
15
+ """
16
+ Using `run_async_from_sync` to be injected in the doctests to better integration with an
17
+ already running loop, as all of our examples are also automated executed as doctests.
18
+
19
+ On real life code you should use standard `import asyncio; asyncio.run(main())`.
20
+ """
21
+
22
+ def __init__(self):
23
+ self.run = run_async_from_sync
24
+
25
+ doctest_namespace["State"] = State
26
+ doctest_namespace["StateMachine"] = StateMachine
27
+ doctest_namespace["asyncio"] = ContribAsyncio()
28
+
29
+
30
+ def pytest_ignore_collect(collection_path, path, config):
31
+ if sys.version_info >= (3, 10): # noqa: UP036
32
+ return None
33
+
34
+ if "django_project" in str(path):
35
+ return True
36
+
37
+
38
+ @pytest.fixture(scope="session")
39
+ def has_dot_installed():
40
+ return bool(shutil.which("dot"))
41
+
42
+
43
+ @pytest.fixture()
44
+ def requires_dot_installed(request, has_dot_installed):
45
+ if not has_dot_installed:
46
+ pytest.skip(f"Test {request.node.nodeid} requires 'dot' that is not installed.")
@@ -0,0 +1 @@
1
+ Please see [docs/contributing.md](docs/contributing).
@@ -0,0 +1,59 @@
1
+ /* div.sphx-glr-download {
2
+ height: 0px;
3
+ visibility: hidden;
4
+ } */
5
+
6
+ @media only screen and (min-width: 650px) {
7
+
8
+ .sphx-glr-thumbnails {
9
+ grid-template-columns: repeat(auto-fill, minmax(600px, 1fr)) !important;
10
+ }
11
+
12
+ .sphx-glr-thumbcontainer {
13
+ min-height: 320px !important;
14
+ margin: 20px !important;
15
+ justify-content: center;
16
+ }
17
+ .sphx-glr-thumbcontainer .figure {
18
+ width: 600px !important;
19
+ }
20
+ .sphx-glr-thumbcontainer img {
21
+ max-height: 250px !important;
22
+ max-width: 600px !important;
23
+ width: 100% !important;
24
+ }
25
+ .sphx-glr-thumbcontainer a.internal {
26
+ padding: 20px 10px 0 !important;
27
+ }
28
+
29
+ }
30
+
31
+ /* Gallery Donwload buttons */
32
+ div.sphx-glr-download a {
33
+ color: #404040 !important;
34
+ background-color: #f3f6f6 !important;
35
+ background-image: none;
36
+ border-radius: 4px;
37
+ border: none;
38
+ display: inline-block;
39
+ font-weight: bold;
40
+ padding: 1ex;
41
+ text-align: center;
42
+ }
43
+
44
+ div.sphx-glr-download code.download {
45
+ display: inline-block;
46
+ white-space: normal;
47
+ word-break: normal;
48
+ overflow-wrap: break-word;
49
+ /* border and background are given by the enclosing 'a' */
50
+ border: none;
51
+ background: none;
52
+ }
53
+
54
+ div.sphx-glr-download a:hover {
55
+ box-shadow: none;
56
+ text-decoration: none;
57
+ background-image: none;
58
+ background-color: #e5ebeb !important;
59
+ }