Skip to main content

State machine analysis library

Project description

smcheck

PyPI - Version codecov

Automatic analysis, testing, and visualization for python-statemachine state machines.

smcheck gives you confidence in your state machines by:

  • ๐Ÿ” Validating 9 structural properties (reachability, liveness, determinism, completeness, etc.)
  • ๐Ÿ“Š Analyzing all possible paths through your state machine
  • ๐Ÿงช Generating pytest tests for every transition and path
  • ๐Ÿ“ˆ Exporting interactive Mermaid diagrams with guard labels

Installation

pip install smcheck

For LLM-powered path explanations (optional):

pip install smcheck[llm]

Requires Python โ‰ฅ3.11 and python-statemachine โ‰ฅ3.0.


Quick Start โ€” 5 minutes

Define your state machine normally:

# myapp/machine.py
from statemachine import State, StateChart

class OrderFlow(StateChart):
    waiting   = State(initial=True)
    processing = State()
    shipped   = State(final=True)
    cancelled = State(final=True)

    submit = waiting.to(processing)
    ship   = processing.to(shipped)
    cancel = processing.to(cancelled)  # can cancel during processing

Now run smcheck:

from smcheck import SMCheck

sm = SMCheck(OrderFlow)

# Validation report (9 checks)
sm.report_validation()

# Path analysis
analysis = sm.analyze_paths()
print(f"Total paths: {analysis.combined_count}")

# Auto-generate tests
sm.write_tests(
    sm_import="myapp.machine",
    output_dir="tests/generated/"
)

# Export diagram
sm.write_mermaid("diagram.mmd")

That's it! You now have:

  • โœ… A validation report
  • โœ… All states are reachable
  • โœ… No deadlocks or infinite loops
  • โœ… Pytest test files for every transition and path
  • โœ… A diagram you can share with stakeholders

The 9 Validation Checks

smcheck validates that your state machine is:

Check Detects Level
Reachability States you can't reach from the start โš ๏ธ WARN
Liveness Deadlocks (states with no way out) ๐Ÿšจ ERROR
Determinism Ambiguous transitions (multiple targets for one event) โš ๏ธ WARN
Completeness Unfinished states (no outgoing transitions) โš ๏ธ WARN
Trap cycles Infinite loops with no exit ๐Ÿšจ ERROR
Class flags Risky behavioral settings โš ๏ธ WARN
Invoke states States that fire events spontaneously โ„น๏ธ INFO
Self-transitions Loops that trigger entry/exit callbacks โ„น๏ธ INFO
Hook names Typos in convention method names โš ๏ธ WARN

Example:

sm = SMCheck(OrderFlow)
v = sm.validate()
for finding in v.run_all():
    print(f"[{finding.level}] {finding.category}")
    if finding.detail:
        print(f"  {finding.detail}")

Common Tasks

Task: Add guards with conditions

class OrderFlow(StateChart):
    # ... states ...
    
    def can_proceed(self) -> bool:
        """Payment verified."""
        return self._payment_approved
    
    submit = waiting.to(processing, cond="can_proceed")
    
    def __init__(self):
        self._payment_approved = False
        super().__init__()

smcheck automatically detects the guard and generates setup code in tests.

Task: Add sub-machines (compound states)

class OrderFlow(StateChart):
    waiting   = State(initial=True)
    
    class processing(StateChart):
        checking  = State(initial=True)
        shipping  = State()
        done      = shipping  # shorthand for final
        
        check_inv    = checking.to(shipping)
        finish_ship  = shipping.to(done)
    
    cancelled = State(final=True)
    
    submit = waiting.to(processing)
    cancel = waiting.to(cancelled) | processing.to(cancelled)

Task: See the diagram before generating tests

sm = SMCheck(OrderFlow)
diagram_text = sm.to_mermaid()
print(diagram_text)
# โ†’ copy/paste into https://mermaid.live

Understanding the Output

