pytaut 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 (209) hide show
  1. pytaut-0.1.0/.github/workflows/ci.yml +20 -0
  2. pytaut-0.1.0/.github/workflows/release.yml +35 -0
  3. pytaut-0.1.0/.gitignore +14 -0
  4. pytaut-0.1.0/CONTRIBUTING.md +17 -0
  5. pytaut-0.1.0/LICENSE +21 -0
  6. pytaut-0.1.0/PKG-INFO +174 -0
  7. pytaut-0.1.0/README.md +152 -0
  8. pytaut-0.1.0/SECURITY.md +6 -0
  9. pytaut-0.1.0/pyproject.toml +159 -0
  10. pytaut-0.1.0/scripts/__init__.py +1 -0
  11. pytaut-0.1.0/scripts/check_conventions.py +208 -0
  12. pytaut-0.1.0/scripts/test.sh +128 -0
  13. pytaut-0.1.0/src/taut/__init__.py +5 -0
  14. pytaut-0.1.0/src/taut/analysis/__init__.py +3 -0
  15. pytaut-0.1.0/src/taut/analysis/contracts.py +108 -0
  16. pytaut-0.1.0/src/taut/analysis/module_analysis.py +31 -0
  17. pytaut-0.1.0/src/taut/analysis/project_analyzer.py +80 -0
  18. pytaut-0.1.0/src/taut/analysis/project_index.py +132 -0
  19. pytaut-0.1.0/src/taut/analysis/python/__init__.py +3 -0
  20. pytaut-0.1.0/src/taut/analysis/python/adapter.py +421 -0
  21. pytaut-0.1.0/src/taut/analysis/python/expression_summary.py +159 -0
  22. pytaut-0.1.0/src/taut/analysis/python/failed_analysis.py +44 -0
  23. pytaut-0.1.0/src/taut/analysis/python/language_adapter.py +87 -0
  24. pytaut-0.1.0/src/taut/analysis/python/symbol_resolver.py +216 -0
  25. pytaut-0.1.0/src/taut/analysis/semantic_model.py +75 -0
  26. pytaut-0.1.0/src/taut/cli.py +280 -0
  27. pytaut-0.1.0/src/taut/configuration/__init__.py +3 -0
  28. pytaut-0.1.0/src/taut/configuration/catalog.py +102 -0
  29. pytaut-0.1.0/src/taut/configuration/effective_policy.py +362 -0
  30. pytaut-0.1.0/src/taut/configuration/manifest.py +121 -0
  31. pytaut-0.1.0/src/taut/configuration/model.py +51 -0
  32. pytaut-0.1.0/src/taut/configuration/rule_standard.py +61 -0
  33. pytaut-0.1.0/src/taut/configuration/validation.py +20 -0
  34. pytaut-0.1.0/src/taut/domain/__init__.py +3 -0
  35. pytaut-0.1.0/src/taut/domain/diagnostics.py +32 -0
  36. pytaut-0.1.0/src/taut/domain/evaluations.py +81 -0
  37. pytaut-0.1.0/src/taut/domain/facts.py +370 -0
  38. pytaut-0.1.0/src/taut/domain/findings.py +81 -0
  39. pytaut-0.1.0/src/taut/domain/frozen.py +48 -0
  40. pytaut-0.1.0/src/taut/domain/ids.py +84 -0
  41. pytaut-0.1.0/src/taut/domain/ignores.py +22 -0
  42. pytaut-0.1.0/src/taut/domain/issues.py +33 -0
  43. pytaut-0.1.0/src/taut/domain/location.py +81 -0
  44. pytaut-0.1.0/src/taut/domain/provenance.py +21 -0
  45. pytaut-0.1.0/src/taut/domain/reports.py +85 -0
  46. pytaut-0.1.0/src/taut/domain/snapshot.py +79 -0
  47. pytaut-0.1.0/src/taut/finding_processing/__init__.py +3 -0
  48. pytaut-0.1.0/src/taut/finding_processing/finding_processor.py +192 -0
  49. pytaut-0.1.0/src/taut/finding_processing/report_builder.py +55 -0
  50. pytaut-0.1.0/src/taut/loading/__init__.py +3 -0
  51. pytaut-0.1.0/src/taut/loading/boundary_extension_schema.py +43 -0
  52. pytaut-0.1.0/src/taut/loading/builtin_catalog.py +49 -0
  53. pytaut-0.1.0/src/taut/loading/code_conventions.py +136 -0
  54. pytaut-0.1.0/src/taut/loading/config_loader.py +488 -0
  55. pytaut-0.1.0/src/taut/loading/configuration_document.py +222 -0
  56. pytaut-0.1.0/src/taut/loading/default_configuration.py +59 -0
  57. pytaut-0.1.0/src/taut/loading/errors.py +2 -0
  58. pytaut-0.1.0/src/taut/loading/inline_ignores.py +81 -0
  59. pytaut-0.1.0/src/taut/loading/source_discovery.py +159 -0
  60. pytaut-0.1.0/src/taut/policy/__init__.py +3 -0
  61. pytaut-0.1.0/src/taut/policy/context.py +44 -0
  62. pytaut-0.1.0/src/taut/policy/decision_digest.py +30 -0
  63. pytaut-0.1.0/src/taut/policy/engine.py +187 -0
  64. pytaut-0.1.0/src/taut/policy/indexes.py +82 -0
  65. pytaut-0.1.0/src/taut/policy/registry.py +27 -0
  66. pytaut-0.1.0/src/taut/policy/rule.py +75 -0
  67. pytaut-0.1.0/src/taut/policy/rules/__init__.py +99 -0
  68. pytaut-0.1.0/src/taut/policy/rules/api_contracts.py +484 -0
  69. pytaut-0.1.0/src/taut/policy/rules/architecture.py +142 -0
  70. pytaut-0.1.0/src/taut/policy/rules/async_safety.py +73 -0
  71. pytaut-0.1.0/src/taut/policy/rules/boundary.py +164 -0
  72. pytaut-0.1.0/src/taut/policy/rules/catalog_coverage.py +68 -0
  73. pytaut-0.1.0/src/taut/policy/rules/classification.py +54 -0
  74. pytaut-0.1.0/src/taut/policy/rules/construction_boundaries.py +191 -0
  75. pytaut-0.1.0/src/taut/policy/rules/conventions.py +144 -0
  76. pytaut-0.1.0/src/taut/policy/rules/enums.py +217 -0
  77. pytaut-0.1.0/src/taut/policy/rules/exceptions.py +241 -0
  78. pytaut-0.1.0/src/taut/policy/rules/external_calls.py +134 -0
  79. pytaut-0.1.0/src/taut/policy/rules/helpers.py +48 -0
  80. pytaut-0.1.0/src/taut/policy/rules/ignore_audit.py +35 -0
  81. pytaut-0.1.0/src/taut/policy/rules/layer_boundaries.py +440 -0
  82. pytaut-0.1.0/src/taut/policy/rules/model_shapes.py +366 -0
  83. pytaut-0.1.0/src/taut/policy/rules/persistence.py +370 -0
  84. pytaut-0.1.0/src/taut/policy/rules/responsibility_boundaries.py +227 -0
  85. pytaut-0.1.0/src/taut/policy/rules/runtime_safety.py +168 -0
  86. pytaut-0.1.0/src/taut/policy/rules/security.py +122 -0
  87. pytaut-0.1.0/src/taut/policy/rules/session.py +192 -0
  88. pytaut-0.1.0/src/taut/policy/rules/test_boundaries.py +110 -0
  89. pytaut-0.1.0/src/taut/policy/rules/time_access.py +86 -0
  90. pytaut-0.1.0/src/taut/policy/rules/transaction.py +106 -0
  91. pytaut-0.1.0/src/taut/policy/scheduler.py +51 -0
  92. pytaut-0.1.0/src/taut/py.typed +1 -0
  93. pytaut-0.1.0/src/taut/reporting/__init__.py +3 -0
  94. pytaut-0.1.0/src/taut/reporting/json.py +117 -0
  95. pytaut-0.1.0/src/taut/reporting/text.py +211 -0
  96. pytaut-0.1.0/tests/__init__.py +1 -0
  97. pytaut-0.1.0/tests/contract/test_architecture_contracts.py +23 -0
  98. pytaut-0.1.0/tests/contract/test_rule_fixture_semantics.py +268 -0
  99. pytaut-0.1.0/tests/fixtures/rules/adapter_type/compliant.py +5 -0
  100. pytaut-0.1.0/tests/fixtures/rules/adapter_type/violation.py +5 -0
  101. pytaut-0.1.0/tests/fixtures/rules/api_endpoint/compliant.py +9 -0
  102. pytaut-0.1.0/tests/fixtures/rules/api_endpoint/violation.py +8 -0
  103. pytaut-0.1.0/tests/fixtures/rules/api_field/compliant.py +5 -0
  104. pytaut-0.1.0/tests/fixtures/rules/api_field/violation.py +5 -0
  105. pytaut-0.1.0/tests/fixtures/rules/api_metadata/compliant.py +8 -0
  106. pytaut-0.1.0/tests/fixtures/rules/api_metadata/violation.py +8 -0
  107. pytaut-0.1.0/tests/fixtures/rules/architecture/compliant/router.py +3 -0
  108. pytaut-0.1.0/tests/fixtures/rules/architecture/cycle_violation/a.py +3 -0
  109. pytaut-0.1.0/tests/fixtures/rules/architecture/cycle_violation/b.py +3 -0
  110. pytaut-0.1.0/tests/fixtures/rules/architecture/direction_violation/domain.py +3 -0
  111. pytaut-0.1.0/tests/fixtures/rules/async_safety/compliant.py +5 -0
  112. pytaut-0.1.0/tests/fixtures/rules/async_safety/violation.py +5 -0
  113. pytaut-0.1.0/tests/fixtures/rules/boundary/compliant.py +5 -0
  114. pytaut-0.1.0/tests/fixtures/rules/boundary/violation.py +6 -0
  115. pytaut-0.1.0/tests/fixtures/rules/catalog/compliant.py +3 -0
  116. pytaut-0.1.0/tests/fixtures/rules/catalog/violation.py +3 -0
  117. pytaut-0.1.0/tests/fixtures/rules/classification/compliant.py +1 -0
  118. pytaut-0.1.0/tests/fixtures/rules/classification/violation.py +1 -0
  119. pytaut-0.1.0/tests/fixtures/rules/database_time/compliant.py +3 -0
  120. pytaut-0.1.0/tests/fixtures/rules/database_time/violation.py +3 -0
  121. pytaut-0.1.0/tests/fixtures/rules/db_enum/compliant.py +9 -0
  122. pytaut-0.1.0/tests/fixtures/rules/db_enum/violation.py +4 -0
  123. pytaut-0.1.0/tests/fixtures/rules/dependency/compliant.py +5 -0
  124. pytaut-0.1.0/tests/fixtures/rules/dependency/violation.py +5 -0
  125. pytaut-0.1.0/tests/fixtures/rules/dto/compliant.py +6 -0
  126. pytaut-0.1.0/tests/fixtures/rules/dto/violation.py +6 -0
  127. pytaut-0.1.0/tests/fixtures/rules/dto_name/compliant.py +6 -0
  128. pytaut-0.1.0/tests/fixtures/rules/dto_name/violation.py +6 -0
  129. pytaut-0.1.0/tests/fixtures/rules/entry_boundary/compliant.py +5 -0
  130. pytaut-0.1.0/tests/fixtures/rules/entry_boundary/violation.py +7 -0
  131. pytaut-0.1.0/tests/fixtures/rules/enum/compliant.py +5 -0
  132. pytaut-0.1.0/tests/fixtures/rules/enum/violation.py +5 -0
  133. pytaut-0.1.0/tests/fixtures/rules/exception/compliant.py +5 -0
  134. pytaut-0.1.0/tests/fixtures/rules/exception/violation.py +5 -0
  135. pytaut-0.1.0/tests/fixtures/rules/external/http_compliant.py +3 -0
  136. pytaut-0.1.0/tests/fixtures/rules/external/http_violation.py +3 -0
  137. pytaut-0.1.0/tests/fixtures/rules/external/log_compliant.py +5 -0
  138. pytaut-0.1.0/tests/fixtures/rules/external/log_violation.py +3 -0
  139. pytaut-0.1.0/tests/fixtures/rules/ignore/compliant.py +1 -0
  140. pytaut-0.1.0/tests/fixtures/rules/ignore/violation.py +1 -0
  141. pytaut-0.1.0/tests/fixtures/rules/import/compliant.py +3 -0
  142. pytaut-0.1.0/tests/fixtures/rules/import/violation.py +3 -0
  143. pytaut-0.1.0/tests/fixtures/rules/model_boundary/compliant.py +5 -0
  144. pytaut-0.1.0/tests/fixtures/rules/model_boundary/violation.py +5 -0
  145. pytaut-0.1.0/tests/fixtures/rules/query_boundary/compliant.py +6 -0
  146. pytaut-0.1.0/tests/fixtures/rules/query_boundary/violation.py +6 -0
  147. pytaut-0.1.0/tests/fixtures/rules/raw_sql/compliant.py +3 -0
  148. pytaut-0.1.0/tests/fixtures/rules/raw_sql/violation.py +3 -0
  149. pytaut-0.1.0/tests/fixtures/rules/relationship/compliant.py +3 -0
  150. pytaut-0.1.0/tests/fixtures/rules/relationship/violation.py +3 -0
  151. pytaut-0.1.0/tests/fixtures/rules/response_mapping/compliant.py +4 -0
  152. pytaut-0.1.0/tests/fixtures/rules/response_mapping/violation.py +5 -0
  153. pytaut-0.1.0/tests/fixtures/rules/responsibility_boundary/adapter_compliant.py +3 -0
  154. pytaut-0.1.0/tests/fixtures/rules/responsibility_boundary/adapter_violation.py +3 -0
  155. pytaut-0.1.0/tests/fixtures/rules/responsibility_boundary/contract_compliant.py +6 -0
  156. pytaut-0.1.0/tests/fixtures/rules/responsibility_boundary/contract_violation.py +3 -0
  157. pytaut-0.1.0/tests/fixtures/rules/responsibility_boundary/service_compliant.py +3 -0
  158. pytaut-0.1.0/tests/fixtures/rules/responsibility_boundary/service_violation.py +3 -0
  159. pytaut-0.1.0/tests/fixtures/rules/runtime/call_compliant.py +3 -0
  160. pytaut-0.1.0/tests/fixtures/rules/runtime/call_violation.py +3 -0
  161. pytaut-0.1.0/tests/fixtures/rules/runtime/import_compliant.py +3 -0
  162. pytaut-0.1.0/tests/fixtures/rules/runtime/import_violation.py +3 -0
  163. pytaut-0.1.0/tests/fixtures/rules/runtime/transaction_compliant.py +5 -0
  164. pytaut-0.1.0/tests/fixtures/rules/runtime/transaction_violation.py +7 -0
  165. pytaut-0.1.0/tests/fixtures/rules/schema_config/compliant.py +6 -0
  166. pytaut-0.1.0/tests/fixtures/rules/schema_config/violation.py +5 -0
  167. pytaut-0.1.0/tests/fixtures/rules/schema_inheritance/compliant.py +5 -0
  168. pytaut-0.1.0/tests/fixtures/rules/schema_inheritance/violation.py +5 -0
  169. pytaut-0.1.0/tests/fixtures/rules/security/compliant.py +3 -0
  170. pytaut-0.1.0/tests/fixtures/rules/security/violation.py +3 -0
  171. pytaut-0.1.0/tests/fixtures/rules/service_boundary/compliant.py +6 -0
  172. pytaut-0.1.0/tests/fixtures/rules/service_boundary/violation.py +7 -0
  173. pytaut-0.1.0/tests/fixtures/rules/session/compliant.py +6 -0
  174. pytaut-0.1.0/tests/fixtures/rules/session/violation.py +6 -0
  175. pytaut-0.1.0/tests/fixtures/rules/session_nested/compliant.py +8 -0
  176. pytaut-0.1.0/tests/fixtures/rules/session_nested/violation.py +6 -0
  177. pytaut-0.1.0/tests/fixtures/rules/session_parameter/compliant.py +2 -0
  178. pytaut-0.1.0/tests/fixtures/rules/session_parameter/violation.py +5 -0
  179. pytaut-0.1.0/tests/fixtures/rules/settings/compliant.py +5 -0
  180. pytaut-0.1.0/tests/fixtures/rules/settings/violation.py +5 -0
  181. pytaut-0.1.0/tests/fixtures/rules/size/compliant.py +1 -0
  182. pytaut-0.1.0/tests/fixtures/rules/size/violation.py +3 -0
  183. pytaut-0.1.0/tests/fixtures/rules/snapshot/compliant.py +5 -0
  184. pytaut-0.1.0/tests/fixtures/rules/snapshot/violation.py +5 -0
  185. pytaut-0.1.0/tests/fixtures/rules/test_http/compliant.py +5 -0
  186. pytaut-0.1.0/tests/fixtures/rules/test_http/violation.py +5 -0
  187. pytaut-0.1.0/tests/fixtures/rules/test_layout/compliant.py +1 -0
  188. pytaut-0.1.0/tests/fixtures/rules/test_layout/violation.py +1 -0
  189. pytaut-0.1.0/tests/fixtures/rules/time/compliant.py +5 -0
  190. pytaut-0.1.0/tests/fixtures/rules/time/violation.py +5 -0
  191. pytaut-0.1.0/tests/fixtures/rules/transaction/compliant.py +5 -0
  192. pytaut-0.1.0/tests/fixtures/rules/transaction/violation.py +5 -0
  193. pytaut-0.1.0/tests/fixtures/rules/wiring/compliant.py +5 -0
  194. pytaut-0.1.0/tests/fixtures/rules/wiring/violation.py +5 -0
  195. pytaut-0.1.0/tests/integration/test_cli.py +234 -0
  196. pytaut-0.1.0/tests/unit/analysis/test_project_index.py +78 -0
  197. pytaut-0.1.0/tests/unit/analysis/test_python_analysis.py +255 -0
  198. pytaut-0.1.0/tests/unit/configuration/test_configuration.py +375 -0
  199. pytaut-0.1.0/tests/unit/domain/test_immutable_contracts.py +135 -0
  200. pytaut-0.1.0/tests/unit/domain/test_validation_edges.py +178 -0
  201. pytaut-0.1.0/tests/unit/finding_processing/test_finding_processing.py +82 -0
  202. pytaut-0.1.0/tests/unit/finding_processing/test_report_builder.py +64 -0
  203. pytaut-0.1.0/tests/unit/loading/test_source_discovery.py +90 -0
  204. pytaut-0.1.0/tests/unit/policy/test_builtin_rules.py +1740 -0
  205. pytaut-0.1.0/tests/unit/policy/test_engine_contracts.py +222 -0
  206. pytaut-0.1.0/tests/unit/reporting/test_reporters.py +105 -0
  207. pytaut-0.1.0/tests/utils/__init__.py +1 -0
  208. pytaut-0.1.0/tests/utils/builders.py +210 -0
  209. pytaut-0.1.0/uv.lock +499 -0
