archml 0.1.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 (107) hide show
  1. archml-0.1.0/.github/workflows/ci.yml +102 -0
  2. archml-0.1.0/.github/workflows/claude_instruction.yml +43 -0
  3. archml-0.1.0/.github/workflows/publish.yml +60 -0
  4. archml-0.1.0/.gitignore +217 -0
  5. archml-0.1.0/CLAUDE.md +94 -0
  6. archml-0.1.0/LICENSE +201 -0
  7. archml-0.1.0/PKG-INFO +475 -0
  8. archml-0.1.0/README.md +264 -0
  9. archml-0.1.0/docs/LANGUAGE_SYNTAX.md +761 -0
  10. archml-0.1.0/docs/PROJECT_SCOPE.md +126 -0
  11. archml-0.1.0/docs/sphinx/conf.py +12 -0
  12. archml-0.1.0/docs/sphinx/index.rst +8 -0
  13. archml-0.1.0/pyproject.toml +48 -0
  14. archml-0.1.0/src/archml/__init__.py +6 -0
  15. archml-0.1.0/src/archml/cli/__init__.py +4 -0
  16. archml-0.1.0/src/archml/cli/main.py +614 -0
  17. archml-0.1.0/src/archml/compiler/__init__.py +23 -0
  18. archml-0.1.0/src/archml/compiler/artifact.py +76 -0
  19. archml-0.1.0/src/archml/compiler/build.py +357 -0
  20. archml-0.1.0/src/archml/compiler/parser.py +822 -0
  21. archml-0.1.0/src/archml/compiler/scanner.py +393 -0
  22. archml-0.1.0/src/archml/compiler/semantic_analysis.py +1087 -0
  23. archml-0.1.0/src/archml/export/__init__.py +133 -0
  24. archml-0.1.0/src/archml/model/__init__.py +52 -0
  25. archml-0.1.0/src/archml/model/entities.py +205 -0
  26. archml-0.1.0/src/archml/model/types.py +90 -0
  27. archml-0.1.0/src/archml/sphinx_ext/__init__.py +13 -0
  28. archml-0.1.0/src/archml/sphinx_ext/extension.py +402 -0
  29. archml-0.1.0/src/archml/static/archml-diagram.css +619 -0
  30. archml-0.1.0/src/archml/static/archml-viewer-template.html +90 -0
  31. archml-0.1.0/src/archml/static/archml-viewer.js +78 -0
  32. archml-0.1.0/src/archml/validation/__init__.py +18 -0
  33. archml-0.1.0/src/archml/validation/checks.py +404 -0
  34. archml-0.1.0/src/archml/views/__init__.py +4 -0
  35. archml-0.1.0/src/archml/views/diagram.py +453 -0
  36. archml-0.1.0/src/archml/views/layout.py +518 -0
  37. archml-0.1.0/src/archml/views/placement.py +615 -0
  38. archml-0.1.0/src/archml/views/resolver.py +104 -0
  39. archml-0.1.0/src/archml/views/topology.py +1588 -0
  40. archml-0.1.0/src/archml/workspace/__init__.py +48 -0
  41. archml-0.1.0/src/archml/workspace/config.py +153 -0
  42. archml-0.1.0/src/archml/workspace/git_ops.py +148 -0
  43. archml-0.1.0/src/archml/workspace/lockfile.py +92 -0
  44. archml-0.1.0/tests/__init__.py +2 -0
  45. archml-0.1.0/tests/cli/__init__.py +2 -0
  46. archml-0.1.0/tests/cli/test_main.py +1000 -0
  47. archml-0.1.0/tests/compiler/__init__.py +2 -0
  48. archml-0.1.0/tests/compiler/test_artifact.py +411 -0
  49. archml-0.1.0/tests/compiler/test_build.py +686 -0
  50. archml-0.1.0/tests/compiler/test_compiler_integration.py +429 -0
  51. archml-0.1.0/tests/compiler/test_parser.py +2180 -0
  52. archml-0.1.0/tests/compiler/test_scanner.py +966 -0
  53. archml-0.1.0/tests/compiler/test_semantic_analysis.py +1995 -0
  54. archml-0.1.0/tests/data/negative/component_system_name_conflict.archml +19 -0
  55. archml-0.1.0/tests/data/negative/duplicate_component_name.archml +17 -0
  56. archml-0.1.0/tests/data/negative/duplicate_enum_name.archml +12 -0
  57. archml-0.1.0/tests/data/negative/duplicate_enum_values.archml +18 -0
  58. archml-0.1.0/tests/data/negative/duplicate_field_names.archml +18 -0
  59. archml-0.1.0/tests/data/negative/duplicate_interface.archml +22 -0
  60. archml-0.1.0/tests/data/negative/duplicate_type_name.archml +13 -0
  61. archml-0.1.0/tests/data/negative/enum_type_name_conflict.archml +17 -0
  62. archml-0.1.0/tests/data/negative/imports/consumer.archml +10 -0
  63. archml-0.1.0/tests/data/negative/imports/source.archml +13 -0
  64. archml-0.1.0/tests/data/negative/undefined_connection_endpoint.archml +28 -0
  65. archml-0.1.0/tests/data/negative/undefined_interface_ref.archml +20 -0
  66. archml-0.1.0/tests/data/negative/undefined_type_ref.archml +22 -0
  67. archml-0.1.0/tests/data/negative/wrong_interface_version.archml +23 -0
  68. archml-0.1.0/tests/data/positive/compiler/shared/types.archml +24 -0
  69. archml-0.1.0/tests/data/positive/compiler/simple.archml +38 -0
  70. archml-0.1.0/tests/data/positive/compiler/system.archml +16 -0
  71. archml-0.1.0/tests/data/positive/compiler/worker.archml +13 -0
  72. archml-0.1.0/tests/data/positive/imports/ecommerce_system.archml +36 -0
  73. archml-0.1.0/tests/data/positive/imports/order_service.archml +13 -0
  74. archml-0.1.0/tests/data/positive/imports/types.archml +34 -0
  75. archml-0.1.0/tests/data/positive/interfaces.archml +91 -0
  76. archml-0.1.0/tests/data/positive/minimal_component.archml +8 -0
  77. archml-0.1.0/tests/data/positive/nested_components.archml +52 -0
  78. archml-0.1.0/tests/data/positive/system_with_connections.archml +79 -0
  79. archml-0.1.0/tests/data/positive/types_and_enums.archml +55 -0
  80. archml-0.1.0/tests/data/positive/versioned_interfaces.archml +40 -0
  81. archml-0.1.0/tests/dot_sync/flat.dot +12 -0
  82. archml-0.1.0/tests/dot_sync/flat.viz.json +66 -0
  83. archml-0.1.0/tests/dot_sync/nested.dot +18 -0
  84. archml-0.1.0/tests/dot_sync/nested.viz.json +90 -0
  85. archml-0.1.0/tests/dot_sync/with_terminals.dot +12 -0
  86. archml-0.1.0/tests/dot_sync/with_terminals.viz.json +67 -0
  87. archml-0.1.0/tests/export/__init__.py +0 -0
  88. archml-0.1.0/tests/export/test_export.py +258 -0
  89. archml-0.1.0/tests/lsp/__init__.py +2 -0
  90. archml-0.1.0/tests/model/__init__.py +2 -0
  91. archml-0.1.0/tests/model/test_model.py +335 -0
  92. archml-0.1.0/tests/sphinx_ext/__init__.py +2 -0
  93. archml-0.1.0/tests/sphinx_ext/test_sphinx_ext.py +680 -0
  94. archml-0.1.0/tests/validation/__init__.py +2 -0
  95. archml-0.1.0/tests/validation/test_checks.py +750 -0
  96. archml-0.1.0/tests/views/__init__.py +2 -0
  97. archml-0.1.0/tests/views/test_diagram.py +452 -0
  98. archml-0.1.0/tests/views/test_dot_sync.py +199 -0
  99. archml-0.1.0/tests/views/test_resolver.py +134 -0
  100. archml-0.1.0/tests/views/test_topology.py +1868 -0
  101. archml-0.1.0/tests/workspace/__init__.py +2 -0
  102. archml-0.1.0/tests/workspace/test_config.py +437 -0
  103. archml-0.1.0/tests/workspace/test_git_ops.py +310 -0
  104. archml-0.1.0/tests/workspace/test_lockfile.py +230 -0
  105. archml-0.1.0/tools/build_js.py +63 -0
  106. archml-0.1.0/tools/ci.py +73 -0
  107. archml-0.1.0/uv.lock +752 -0
