truequery 0.1.0__py3-none-any.whl

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 (254) hide show
  1. truequery/__init__.py +3 -0
  2. truequery/analyzers/__init__.py +0 -0
  3. truequery/analyzers/project_checks.py +220 -0
  4. truequery/analyzers/sql.py +529 -0
  5. truequery/checks/__init__.py +0 -0
  6. truequery/checks/sql.py +1278 -0
  7. truequery/cli.py +710 -0
  8. truequery/engine.py +346 -0
  9. truequery/hook.py +860 -0
  10. truequery/keylookup.py +135 -0
  11. truequery/models.py +32 -0
  12. truequery/project.py +561 -0
  13. truequery/registry_impl.json +127 -0
  14. truequery/report.py +343 -0
  15. truequery/schema.py +206 -0
  16. truequery/settings.py +239 -0
  17. truequery/standard/consume/README.md +111 -0
  18. truequery/standard/consume/adhoc-limit-required.yaml +50 -0
  19. truequery/standard/consume/aggregate-before-export.yaml +39 -0
  20. truequery/standard/consume/consumer-cost-guard.yaml +44 -0
  21. truequery/standard/consume/consumer-reads-marts.yaml +38 -0
  22. truequery/standard/consume/dashboard-query-budget.yaml +40 -0
  23. truequery/standard/consume/dashboard-refresh-schedule.yaml +47 -0
  24. truequery/standard/consume/deterministic-export-order.yaml +44 -0
  25. truequery/standard/consume/explicit-timezone-consumers.yaml +44 -0
  26. truequery/standard/consume/explore-answer-carries-reproducible-query.yaml +56 -0
  27. truequery/standard/consume/explore-caveats-and-unchecked-stated.yaml +52 -0
  28. truequery/standard/consume/explore-day-boundaries-and-partial-periods.yaml +51 -0
  29. truequery/standard/consume/explore-experiment-readout-follows-plan.yaml +63 -0
  30. truequery/standard/consume/explore-freshness-and-coverage-checked.yaml +54 -0
  31. truequery/standard/consume/explore-inspect-distributions-and-examples.yaml +47 -0
  32. truequery/standard/consume/explore-join-row-count-checks.yaml +53 -0
  33. truequery/standard/consume/explore-null-aware-negative-filters.yaml +50 -0
  34. truequery/standard/consume/explore-question-metric-grain-stated.yaml +55 -0
  35. truequery/standard/consume/explore-rates-not-counts-across-segments.yaml +51 -0
  36. truequery/standard/consume/explore-reconcile-to-known-total.yaml +51 -0
  37. truequery/standard/consume/explore-reuse-governed-metric.yaml +52 -0
  38. truequery/standard/consume/export-row-cap.yaml +45 -0
  39. truequery/standard/consume/freshness-displayed.yaml +44 -0
  40. truequery/standard/consume/no-deep-offset.yaml +38 -0
  41. truequery/standard/consume/parameterized-dashboard-filters.yaml +38 -0
  42. truequery/standard/consume/reverse-etl-idempotent-keys.yaml +45 -0
  43. truequery/standard/consume/reverse-etl-pii-consent.yaml +50 -0
  44. truequery/standard/consume/reverse-etl-schema-contract.yaml +47 -0
  45. truequery/standard/dq/README.md +117 -0
  46. truequery/standard/dq/accuracy-proxy-or-unmeasurable.yaml +45 -0
  47. truequery/standard/dq/audits-blocking-every-run.yaml +47 -0
  48. truequery/standard/dq/checks-versioned-with-code.yaml +52 -0
  49. truequery/standard/dq/completeness-declare-missing-sentinels.yaml +44 -0
  50. truequery/standard/dq/completeness-fill-rate-thresholds.yaml +45 -0
  51. truequery/standard/dq/completeness-not-null-required-columns.yaml +60 -0
  52. truequery/standard/dq/completeness-table-not-empty.yaml +48 -0
  53. truequery/standard/dq/consistency-cross-column-row-rules.yaml +49 -0
  54. truequery/standard/dq/consistency-no-orphan-keys.yaml +49 -0
  55. truequery/standard/dq/consistency-paired-aggregate-reconciliation.yaml +48 -0
  56. truequery/standard/dq/schema-conformance-checked-on-read.yaml +48 -0
  57. truequery/standard/dq/unenforced-constraints-are-tested.yaml +51 -0
  58. truequery/standard/dq/uniqueness-composite-grain.yaml +52 -0
  59. truequery/standard/dq/uniqueness-key-no-duplicates.yaml +53 -0
  60. truequery/standard/dq/unit-tests-with-fixtures.yaml +52 -0
  61. truequery/standard/dq/validity-allowed-value-sets.yaml +46 -0
  62. truequery/standard/dq/validity-enforce-model-contracts.yaml +51 -0
  63. truequery/standard/dq/validity-range-format-type-rules.yaml +50 -0
  64. truequery/standard/gov/README.md +94 -0
  65. truequery/standard/gov/audit-columns-or-snapshots.yaml +45 -0
  66. truequery/standard/gov/column-grants-for-pii.yaml +41 -0
  67. truequery/standard/gov/declared-model-owner.yaml +43 -0
  68. truequery/standard/gov/description-coverage-for-shared-models.yaml +40 -0
  69. truequery/standard/gov/erasure-path-documented.yaml +43 -0
  70. truequery/standard/gov/exposures-registered.yaml +73 -0
  71. truequery/standard/gov/group-private-access.yaml +41 -0
  72. truequery/standard/gov/least-privilege-grants.yaml +80 -0
  73. truequery/standard/gov/lineage-from-declared-sources.yaml +41 -0
  74. truequery/standard/gov/mesh-public-contracts-only.yaml +41 -0
  75. truequery/standard/gov/no-bypassrls-roles.yaml +39 -0
  76. truequery/standard/gov/no-public-privileges.yaml +39 -0
  77. truequery/standard/gov/no-raw-pii-in-seeds.yaml +41 -0
  78. truequery/standard/gov/pii-classification-declared.yaml +42 -0
  79. truequery/standard/gov/production-pii-not-in-dev.yaml +43 -0
  80. truequery/standard/gov/redacting-views-for-pii.yaml +41 -0
  81. truequery/standard/gov/retention-declared.yaml +42 -0
  82. truequery/standard/gov/row-policy-for-sensitive-tables.yaml +38 -0
  83. truequery/standard/ingest/README.md +100 -0
  84. truequery/standard/ingest/cap-intake-cost-and-volume.yaml +46 -0
  85. truequery/standard/ingest/cdc-replica-identity-required.yaml +47 -0
  86. truequery/standard/ingest/checkpoint-streaming-intake.yaml +42 -0
  87. truequery/standard/ingest/choose-batch-vs-streaming-deliberately.yaml +45 -0
  88. truequery/standard/ingest/declare-source-freshness.yaml +80 -0
  89. truequery/standard/ingest/dedupe-on-natural-key-before-append.yaml +45 -0
  90. truequery/standard/ingest/handle-schema-drift-explicitly.yaml +45 -0
  91. truequery/standard/ingest/idempotent-batch-loads.yaml +49 -0
  92. truequery/standard/ingest/isolate-source-failures.yaml +50 -0
  93. truequery/standard/ingest/landing-rebuildable-from-source.yaml +43 -0
  94. truequery/standard/ingest/monitor-cdc-lag-and-slots.yaml +47 -0
  95. truequery/standard/ingest/pin-connector-and-schema-versions.yaml +42 -0
  96. truequery/standard/ingest/pin-file-format-options.yaml +42 -0
  97. truequery/standard/ingest/propagate-deletes-explicitly.yaml +48 -0
  98. truequery/standard/ingest/quarantine-invalid-rows-at-intake.yaml +56 -0
  99. truequery/standard/ingest/raw-landing-append-only.yaml +48 -0
  100. truequery/standard/ingest/reconcile-row-counts-per-load.yaml +43 -0
  101. truequery/standard/ingest/record-file-lineage-at-intake.yaml +48 -0
  102. truequery/standard/ingest/reference-sources-not-raw-tables.yaml +42 -0
  103. truequery/standard/ingest/require-completeness-marker.yaml +50 -0
  104. truequery/standard/ingest/scan-pii-at-landing.yaml +45 -0
  105. truequery/standard/ingest/seeds-small-static-only.yaml +43 -0
  106. truequery/standard/ingest/test-intake-against-fixtures.yaml +46 -0
  107. truequery/standard/ingest/watermark-and-late-data-policy.yaml +49 -0
  108. truequery/standard/model/README.md +91 -0
  109. truequery/standard/model/dbt-contract-enforced.yaml +50 -0
  110. truequery/standard/model/delete-handling-incremental.yaml +51 -0
  111. truequery/standard/model/explicit-column-lists.yaml +83 -0
  112. truequery/standard/model/full-refresh-parity.yaml +49 -0
  113. truequery/standard/model/grain-preserved-joins.yaml +52 -0
  114. truequery/standard/model/idempotent-reexec.yaml +48 -0
  115. truequery/standard/model/incremental-lookback-window.yaml +53 -0
  116. truequery/standard/model/incremental-watermark-filter.yaml +48 -0
  117. truequery/standard/model/jinja-macro-determinism.yaml +46 -0
  118. truequery/standard/model/jinja-no-nested-curlies.yaml +43 -0
  119. truequery/standard/model/materialization-choice.yaml +51 -0
  120. truequery/standard/model/merge-source-deduped.yaml +57 -0
  121. truequery/standard/model/microbatch-event-time.yaml +39 -0
  122. truequery/standard/model/null-join-keys-excluded.yaml +45 -0
  123. truequery/standard/model/snapshot-scd2-validity.yaml +45 -0
  124. truequery/standard/model/snapshot-strategy-choice.yaml +41 -0
  125. truequery/standard/model/staging-no-business-logic.yaml +44 -0
  126. truequery/standard/model/surrogate-keys-stable.yaml +37 -0
  127. truequery/standard/model/unique-key-merge-key.yaml +55 -0
  128. truequery/standard/ops/README.md +98 -0
  129. truequery/standard/ops/breaking-change-categorization.yaml +35 -0
  130. truequery/standard/ops/ci-pr-checks-required.yaml +44 -0
  131. truequery/standard/ops/ci-slim-builds-only-changed.yaml +42 -0
  132. truequery/standard/ops/cost-estimate-before-run.yaml +41 -0
  133. truequery/standard/ops/cost-guardrail-confidence-levels.yaml +39 -0
  134. truequery/standard/ops/dev-environment-expiry.yaml +42 -0
  135. truequery/standard/ops/disposable-dev-environments.yaml +42 -0
  136. truequery/standard/ops/drift-detection-history-first.yaml +42 -0
  137. truequery/standard/ops/full-refresh-approval.yaml +76 -0
  138. truequery/standard/ops/model-versioning-breaking-changes.yaml +83 -0
  139. truequery/standard/ops/plan-preview-before-apply.yaml +39 -0
  140. truequery/standard/ops/post-deploy-data-validation.yaml +42 -0
  141. truequery/standard/ops/pre-destructive-snapshot.yaml +35 -0
  142. truequery/standard/ops/prod-credentials-isolation.yaml +96 -0
  143. truequery/standard/ops/run-artifact-retention.yaml +41 -0
  144. truequery/standard/ops/staged-upgrades-with-backup.yaml +39 -0
  145. truequery/standard/orch/README.md +74 -0
  146. truequery/standard/orch/concurrency-pools-and-limits.yaml +63 -0
  147. truequery/standard/orch/dag-ci-parse-and-unit-tests.yaml +70 -0
  148. truequery/standard/orch/data-aware-scheduling-over-polling.yaml +62 -0
  149. truequery/standard/orch/declared-asset-lineage.yaml +66 -0
  150. truequery/standard/orch/deferrable-sensors-with-timeout.yaml +75 -0
  151. truequery/standard/orch/explicit-catchup-policy.yaml +53 -0
  152. truequery/standard/orch/explicit-schedule-timezone.yaml +68 -0
  153. truequery/standard/orch/failure-alert-routing.yaml +100 -0
  154. truequery/standard/orch/idempotent-backfill-windowing.yaml +68 -0
  155. truequery/standard/orch/idempotent-retries-with-limits.yaml +79 -0
  156. truequery/standard/orch/light-dag-parse-no-heavy-top-level.yaml +75 -0
  157. truequery/standard/orch/no-circular-dependencies.yaml +67 -0
  158. truequery/standard/orch/partitioned-assets-windowing.yaml +66 -0
  159. truequery/standard/orch/sla-deadline-alerts.yaml +76 -0
  160. truequery/standard/orch/task-atomicity-no-partial-writes.yaml +62 -0
  161. truequery/standard/orch/task-timeout-execution-limit.yaml +70 -0
  162. truequery/standard/py/README.md +63 -0
  163. truequery/standard/py/airflow-isolate-task-dependencies.yaml +40 -0
  164. truequery/standard/py/dagster-asset-declared-dependencies.yaml +47 -0
  165. truequery/standard/py/dagster-io-manager-not-manual-io.yaml +47 -0
  166. truequery/standard/py/dagster-resources-for-external-clients.yaml +53 -0
  167. truequery/standard/py/dbt-python-model-contract.yaml +48 -0
  168. truequery/standard/py/deterministic-model-outputs.yaml +83 -0
  169. truequery/standard/py/hook-adapter-connection-lifecycle.yaml +59 -0
  170. truequery/standard/py/idempotent-task-logic.yaml +55 -0
  171. truequery/standard/py/no-n-plus-one-queries-in-loops.yaml +46 -0
  172. truequery/standard/py/pandas-filter-before-collect.yaml +44 -0
  173. truequery/standard/py/pandas-no-chained-assignment.yaml +41 -0
  174. truequery/standard/py/pandas-no-row-loop-mutations.yaml +44 -0
  175. truequery/standard/py/pin-pipeline-dependencies.yaml +82 -0
  176. truequery/standard/py/sqlmesh-audit-python-outputs.yaml +49 -0
  177. truequery/standard/py/sqlmesh-python-model-execution-context.yaml +52 -0
  178. truequery/standard/sem/README.md +97 -0
  179. truequery/standard/sem/aggregation-type-matches-additivity.yaml +61 -0
  180. truequery/standard/sem/conversion-metrics-window-bounds.yaml +54 -0
  181. truequery/standard/sem/count-distinct-uses-entity-key.yaml +52 -0
  182. truequery/standard/sem/cumulative-metrics-time-spine.yaml +53 -0
  183. truequery/standard/sem/derived-metrics-compose-from-governed.yaml +52 -0
  184. truequery/standard/sem/description-and-ownership-per-metric.yaml +50 -0
  185. truequery/standard/sem/dimension-naming-conventions.yaml +52 -0
  186. truequery/standard/sem/entity-primary-key-declared.yaml +54 -0
  187. truequery/standard/sem/join-paths-declared-via-entities.yaml +47 -0
  188. truequery/standard/sem/metric-filters-declared-not-hidden.yaml +57 -0
  189. truequery/standard/sem/metric-grain-declaration.yaml +52 -0
  190. truequery/standard/sem/metric-smoke-test-on-change.yaml +43 -0
  191. truequery/standard/sem/no-fanout-double-counting.yaml +52 -0
  192. truequery/standard/sem/no-metric-defined-off-staging.yaml +47 -0
  193. truequery/standard/sem/null-handling-in-aggregations.yaml +57 -0
  194. truequery/standard/sem/percentile-metrics-use-approximate.yaml +51 -0
  195. truequery/standard/sem/ratio-metrics-divide-after-aggregation.yaml +49 -0
  196. truequery/standard/sem/saved-query-canonical-slices.yaml +56 -0
  197. truequery/standard/sem/semantic-model-validates.yaml +50 -0
  198. truequery/standard/sem/single-governed-metric-definition.yaml +68 -0
  199. truequery/standard/sem/time-dimension-standardized.yaml +51 -0
  200. truequery/standard/sem/unit-consistency.yaml +54 -0
  201. truequery/standard/sql/README.md +126 -0
  202. truequery/standard/sql/accidental-cross-join.yaml +58 -0
  203. truequery/standard/sql/average-of-ratios.yaml +63 -0
  204. truequery/standard/sql/between-date-on-timestamp.yaml +74 -0
  205. truequery/standard/sql/case-without-else-in-aggregate.yaml +67 -0
  206. truequery/standard/sql/count-star-after-left-join.yaml +67 -0
  207. truequery/standard/sql/distinct-dedup-bandaid.yaml +59 -0
  208. truequery/standard/sql/explore-limit-is-not-a-sample.yaml +56 -0
  209. truequery/standard/sql/explore-profile-filter-columns.yaml +48 -0
  210. truequery/standard/sql/fanout-join-pre-aggregation.yaml +64 -0
  211. truequery/standard/sql/group-by-bare-column.yaml +75 -0
  212. truequery/standard/sql/insert-explicit-column-list.yaml +60 -0
  213. truequery/standard/sql/integer-division-truncation.yaml +94 -0
  214. truequery/standard/sql/keyset-pagination.yaml +70 -0
  215. truequery/standard/sql/left-join-null-rejecting-where.yaml +58 -0
  216. truequery/standard/sql/limit-requires-order-by.yaml +67 -0
  217. truequery/standard/sql/natural-join-explicit-keys.yaml +56 -0
  218. truequery/standard/sql/negative-filter-drops-nulls.yaml +70 -0
  219. truequery/standard/sql/nondeterministic-merge-keys.yaml +69 -0
  220. truequery/standard/sql/not-in-nullable-subquery.yaml +57 -0
  221. truequery/standard/sql/null-equality-comparison.yaml +65 -0
  222. truequery/standard/sql/random-sampling-no-sort.yaml +48 -0
  223. truequery/standard/sql/row-number-dedupe-tiebreaker.yaml +67 -0
  224. truequery/standard/sql/sargable-filter-bare-column.yaml +53 -0
  225. truequery/standard/sql/set-operation-column-position.yaml +59 -0
  226. truequery/standard/sql/unguarded-update-delete.yaml +63 -0
  227. truequery/standard/sql/union-all-default.yaml +70 -0
  228. truequery/standard/store/README.md +88 -0
  229. truequery/standard/store/cluster-on-frequent-filters-joins.yaml +44 -0
  230. truequery/standard/store/compact-small-files-optimize.yaml +44 -0
  231. truequery/standard/store/declare-not-null-check-constraints.yaml +43 -0
  232. truequery/standard/store/declare-sort-order-for-range-queries.yaml +44 -0
  233. truequery/standard/store/enable-change-feed-for-incremental-consumers.yaml +44 -0
  234. truequery/standard/store/evolve-partitions-by-metadata-not-rewrite.yaml +45 -0
  235. truequery/standard/store/explicit-physical-types-no-implicit-casts.yaml +43 -0
  236. truequery/standard/store/full-refresh-by-replace-not-delete-insert.yaml +44 -0
  237. truequery/standard/store/incremental-watermark-partition-alignment.yaml +40 -0
  238. truequery/standard/store/no-high-cardinality-partition-column.yaml +47 -0
  239. truequery/standard/store/partition-expiration-retention-policy.yaml +43 -0
  240. truequery/standard/store/partition-on-filter-column.yaml +43 -0
  241. truequery/standard/store/partition-predicate-required.yaml +41 -0
  242. truequery/standard/store/prefer-auto-liquid-clustering-over-manual-tune.yaml +37 -0
  243. truequery/standard/store/rewrite-manifests-and-stats-periodically.yaml +37 -0
  244. truequery/standard/store/single-open-format-per-medallion-layer.yaml +37 -0
  245. truequery/standard/store/time-travel-retention-explicit.yaml +46 -0
  246. truequery/standard/store/use-transient-temporary-for-ephemeral.yaml +35 -0
  247. truequery/standard/store/vacuum-expire-snapshots-with-retention-floor.yaml +42 -0
  248. truequery/std_registry.py +3310 -0
  249. truequery-0.1.0.dist-info/METADATA +52 -0
  250. truequery-0.1.0.dist-info/RECORD +254 -0
  251. truequery-0.1.0.dist-info/WHEEL +4 -0
  252. truequery-0.1.0.dist-info/entry_points.txt +3 -0
  253. truequery-0.1.0.dist-info/licenses/LICENSE +201 -0
  254. truequery-0.1.0.dist-info/licenses/NOTICE +4 -0