Validation Report Example

โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘                    VALIDATION FINDINGS                                 โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘  [PASS]     reachability     All 8 states reachable                    โ•‘
โ•‘  [PASS]     liveness         No deadlocks (all non-final states exit)  โ•‘
โ•‘  [INFO]     invoke_states     processing [auto-timeout]                โ•‘
โ•‘  [WARN]     hook_names       on_exit_old_state: state 'old_state' not  โ•‘
โ•‘                              found (typo?) โ€” remove or fix             โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Generated Tests

generated_tests/
โ”œโ”€โ”€ test_transitions.py    # One test per (state, event) โ†’ target
โ””โ”€โ”€ test_paths.py          # One test per enumerated path

Tests are self-contained and can run offline (no real dependencies).

Mermaid Diagram

The exported diagram includes:

  • Guard conditions in [square brackets]
  • Constraint notes (e.g., "Only if payment verified")
  • Compound states grouped together
  • Auto-generated from your state machine โ€” always in sync

Advanced: Custom Guard Setup

If your machine uses dynamic guards that can't be inspected statically:

sm = SMCheck(OrderFlow)

# Tell smcheck how to set up each guarded event
guard_setup = {
    "submit": {"_payment_approved": True},
    "ship":   {"_inventory_reserved": True},
}

tests = sm.generate_tests(guard_setup_map=guard_setup)
sm.write_tests(...)

Detailed Analysis with PathAnalysis

analysis = sm.analyze_paths()

print(f"Top-level paths: {len(analysis.top_level_paths)}")
print(f"Parallel track paths: {analysis.track_paths}")
print(f"Total combinations: {analysis.combined_count}")

# Each path is an SMPath object
for path in analysis.top_level_paths:
    for edge in path.edges:
        print(f"  {edge.source} --{edge.event}--> {edge.target}")

Why smcheck?

For teams developing state machines:

  • Catch structural bugs early (deadlocks, unreachable states)
  • Auto-generate test boilerplate (no manual path enumeration)
  • Share diagrams with non-technical stakeholders
  • Refactor with confidence (re-run validation in CI)

For library authors:

  • Document state machine behavior with generated tests
  • Ensure compatibility with new python-statemachine versions
  • Give users example state machines + their tests

For teaching:

  • Teach students to think about paths and edge cases
  • Let students focus on business logic, not test scaffolding
  • Visualize complex state machines

Further Reading

See Feature Catalogue for details on:

  • Graph extraction (adjacency maps, path algorithms)
  • Detailed validation rules
  • Code generation internals
  • Mermaid export options

For python-statemachine docs, see python-statemachine.readthedocs.io.


Contributing

Issues and PRs welcome at github.com/brunolnetto/smcheck.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

smcheck-0.0.1.tar.gz (296.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

smcheck-0.0.1-py3-none-any.whl (63.4 kB view details)

Uploaded Python 3

File details

Details for the file smcheck-0.0.1.tar.gz.

File metadata

  • Download URL: smcheck-0.0.1.tar.gz
  • Upload date:
  • Size: 296.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.2

File hashes

Hashes for smcheck-0.0.1.tar.gz
Algorithm Hash digest
SHA256 2ed588717bdcb0f5083624697518299d066852a59256dab09592cb50bb712821
MD5 b28e1148bce9243b4867c932fa1b8000
BLAKE2b-256 a40c22fcdb061c2bc146e96d156bcb34956dd8455030ad78942e7202c8e9c9a1

See more details on using hashes here.

File details

Details for the file smcheck-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: smcheck-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 63.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.2

File hashes

Hashes for smcheck-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b77726df53fb78275abf2e41a98c50901ffb1b625dced48127d64185cc278447
MD5 8ac287821b4b3ead709dbb0e82d84685
BLAKE2b-256 970d62491f46710a466ef5417744087a567284a33b9b13a0881baacde17a263d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page