@@ -0,0 +1,102 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: ["main"]
6
+
7
+ concurrency:
8
+ group: ${{ github.workflow }}-${{ github.ref }}
9
+ cancel-in-progress: true
10
+
11
+ jobs:
12
+ lint:
13
+ name: Lint & Format
14
+ runs-on: ubuntu-latest
15
+ timeout-minutes: 10
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v5
21
+
22
+ - name: Install dependencies
23
+ run: uv sync
24
+
25
+ - name: Check formatting
26
+ run: uv run ruff format --check src/ tests/
27
+
28
+ - name: Lint
29
+ run: uv run ruff check src/ tests/
30
+
31
+ typecheck:
32
+ name: Type Check
33
+ runs-on: ubuntu-latest
34
+ timeout-minutes: 10
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+
38
+ - name: Install uv
39
+ uses: astral-sh/setup-uv@v5
40
+
41
+ - name: Install dependencies
42
+ run: uv sync
43
+
44
+ - name: Type check
45
+ run: uv run ty check src/
46
+
47
+ test:
48
+ name: Tests (Python ${{ matrix.python-version }})
49
+ runs-on: ubuntu-latest
50
+ timeout-minutes: 10
51
+ strategy:
52
+ fail-fast: false
53
+ matrix:
54
+ python-version: ["3.12", "3.13"]
55
+ steps:
56
+ - uses: actions/checkout@v4
57
+
58
+ - name: Install uv
59
+ uses: astral-sh/setup-uv@v5
60
+
61
+ - name: Set up Python ${{ matrix.python-version }}
62
+ uses: actions/setup-python@v5
63
+ with:
64
+ python-version: ${{ matrix.python-version }}
65
+
66
+ - name: Install dependencies
67
+ run: uv sync
68
+
69
+ - name: Run tests
70
+ run: uv run pytest --cov=archml --cov-report=term-missing
71
+
72
+ build:
73
+ name: Build Wheel
74
+ runs-on: ubuntu-latest
75
+ timeout-minutes: 10
76
+ steps:
77
+ - uses: actions/checkout@v4
78
+
79
+ - name: Set up Node.js
80
+ uses: actions/setup-node@v4
81
+ with:
82
+ node-version: "20"
83
+
84
+ - name: Install uv
85
+ uses: astral-sh/setup-uv@v5
86
+
87
+ - name: Set up Python
88
+ uses: actions/setup-python@v5
89
+ with:
90
+ python-version: "3.12"
91
+
92
+ - name: Build JS frontend
93
+ run: python tools/build_js.py
94
+
95
+ - name: Build wheel
96
+ run: uv build
97
+
98
+ - name: Upload wheel artifact
99
+ uses: actions/upload-artifact@v4
100
+ with:
101
+ name: dist
102
+ path: dist/
@@ -0,0 +1,43 @@
1
+ name: Claude Code (Issues)
2
+
3
+ on:
4
+ issue_comment:
5
+ types: [created]
6
+ pull_request_review_comment:
7
+ types: [created]
8
+ issues:
9
+ types: [opened, assigned]
10
+ pull_request_review:
11
+ types: [submitted]
12
+
13
+ jobs:
14
+ claude:
15
+ if: |
16
+ (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17
+ (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18
+ (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19
+ (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20
+ runs-on: ubuntu-latest
21
+ permissions:
22
+ contents: write
23
+ pull-requests: write
24
+ issues: write
25
+ id-token: write
26
+ actions: read # Required for Claude to read CI results on PRs
27
+ steps:
28
+ - name: Checkout repository
29
+ uses: actions/checkout@v6
30
+ with:
31
+ fetch-depth: 1
32
+
33
+ - name: Install uv
34
+ uses: astral-sh/setup-uv@v5
35
+
36
+ - name: Install dependencies
37
+ run: uv sync
38
+
39
+ - name: Run Claude Code
40
+ id: claude
41
+ uses: anthropics/claude-code-action@v1
42
+ with:
43
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
@@ -0,0 +1,60 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+ id-token: write # required for PyPI trusted publishing (OIDC)
10
+
11
+ jobs:
12
+ build:
13
+ name: Build
14
+ runs-on: ubuntu-latest
15
+ timeout-minutes: 15
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Node.js
20
+ uses: actions/setup-node@v4
21
+ with:
22
+ node-version: "20"
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v5
26
+
27
+ - name: Set up Python
28
+ uses: actions/setup-python@v5
29
+ with:
30
+ python-version: "3.12"
31
+
32
+ - name: Build JS frontend
33
+ run: python tools/build_js.py
34
+
35
+ - name: Build wheel and sdist
36
+ run: uv build
37
+
38
+ - name: Upload dist artifacts
39
+ uses: actions/upload-artifact@v4
40
+ with:
41
+ name: dist
42
+ path: dist/
43
+
44
+ publish:
45
+ name: Publish to PyPI
46
+ needs: build
47
+ runs-on: ubuntu-latest
48
+ timeout-minutes: 10
49
+ environment:
50
+ name: pypi
51
+ url: https://pypi.org/p/archml
52
+ steps:
53
+ - name: Download dist artifacts
54
+ uses: actions/download-artifact@v4
55
+ with:
56
+ name: dist
57
+ path: dist/
58
+
59
+ - name: Publish to PyPI
60
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,217 @@
1
+ # JS build artifacts (generated by tools/build_js.py — do not check in)
2
+ src/archml/static/archml-viewer.js
3
+ src/archml/static/archml-viewer-template.html
4
+ src/archml/static/archml-diagram.css
5
+ src/archml/export/js/node_modules
6
+ src/archml/static/index.html
7
+
8
+ # Byte-compiled / optimized / DLL files
9
+ __pycache__/
10
+ *.py[codz]
11
+ *$py.class
12
+
13
+ # C extensions
14
+ *.so
15
+
16
+ # Distribution / packaging
17
+ .Python
18
+ build/
19
+ develop-eggs/
20
+ dist/
21
+ downloads/
22
+ eggs/
23
+ .eggs/
24
+ lib/
25
+ lib64/
26
+ parts/
27
+ sdist/
28
+ var/
29
+ wheels/
30
+ share/python-wheels/
31
+ *.egg-info/
32
+ .installed.cfg
33
+ *.egg
34
+ MANIFEST
35
+
36
+ # PyInstaller
37
+ # Usually these files are written by a python script from a template
38
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
39
+ *.manifest
40
+ *.spec
41
+
42
+ # Installer logs
43
+ pip-log.txt
44
+ pip-delete-this-directory.txt
45
+
46
+ # Unit test / coverage reports
47
+ htmlcov/
48
+ .tox/
49
+ .nox/
50
+ .coverage
51
+ .coverage.*
52
+ .cache
53
+ nosetests.xml
54
+ coverage.xml
55
+ *.cover
56
+ *.py.cover
57
+ .hypothesis/
58
+ .pytest_cache/
59
+ cover/
60
+
61
+ # Translations
62
+ *.mo
63
+ *.pot
64
+
65
+ # Django stuff:
66
+ *.log
67
+ local_settings.py
68
+ db.sqlite3
69
+ db.sqlite3-journal
70
+
71
+ # Flask stuff:
72
+ instance/
73
+ .webassets-cache
74
+
75
+ # Scrapy stuff:
76
+ .scrapy
77
+
78
+ # Sphinx documentation
79
+ docs/_build/
80
+
81
+ # PyBuilder
82
+ .pybuilder/
83
+ target/
84
+
85
+ # Jupyter Notebook
86
+ .ipynb_checkpoints
87
+
88
+ # IPython
89
+ profile_default/
90
+ ipython_config.py
91
+
92
+ # pyenv
93
+ # For a library or package, you might want to ignore these files since the code is
94
+ # intended to run in multiple environments; otherwise, check them in:
95
+ # .python-version
96
+
97
+ # pipenv
98
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
99
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
100
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
101
+ # install all needed dependencies.
102
+ #Pipfile.lock
103
+
104
+ # UV
105
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
106
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
107
+ # commonly ignored for libraries.
108
+ #uv.lock
109
+
110
+ # poetry
111
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
112
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
113
+ # commonly ignored for libraries.
114
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
115
+ #poetry.lock
116
+ #poetry.toml
117
+
118
+ # pdm
119
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
120
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
121
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
122
+ #pdm.lock
123
+ #pdm.toml
124
+ .pdm-python
125
+ .pdm-build/
126
+
127
+ # pixi
128
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
129
+ #pixi.lock
130
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
131
+ # in the .venv directory. It is recommended not to include this directory in version control.
132
+ .pixi
133
+
134
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
135
+ __pypackages__/
136
+
137
+ # Celery stuff
138
+ celerybeat-schedule
139
+ celerybeat.pid
140
+
141
+ # SageMath parsed files
142
+ *.sage.py
143
+
144
+ # Environments
145
+ .env
146
+ .envrc
147
+ .venv
148
+ env/
149
+ venv/
150
+ ENV/
151
+ env.bak/
152
+ venv.bak/
153
+
154
+ # Spyder project settings
155
+ .spyderproject
156
+ .spyproject
157
+
158
+ # Rope project settings
159
+ .ropeproject
160
+
161
+ # mkdocs documentation
162
+ /site
163
+
164
+ # mypy
165
+ .mypy_cache/
166
+ .dmypy.json
167
+ dmypy.json
168
+
169
+ # Pyre type checker
170
+ .pyre/
171
+
172
+ # pytype static type analyzer
173
+ .pytype/
174
+
175
+ # Cython debug symbols
176
+ cython_debug/
177
+
178
+ # PyCharm
179
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
180
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
181
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
182
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
183
+ #.idea/
184
+
185
+ # Abstra
186
+ # Abstra is an AI-powered process automation framework.
187
+ # Ignore directories containing user credentials, local state, and settings.
188
+ # Learn more at https://abstra.io/docs
189
+ .abstra/
190
+
191
+ # Visual Studio Code
192
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
193
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
194
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
195
+ # you could uncomment the following to ignore the entire vscode folder
196
+ # .vscode/
197
+
198
+ # Ruff stuff:
199
+ .ruff_cache/
200
+
201
+ # PyPI configuration file
202
+ .pypirc
203
+
204
+ # Cursor
205
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
206
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
207
+ # refer to https://docs.cursor.com/context/ignore-files
208
+ .cursorignore
209
+ .cursorindexingignore
210
+
211
+ # Marimo
212
+ marimo/_static/
213
+ marimo/_lsp/
214
+ __marimo__/
215
+
216
+ # Claude
217
+ .claude
archml-0.1.0/CLAUDE.md ADDED
@@ -0,0 +1,94 @@
1
+ # CLAUDE.md — ArchML Development Guide
2
+
3
+ ## Project Overview
4
+
5
+ ArchML is a text-based DSL for defining software architecture alongside code.
6
+ It covers functional, behavioral, and deployment architecture domains with consistency checking, navigable web views, and native Sphinx integration. Architecture files use the `.archml` extension.
7
+
8
+
9
+ ## Tech Stack
10
+
11
+ - **Language**: Python (3.12+)
12
+ - **Package manager**: uv
13
+ - **Linter/formatter**: ruff
14
+ - **Type checker**: ty
15
+ - **Testing**: pytest
16
+ - **Documentation**: Sphinx
17
+ - **Distribution**: PyPI
18
+
19
+
20
+ ## Project Structure (Target)
21
+
22
+ ## Common Commands
23
+
24
+ ```bash
25
+ # Install dependencies
26
+ uv sync
27
+
28
+ # Run all tests
29
+ uv run pytest
30
+
31
+ # Run a specific test file or test
32
+ uv run pytest tests/parser/test_lexer.py
33
+ uv run pytest -k "test_parse_component"
34
+
35
+ # Lint and format
36
+ uv run ruff check src/ tests/
37
+ uv run ruff format src/ tests/
38
+
39
+ # Type check
40
+ uv run ty check src/
41
+ ```
42
+
43
+ ## Development Methodology
44
+
45
+ Every new feature requires thorough testing before it is considered complete. The workflow is:
46
+
47
+ 1. Write or update the relevant specification/design if needed.
48
+ 2. Implement the feature in `src/archml/`.
49
+ 3. Write tests in `tests/` covering normal cases, edge cases, and error cases.
50
+ 4. Ensure all tests pass, ruff reports no issues, and ty finds no type errors using `uv run tools/ci.py`.
51
+ 5. Commit with a clear message describing the change.
52
+
53
+ Tests are not optional.
54
+ A feature without tests is not done.
55
+
56
+
57
+ ## ArchML Language Quick Reference
58
+
59
+ Full syntax specification: `docs/LANGUAGE_SYNTAX.md`
60
+
61
+
62
+
63
+ ## Coding Conventions
64
+
65
+ - Use `ruff` defaults for formatting and linting rules.
66
+ - All public APIs must have type annotations.
67
+ - Prefer dataclasses or attrs for model types.
68
+ - Keep modules focused: one responsibility per module.
69
+ - The test directory structure mirrors the source structure. Every module in `src/archml/<package>/` has a corresponding directory in `tests/<package>/`. Test files are prefixed with `test_`: `src/archml/parser/lexer.py` -> `tests/parser/test_lexer.py`.
70
+ - Use proper docstrings for public functions.
71
+ - Every Python file follows this layout:
72
+
73
+ ```python
74
+ # Copyright ...
75
+ # SPDX-License-Identifier: Apache-2.0
76
+
77
+ import ...
78
+
79
+ # ###############
80
+ # Public Interface
81
+ # ###############
82
+
83
+ def public_function() -> None:
84
+ ...
85
+
86
+ # ################
87
+ # Implementation
88
+ # ################
89
+
90
+ def _private_helper() -> None:
91
+ ...
92
+ ```
93
+
94
+ The copyright header and imports come first. Public interface (classes, functions, constants) is separated from private implementation by section comments. All private members are prefixed with an underscore.