@@ -0,0 +1,20 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v7.0.1
16
+ - uses: astral-sh/setup-uv@v10.0.1
17
+ with:
18
+ python-version: "3.12"
19
+ enable-cache: true
20
+ - run: bash scripts/test.sh
@@ -0,0 +1,35 @@
1
+ name: Release to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ verify:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v7.0.1
15
+ - uses: astral-sh/setup-uv@v10.0.1
16
+ with:
17
+ python-version: "3.12"
18
+ enable-cache: true
19
+ - run: bash scripts/test.sh
20
+
21
+ publish:
22
+ needs: verify
23
+ runs-on: ubuntu-latest
24
+ environment:
25
+ name: pypi
26
+ url: https://pypi.org/project/pytaut/
27
+ permissions:
28
+ id-token: write
29
+ steps:
30
+ - uses: actions/checkout@v7.0.1
31
+ - uses: astral-sh/setup-uv@v10.0.1
32
+ with:
33
+ python-version: "3.12"
34
+ - run: uv build
35
+ - uses: pypa/gh-action-pypi-publish@v1.14.2
@@ -0,0 +1,14 @@
1
+ .DS_Store
2
+ .research/
3
+ .venv/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .mypy_cache/
7
+ __pycache__/
8
+ *.pyc
9
+ .coverage
10
+ htmlcov/
11
+ build/
12
+ dist/
13
+ *.egg-info/
14
+ /docs/
@@ -0,0 +1,17 @@
1
+ # Contributing to taut
2
+
3
+ Bug reports and rule proposals are welcome through GitHub Issues. A rule must describe a condition
4
+ that can be determined reliably from code, rather than a subjective preference.
5
+
6
+ Run the full verification suite before submitting a change:
7
+
8
+ ```bash
9
+ bash scripts/test.sh
10
+ ```
11
+
12
+ A new rule needs tests that demonstrate:
13
+
14
+ - a violation is detected;
15
+ - compliant code passes;
16
+ - unrelated code zones remain unaffected; and
17
+ - invalid configuration fails with a clear error.
pytaut-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TaeWoo Kim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pytaut-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,174 @@
1
+ Metadata-Version: 2.5
2
+ Name: pytaut
3
+ Version: 0.1.0
4
+ Summary: Deterministic architecture policy checks for Python backends
5
+ Project-URL: Documentation, https://github.com/taewoo-dev/taut#readme
6
+ Project-URL: Issues, https://github.com/taewoo-dev/taut/issues
7
+ Project-URL: Repository, https://github.com/taewoo-dev/taut
8
+ Author-email: TaeWoo Kim <173425210+taewoo-dev@users.noreply.github.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: architecture,linter,policy,python,static-analysis
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Quality Assurance
20
+ Requires-Python: >=3.12
21
+ Description-Content-Type: text/markdown
22
+
23
+ # taut
24
+
25
+ `taut` makes hidden Python backend conventions explicit and blocks only violations it can
26
+ determine reliably. The same source and configuration always produce the same result. It does
27
+ not hard-code the names or directory layout of any company or service.
28
+
29
+ The first PyPI release is being prepared. Until it is published, install directly from GitHub:
30
+
31
+ ```bash
32
+ uv add --dev "pytaut @ git+https://github.com/taewoo-dev/taut.git"
33
+ uv run taut check .
34
+ ```
35
+
36
+ After the PyPI release, installation becomes:
37
+
38
+ ```bash
39
+ uv add --dev pytaut
40
+ ```
41
+
42
+ ## Configuration
43
+
44
+ Define repository roles and allowed dependencies in `pyproject.toml`. Strict mode is enabled by
45
+ default, and the built-in maximum file length is 700 lines.
46
+
47
+ ```toml
48
+ [tool.taut]
49
+ strict = true
50
+ source_roots = ["."]
51
+
52
+ [tool.taut.roles]
53
+ router = ["app/router/*.py", "app/router/**/*.py"]
54
+ service = ["app/service/*.py", "app/service/**/*.py"]
55
+
56
+ [tool.taut.allow]
57
+ router = ["router", "service"]
58
+ service = ["service"]
59
+
60
+ [tool.taut.zones]
61
+ test = ["tests/*.py", "tests/**/*.py"]
62
+
63
+ [tool.taut.transaction]
64
+ owner_roles = ["service"]
65
+ session_providers = ["app.database.get_async_session"]
66
+ ```
67
+
68
+ Built-in policies cover external calls, databases, security, DTOs, and schemas. Add only the
69
+ repository-specific differences:
70
+
71
+ ```toml
72
+ [tool.taut.external]
73
+ modules = ["company_sdk"]
74
+ wrappers = ["app.adapters.external_call"]
75
+
76
+ [tool.taut.enum]
77
+ shared_modules = ["app.core.enums"]
78
+ ```
79
+
80
+ ## Commands
81
+
82
+ ```bash
83
+ taut config validate .
84
+ taut check .
85
+ taut check . --verbose
86
+ taut check . --format json
87
+ taut rules
88
+ taut rules ASYNC001
89
+ ```
90
+
91
+ To check another local repository:
92
+
93
+ ```bash
94
+ uvx --no-cache --from /path/to/taut taut check /path/to/project
95
+ ```
96
+
97
+ For a one-time audit that must not modify the target repository, provide an absolute path to an
98
+ external configuration file:
99
+
100
+ ```bash
101
+ taut check /path/to/project --config /path/to/audit-policy.toml
102
+ ```
103
+
104
+ Role patterns and `source_roots` are resolved relative to the target project, not the
105
+ configuration file. The legacy `.policy/policy.toml` format and explicit external configuration
106
+ files remain supported.
107
+
108
+ ## Results
109
+
110
+ The default terminal output prints one finding per line followed by the error and warning totals.
111
+ Long findings wrap to the next indented line. Non-terminal output uses a width of 120 characters;
112
+ override it with an option such as `--width 100`. Use `--verbose` only when you need related
113
+ locations, remediation guidance, decision counts, and the decision digest.
114
+
115
+ - Exit code `0`: no enforced violations
116
+ - Exit code `1`: one or more enforced violations
117
+ - Exit code `2`: invalid configuration, analysis failure, or an enforced rule that could not be
118
+ evaluated
119
+
120
+ When an exception is unavoidable, suppress only the exact rule on the affected line:
121
+
122
+ ```python
123
+ legacy_call() # taut: ignore[ASYNC001]
124
+ ```
125
+
126
+ An ignore without a rule ID, or with an unknown rule ID, is a configuration error. An ignore that
127
+ does not suppress a real violation is reported as `IGNORE001`. File-wide ignores, violation
128
+ baselines, and expiration management are intentionally unsupported.
129
+
130
+ Raw SQL is not generally allowed. Application code should use SQLAlchemy expressions. A necessary
131
+ raw query must pass through a registered shared wrapper configured with `raw_query_roles` and
132
+ `raw_query_wrappers`. Fixed Model `server_default` expressions and partial Index predicates are
133
+ allowed only within `schema_sql_roles` and `schema_sql_argument_names`.
134
+
135
+ Registered raw-query calls must provide `name`, `statement`, and `parameters` as explicit keyword
136
+ arguments. `name` and `statement` must be string literals, preventing SQL construction through
137
+ f-strings or string concatenation.
138
+
139
+ ## Built-in rules
140
+
141
+ With the default `strict = true`, `CAT001` is advisory and the other 47 rules are enforced.
142
+ Individual rules cannot be disabled. Use `strict = false` before adoption to report every finding
143
+ as a warning.
144
+
145
+ | Group | Rules |
146
+ |---|---|
147
+ | Architecture | `ARCH000`-`002`, `BOUNDARY001`-`003`, `ENTRY001`, `SERVICE001`, `QUERY001`, `MODEL001`, `ADAPTER001`-`002`, `WIRING001`, `CONFIG001`, `DEPENDS001` |
148
+ | Runtime safety | `TIME001`, `ASYNC001`, `RUNTIME001`, `IMPORT001`, `IMPORT002`, `SIZE001`, `SEC001` |
149
+ | Database and transactions | `TX001`, `TX002`, `SESSION001`-`003`, `ORM001`, `ORM002`, `DB001`, `SQL001` |
150
+ | External calls | `HTTP001`, `LOG001`, `CAT001` |
151
+ | Data contracts | `DTO001`, `DTO002`, `SNAPSHOT001`, `SCHEMA001`-`003`, `API001`-`003`, `ENUM001`, `EXC001` |
152
+ | Test boundaries | `TEST001`, `TEST002` |
153
+ | Inline ignores | `IGNORE001` |
154
+
155
+ Each rule declares whether it applies to `prod`, `test`, `migration`, or `script` code. Missing
156
+ roles, dependency cycles, import placement, file size, dynamic execution, async safety, and
157
+ security access are checked in every zone. API, DTO, database, and service-boundary rules apply to
158
+ production code.
159
+
160
+ An unregistered call that might have an external effect cannot be proven unsafe, so it is reported
161
+ as a `CAT001` warning. After classifying the call, add it to the project effect catalog for precise
162
+ enforcement.
163
+
164
+ ## Development
165
+
166
+ ```bash
167
+ bash scripts/test.sh
168
+ bash scripts/test.sh --only tests/unit/policy/test_builtin_rules.py -x
169
+ ```
170
+
171
+ The full check runs the repository's own policy rules, Ruff, mypy strict, Pyright strict, pytest
172
+ with at least 90% branch coverage, package builds, and an isolated wheel installation.
173
+
174
+ See [`docs/README.md`](docs/README.md) for the current design documents.
pytaut-0.1.0/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # taut
2
+
3
+ `taut` makes hidden Python backend conventions explicit and blocks only violations it can
4
+ determine reliably. The same source and configuration always produce the same result. It does
5
+ not hard-code the names or directory layout of any company or service.
6
+
7
+ The first PyPI release is being prepared. Until it is published, install directly from GitHub:
8
+
9
+ ```bash
10
+ uv add --dev "pytaut @ git+https://github.com/taewoo-dev/taut.git"
11
+ uv run taut check .
12
+ ```
13
+
14
+ After the PyPI release, installation becomes:
15
+
16
+ ```bash
17
+ uv add --dev pytaut
18
+ ```
19
+
20
+ ## Configuration
21
+
22
+ Define repository roles and allowed dependencies in `pyproject.toml`. Strict mode is enabled by
23
+ default, and the built-in maximum file length is 700 lines.
24
+
25
+ ```toml
26
+ [tool.taut]
27
+ strict = true
28
+ source_roots = ["."]
29
+
30
+ [tool.taut.roles]
31
+ router = ["app/router/*.py", "app/router/**/*.py"]
32
+ service = ["app/service/*.py", "app/service/**/*.py"]
33
+
34
+ [tool.taut.allow]
35
+ router = ["router", "service"]
36
+ service = ["service"]
37
+
38
+ [tool.taut.zones]
39
+ test = ["tests/*.py", "tests/**/*.py"]
40
+
41
+ [tool.taut.transaction]
42
+ owner_roles = ["service"]
43
+ session_providers = ["app.database.get_async_session"]
44
+ ```
45
+
46
+ Built-in policies cover external calls, databases, security, DTOs, and schemas. Add only the
47
+ repository-specific differences:
48
+
49
+ ```toml
50
+ [tool.taut.external]
51
+ modules = ["company_sdk"]
52
+ wrappers = ["app.adapters.external_call"]
53
+
54
+ [tool.taut.enum]
55
+ shared_modules = ["app.core.enums"]
56
+ ```
57
+
58
+ ## Commands
59
+
60
+ ```bash
61
+ taut config validate .
62
+ taut check .
63
+ taut check . --verbose
64
+ taut check . --format json
65
+ taut rules
66
+ taut rules ASYNC001
67
+ ```
68
+
69
+ To check another local repository:
70
+
71
+ ```bash
72
+ uvx --no-cache --from /path/to/taut taut check /path/to/project
73
+ ```
74
+
75
+ For a one-time audit that must not modify the target repository, provide an absolute path to an
76
+ external configuration file:
77
+
78
+ ```bash
79
+ taut check /path/to/project --config /path/to/audit-policy.toml
80
+ ```
81
+
82
+ Role patterns and `source_roots` are resolved relative to the target project, not the
83
+ configuration file. The legacy `.policy/policy.toml` format and explicit external configuration
84
+ files remain supported.
85
+
86
+ ## Results
87
+
88
+ The default terminal output prints one finding per line followed by the error and warning totals.
89
+ Long findings wrap to the next indented line. Non-terminal output uses a width of 120 characters;
90
+ override it with an option such as `--width 100`. Use `--verbose` only when you need related
91
+ locations, remediation guidance, decision counts, and the decision digest.
92
+
93
+ - Exit code `0`: no enforced violations
94
+ - Exit code `1`: one or more enforced violations
95
+ - Exit code `2`: invalid configuration, analysis failure, or an enforced rule that could not be
96
+ evaluated
97
+
98
+ When an exception is unavoidable, suppress only the exact rule on the affected line:
99
+
100
+ ```python
101
+ legacy_call() # taut: ignore[ASYNC001]
102
+ ```
103
+
104
+ An ignore without a rule ID, or with an unknown rule ID, is a configuration error. An ignore that
105
+ does not suppress a real violation is reported as `IGNORE001`. File-wide ignores, violation
106
+ baselines, and expiration management are intentionally unsupported.
107
+
108
+ Raw SQL is not generally allowed. Application code should use SQLAlchemy expressions. A necessary
109
+ raw query must pass through a registered shared wrapper configured with `raw_query_roles` and
110
+ `raw_query_wrappers`. Fixed Model `server_default` expressions and partial Index predicates are
111
+ allowed only within `schema_sql_roles` and `schema_sql_argument_names`.
112
+
113
+ Registered raw-query calls must provide `name`, `statement`, and `parameters` as explicit keyword
114
+ arguments. `name` and `statement` must be string literals, preventing SQL construction through
115
+ f-strings or string concatenation.
116
+
117
+ ## Built-in rules
118
+
119
+ With the default `strict = true`, `CAT001` is advisory and the other 47 rules are enforced.
120
+ Individual rules cannot be disabled. Use `strict = false` before adoption to report every finding
121
+ as a warning.
122
+
123
+ | Group | Rules |
124
+ |---|---|
125
+ | Architecture | `ARCH000`-`002`, `BOUNDARY001`-`003`, `ENTRY001`, `SERVICE001`, `QUERY001`, `MODEL001`, `ADAPTER001`-`002`, `WIRING001`, `CONFIG001`, `DEPENDS001` |
126
+ | Runtime safety | `TIME001`, `ASYNC001`, `RUNTIME001`, `IMPORT001`, `IMPORT002`, `SIZE001`, `SEC001` |
127
+ | Database and transactions | `TX001`, `TX002`, `SESSION001`-`003`, `ORM001`, `ORM002`, `DB001`, `SQL001` |
128
+ | External calls | `HTTP001`, `LOG001`, `CAT001` |
129
+ | Data contracts | `DTO001`, `DTO002`, `SNAPSHOT001`, `SCHEMA001`-`003`, `API001`-`003`, `ENUM001`, `EXC001` |
130
+ | Test boundaries | `TEST001`, `TEST002` |
131
+ | Inline ignores | `IGNORE001` |
132
+
133
+ Each rule declares whether it applies to `prod`, `test`, `migration`, or `script` code. Missing
134
+ roles, dependency cycles, import placement, file size, dynamic execution, async safety, and
135
+ security access are checked in every zone. API, DTO, database, and service-boundary rules apply to
136
+ production code.
137
+
138
+ An unregistered call that might have an external effect cannot be proven unsafe, so it is reported
139
+ as a `CAT001` warning. After classifying the call, add it to the project effect catalog for precise
140
+ enforcement.
141
+
142
+ ## Development
143
+
144
+ ```bash
145
+ bash scripts/test.sh
146
+ bash scripts/test.sh --only tests/unit/policy/test_builtin_rules.py -x
147
+ ```
148
+
149
+ The full check runs the repository's own policy rules, Ruff, mypy strict, Pyright strict, pytest
150
+ with at least 90% branch coverage, package builds, and an isolated wheel installation.
151
+
152
+ See [`docs/README.md`](docs/README.md) for the current design documents.
@@ -0,0 +1,6 @@
1
+ # Security Policy
2
+
3
+ Do not report vulnerabilities through a public Issue. Use GitHub private vulnerability reporting
4
+ and include reproduction steps and the expected impact.
5
+
6
+ Only the latest published version is currently supported with security fixes.
@@ -0,0 +1,159 @@
1
+ [project]
2
+ name = "pytaut"
3
+ version = "0.1.0"
4
+ description = "Deterministic architecture policy checks for Python backends"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [
10
+ { name = "TaeWoo Kim", email = "173425210+taewoo-dev@users.noreply.github.com" },
11
+ ]
12
+ keywords = ["architecture", "linter", "policy", "python", "static-analysis"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Environment :: Console",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Topic :: Software Development :: Quality Assurance",
22
+ ]
23
+ dependencies = []
24
+
25
+ [project.urls]
26
+ Documentation = "https://github.com/taewoo-dev/taut#readme"
27
+ Issues = "https://github.com/taewoo-dev/taut/issues"
28
+ Repository = "https://github.com/taewoo-dev/taut"
29
+
30
+ [project.scripts]
31
+ taut = "taut.cli:main"
32
+
33
+ [dependency-groups]
34
+ dev = [
35
+ "mypy>=1.18",
36
+ "pytest>=8.4",
37
+ "pytest-cov>=6",
38
+ "pyright>=1.1.405",
39
+ "ruff>=0.12",
40
+ ]
41
+
42
+ [build-system]
43
+ requires = ["hatchling>=1.27"]
44
+ build-backend = "hatchling.build"
45
+
46
+ [tool.hatch.build.targets.wheel]
47
+ packages = ["src/taut"]
48
+
49
+ [tool.hatch.build.targets.sdist]
50
+ exclude = [
51
+ ]
52
+
53
+ [tool.pytest.ini_options]
54
+ minversion = "8.0"
55
+ testpaths = ["tests"]
56
+ pythonpath = ["."]
57
+ python_files = ["test_*.py"]
58
+ python_classes = ["Test*"]
59
+ python_functions = ["test_*"]
60
+ addopts = ["-q", "--tb=short", "--strict-markers", "-ra"]
61
+ markers = [
62
+ "integration: checks multiple engine stages or the installed CLI",
63
+ "contract: checks a stable architecture or result contract",
64
+ ]
65
+ filterwarnings = ["error"]
66
+
67
+ [tool.coverage.run]
68
+ source = ["taut"]
69
+ branch = true
70
+
71
+ [tool.coverage.report]
72
+ show_missing = true
73
+ skip_covered = false
74
+ fail_under = 90
75
+
76
+ [tool.ruff]
77
+ line-length = 100
78
+ target-version = "py312"
79
+ src = ["src", "tests", "scripts"]
80
+
81
+ [tool.ruff.lint]
82
+ select = ["E", "F", "I", "UP", "B", "ASYNC", "SIM", "RUF"]
83
+
84
+ [tool.mypy]
85
+ python_version = "3.12"
86
+ strict = true
87
+ explicit_package_bases = true
88
+ mypy_path = ["src"]
89
+ files = ["src", "tests", "scripts"]
90
+ exclude = ["^tests/fixtures/"]
91
+
92
+ [tool.pyright]
93
+ include = ["src", "tests", "scripts"]
94
+ extraPaths = ["."]
95
+ exclude = ["tests/fixtures"]
96
+ pythonVersion = "3.12"
97
+ typeCheckingMode = "strict"
98
+
99
+ [tool.taut]
100
+ strict = true
101
+ include = ["src/*.py", "src/**/*.py"]
102
+ source_roots = ["src"]
103
+
104
+ [tool.taut.roles]
105
+ domain = ["src/taut/domain/*.py", "src/taut/domain/**/*.py"]
106
+ analysis = ["src/taut/analysis/*.py", "src/taut/analysis/**/*.py"]
107
+ configuration = [
108
+ "src/taut/configuration/*.py",
109
+ "src/taut/configuration/**/*.py",
110
+ ]
111
+ policy = ["src/taut/policy/*.py", "src/taut/policy/**/*.py"]
112
+ finding_processing = [
113
+ "src/taut/finding_processing/*.py",
114
+ "src/taut/finding_processing/**/*.py",
115
+ ]
116
+ incremental = ["src/taut/incremental/*.py", "src/taut/incremental/**/*.py"]
117
+ loading = ["src/taut/loading/*.py", "src/taut/loading/**/*.py"]
118
+ reporting = ["src/taut/reporting/*.py", "src/taut/reporting/**/*.py"]
119
+ composition = ["src/taut/__init__.py", "src/taut/cli.py"]
120
+
121
+ [tool.taut.allow]
122
+ domain = ["domain"]
123
+ analysis = ["domain", "analysis"]
124
+ configuration = ["domain", "configuration"]
125
+ policy = ["domain", "analysis", "configuration", "policy"]
126
+ finding_processing = ["domain", "configuration", "finding_processing"]
127
+ incremental = ["domain", "analysis", "policy", "incremental"]
128
+ loading = ["domain", "analysis", "configuration", "loading"]
129
+ reporting = ["domain", "reporting"]
130
+ composition = [
131
+ "domain",
132
+ "analysis",
133
+ "configuration",
134
+ "policy",
135
+ "finding_processing",
136
+ "incremental",
137
+ "loading",
138
+ "reporting",
139
+ "composition",
140
+ ]
141
+
142
+ [tool.taut.layers]
143
+ service = ["policy"]
144
+ contract = ["domain"]
145
+
146
+ [tool.taut.external]
147
+ logged_calls = ["httpx.AsyncClient", "httpx.Client"]
148
+ wrappers = ["taut.external_call"]
149
+
150
+ [tool.taut.enum]
151
+ shared_modules = [
152
+ "taut.configuration.catalog",
153
+ "taut.domain.diagnostics",
154
+ "taut.domain.evaluations",
155
+ "taut.domain.facts",
156
+ "taut.domain.findings",
157
+ "taut.domain.issues",
158
+ ]
159
+ uppercase_value_exceptions = ["taut.configuration.catalog.Effect"]
@@ -0,0 +1 @@
1
+ """Repository verification helpers."""