truequery/keylookup.py ADDED
@@ -0,0 +1,135 @@
1
+ """Declared-key index: the first consumer of contracts/.
2
+
3
+ Maps a relation name (lowercased) to its candidate keys. Each candidate
4
+ is a frozenset of columns that is unique on its own; a relation may
5
+ have several (a surrogate id and a natural key). Sources:
6
+
7
+ - ``contracts/<table>.yaml``: every ``keys:`` entry is one candidate
8
+ (a nested list such as ``[currency, rate_date]`` is one composite
9
+ candidate), and
10
+ - dbt schema YAML under a ``models`` tree: each column with a
11
+ ``unique`` test, and each model-level
12
+ ``unique_combination_of_columns`` test.
13
+
14
+ Deterministic, cached per project. Offline, file-level evidence only.
15
+ Unreadable YAML contributes nothing (the receipt reports invalid
16
+ contracts; this index never breaks a check).
17
+ """
18
+ import os
19
+
20
+ import yaml
21
+
22
+ from truequery.project import SKIP_DIRS
23
+
24
+ _CACHE: dict[tuple, dict[str, list[frozenset]]] = {}
25
+
26
+
27
+ def _read_yaml(path):
28
+ try:
29
+ with open(path, encoding="utf-8") as handle:
30
+ return yaml.safe_load(handle)
31
+ except (OSError, ValueError, yaml.YAMLError):
32
+ return None
33
+
34
+
35
+ def _add(index, table, columns):
36
+ key = frozenset(str(c).lower() for c in columns if isinstance(c, str))
37
+ if key and key not in index.setdefault(table, []):
38
+ index[table].append(key)
39
+
40
+
41
+ def _test_entries(spec):
42
+ """(name, args) for each test on a model or column (dbt >= 1.8
43
+ spells `tests:` as `data_tests:`)."""
44
+ for test in (spec.get("tests") or []) + (spec.get("data_tests") or []):
45
+ if isinstance(test, str):
46
+ yield test, {}
47
+ elif isinstance(test, dict):
48
+ for name, args in test.items():
49
+ yield str(name), args if isinstance(args, dict) else {}
50
+
51
+
52
+ def _from_contracts(cdir, index):
53
+ for name in sorted(os.listdir(cdir)):
54
+ if not name.endswith((".yaml", ".yml")):
55
+ continue
56
+ doc = _read_yaml(os.path.join(cdir, name))
57
+ if not isinstance(doc, dict):
58
+ continue
59
+ table = str(doc.get("table") or os.path.splitext(name)[0]).lower()
60
+ for key in doc.get("keys") or []:
61
+ _add(index, table, key if isinstance(key, list) else [key])
62
+
63
+
64
+ def _from_dbt_yaml(path, index):
65
+ doc = _read_yaml(path)
66
+ if not isinstance(doc, dict):
67
+ return
68
+ for model in doc.get("models") or []:
69
+ if not isinstance(model, dict) or not model.get("name"):
70
+ continue
71
+ table = str(model["name"]).lower()
72
+ for name, args in _test_entries(model):
73
+ # A model-level unique test without a column list pins nothing.
74
+ if name.split(".")[-1] == "unique_combination_of_columns":
75
+ _add(index, table, args.get("combination_of_columns") or [])
76
+ for col in model.get("columns") or []:
77
+ if not isinstance(col, dict) or not col.get("name"):
78
+ continue
79
+ if any(name.split(".")[-1] == "unique"
80
+ for name, _ in _test_entries(col)):
81
+ _add(index, table, [col["name"]])
82
+
83
+
84
+ def _nearest(path, test):
85
+ """Nearest ancestor directory of path for which test(dir) holds."""
86
+ directory = os.path.dirname(os.path.abspath(path))
87
+ while True:
88
+ if test(directory):
89
+ return directory
90
+ parent = os.path.dirname(directory)
91
+ if parent == directory:
92
+ return None
93
+ directory = parent
94
+
95
+
96
+ def _project_root(path):
97
+ """Nearest ancestor with a project marker, or None: without one we
98
+ don't walk the disk looking for keys (they'd belong to someone else)."""
99
+ markers = ("contracts", "dbt_project.yml", "DATA.md")
100
+ return _nearest(path, lambda d: any(
101
+ os.path.exists(os.path.join(d, m)) for m in markers))
102
+
103
+
104
+ def _models_ymls(root):
105
+ found = []
106
+ for dirpath, dirnames, filenames in os.walk(root):
107
+ # A nested dbt project is a different project: its keys aren't ours.
108
+ dirnames[:] = sorted(
109
+ d for d in dirnames
110
+ if d not in SKIP_DIRS and not d.startswith(".")
111
+ and not os.path.isfile(os.path.join(dirpath, d, "dbt_project.yml")))
112
+ parts = os.path.relpath(dirpath, root).split(os.sep)
113
+ if dirpath != root and "models" not in parts:
114
+ continue
115
+ found += [os.path.join(dirpath, name) for name in sorted(filenames)
116
+ if name.endswith((".yml", ".yaml"))]
117
+ return found
118
+
119
+
120
+ def declared_keys_for(path):
121
+ """{relation name -> [candidate key, ...]} for the project of path.
122
+
123
+ contracts/ may sit above the dbt project (examples/shop/contracts
124
+ next to dbt_repo/), so it is looked up on its own."""
125
+ root = _project_root(path)
126
+ cdir = _nearest(path, lambda d: os.path.isdir(os.path.join(d, "contracts")))
127
+ cache_key = (root, cdir)
128
+ if cache_key not in _CACHE:
129
+ index: dict[str, list[frozenset]] = {}
130
+ if cdir is not None:
131
+ _from_contracts(os.path.join(cdir, "contracts"), index)
132
+ for yml in _models_ymls(root) if root is not None else []:
133
+ _from_dbt_yaml(yml, index)
134
+ _CACHE[cache_key] = index
135
+ return _CACHE[cache_key]
truequery/models.py ADDED
@@ -0,0 +1,32 @@
1
+ """Finding and diagnostic shapes. One shape for the CLI, the hook, and tests."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+
8
+ @dataclass
9
+ class Finding:
10
+ rule: str
11
+ severity: str
12
+ mode: str
13
+ evidence: str
14
+ file: str
15
+ line: int
16
+ col: int
17
+ snippet: str
18
+ why: str
19
+ fix: str
20
+
21
+ def sort_key(self):
22
+ return (self.file, self.line, self.col, self.rule)
23
+
24
+
25
+ @dataclass
26
+ class Diagnostic:
27
+ file: str
28
+ message: str
29
+ detail: str = ""
30
+
31
+ def sort_key(self):
32
+ return (self.file, self.message)