code-atlas-forge 1.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 (181) hide show
  1. code_atlas/__init__.py +6 -0
  2. code_atlas/__main__.py +6 -0
  3. code_atlas/api/__init__.py +5 -0
  4. code_atlas/api/handlers.py +1514 -0
  5. code_atlas/api/server.py +394 -0
  6. code_atlas/cli.py +2084 -0
  7. code_atlas/config.py +149 -0
  8. code_atlas/context/__init__.py +66 -0
  9. code_atlas/context/contracts.py +277 -0
  10. code_atlas/context/domain.py +760 -0
  11. code_atlas/context/engine.py +442 -0
  12. code_atlas/context/expand.py +133 -0
  13. code_atlas/context/renderers.py +71 -0
  14. code_atlas/context/tokenizer.py +37 -0
  15. code_atlas/contracts/__init__.py +38 -0
  16. code_atlas/contracts/enums.py +67 -0
  17. code_atlas/contracts/models.py +175 -0
  18. code_atlas/contracts/ports.py +70 -0
  19. code_atlas/discovery/__init__.py +25 -0
  20. code_atlas/discovery/gitignore.py +119 -0
  21. code_atlas/discovery/identity.py +52 -0
  22. code_atlas/discovery/models.py +204 -0
  23. code_atlas/discovery/scanner.py +495 -0
  24. code_atlas/domains/__init__.py +107 -0
  25. code_atlas/domains/api/__init__.py +28 -0
  26. code_atlas/domains/api/adapter.py +405 -0
  27. code_atlas/domains/api/common.py +308 -0
  28. code_atlas/domains/api/consumers.py +224 -0
  29. code_atlas/domains/api/emit.py +231 -0
  30. code_atlas/domains/api/openapi.py +398 -0
  31. code_atlas/domains/api/routes.py +582 -0
  32. code_atlas/domains/api/yaml_subset.py +683 -0
  33. code_atlas/domains/aws/__init__.py +28 -0
  34. code_atlas/domains/aws/adapter.py +382 -0
  35. code_atlas/domains/aws/common.py +261 -0
  36. code_atlas/domains/aws/configs.py +1164 -0
  37. code_atlas/domains/aws/emit.py +411 -0
  38. code_atlas/domains/aws/pyfiles.py +2220 -0
  39. code_atlas/domains/aws/sqlbridge.py +448 -0
  40. code_atlas/domains/cicd/__init__.py +50 -0
  41. code_atlas/domains/cicd/adapter.py +762 -0
  42. code_atlas/domains/cicd/classify.py +136 -0
  43. code_atlas/domains/cicd/workflow.py +504 -0
  44. code_atlas/domains/cicd/yamlmini.py +946 -0
  45. code_atlas/domains/cli_surface.py +739 -0
  46. code_atlas/domains/contracts.py +274 -0
  47. code_atlas/domains/crossresolve.py +1208 -0
  48. code_atlas/domains/evidence.py +57 -0
  49. code_atlas/domains/identity.py +149 -0
  50. code_atlas/domains/model.py +202 -0
  51. code_atlas/domains/registry.py +250 -0
  52. code_atlas/domains/resolution.py +65 -0
  53. code_atlas/domains/spark/__init__.py +31 -0
  54. code_atlas/domains/spark/adapter.py +146 -0
  55. code_atlas/domains/spark/emit.py +344 -0
  56. code_atlas/domains/spark/extract.py +108 -0
  57. code_atlas/domains/spark/sqllit.py +192 -0
  58. code_atlas/domains/spark/walk.py +1943 -0
  59. code_atlas/domains/sql/__init__.py +30 -0
  60. code_atlas/domains/sql/adapter.py +382 -0
  61. code_atlas/domains/sql/extract.py +1271 -0
  62. code_atlas/domains/sql/nodes.py +264 -0
  63. code_atlas/domains/sql/parser.py +1510 -0
  64. code_atlas/domains/sql/tokens.py +318 -0
  65. code_atlas/domains/summarize.py +126 -0
  66. code_atlas/domains/terraform/__init__.py +20 -0
  67. code_atlas/domains/terraform/adapter.py +127 -0
  68. code_atlas/domains/terraform/catalog.py +43 -0
  69. code_atlas/domains/terraform/extract.py +1277 -0
  70. code_atlas/domains/terraform/hcl.py +538 -0
  71. code_atlas/domains/terraform/refs.py +181 -0
  72. code_atlas/domains/terraform/tfjson.py +163 -0
  73. code_atlas/domains/terraform/tfvars.py +98 -0
  74. code_atlas/domains/validation.py +120 -0
  75. code_atlas/env_override.py +27 -0
  76. code_atlas/eval/__init__.py +26 -0
  77. code_atlas/eval/domain_eval.py +681 -0
  78. code_atlas/eval/ground_truth.py +688 -0
  79. code_atlas/eval/impact_truth.py +758 -0
  80. code_atlas/eval/metrics.py +178 -0
  81. code_atlas/eval/pack_value.py +610 -0
  82. code_atlas/federation/__init__.py +72 -0
  83. code_atlas/federation/federation.py +676 -0
  84. code_atlas/federation/model.py +355 -0
  85. code_atlas/federation/store.py +194 -0
  86. code_atlas/gitbin.py +48 -0
  87. code_atlas/graph/__init__.py +20 -0
  88. code_atlas/graph/builder.py +378 -0
  89. code_atlas/graph/identity.py +54 -0
  90. code_atlas/graph/queries.py +47 -0
  91. code_atlas/graph/validation.py +92 -0
  92. code_atlas/impact/__init__.py +37 -0
  93. code_atlas/impact/blast_radius.py +171 -0
  94. code_atlas/impact/contract_analyzer.py +104 -0
  95. code_atlas/impact/contracts.py +315 -0
  96. code_atlas/impact/diff_reader.py +142 -0
  97. code_atlas/impact/domain_traversal.py +768 -0
  98. code_atlas/impact/engine.py +392 -0
  99. code_atlas/impact/packs.py +128 -0
  100. code_atlas/impact/report.py +136 -0
  101. code_atlas/impact/revision_binding.py +220 -0
  102. code_atlas/impact/risk_classifier.py +83 -0
  103. code_atlas/impact/snapshot_resolver.py +136 -0
  104. code_atlas/impact/symbol_diff.py +210 -0
  105. code_atlas/impact/test_selector.py +166 -0
  106. code_atlas/indexing/__init__.py +5 -0
  107. code_atlas/indexing/cache.py +396 -0
  108. code_atlas/indexing/change_detector.py +278 -0
  109. code_atlas/indexing/coordinator.py +1032 -0
  110. code_atlas/indexing/dependency_planner.py +268 -0
  111. code_atlas/indexing/persist.py +436 -0
  112. code_atlas/indexing/reuse.py +85 -0
  113. code_atlas/install/__init__.py +7 -0
  114. code_atlas/install/cli.py +168 -0
  115. code_atlas/install/lifecycle.py +286 -0
  116. code_atlas/mcp.py +674 -0
  117. code_atlas/observability/__init__.py +19 -0
  118. code_atlas/observability/logging.py +199 -0
  119. code_atlas/parsers/__init__.py +43 -0
  120. code_atlas/parsers/contracts.py +209 -0
  121. code_atlas/parsers/engine.py +125 -0
  122. code_atlas/parsers/languages/__init__.py +1 -0
  123. code_atlas/parsers/languages/python.py +391 -0
  124. code_atlas/parsers/languages/tsx.py +28 -0
  125. code_atlas/parsers/languages/typescript.py +909 -0
  126. code_atlas/parsers/registry.py +51 -0
  127. code_atlas/parsers/tree_sitter_runtime.py +118 -0
  128. code_atlas/resolvers/__init__.py +33 -0
  129. code_atlas/resolvers/bootstrap.py +246 -0
  130. code_atlas/resolvers/capabilities.py +33 -0
  131. code_atlas/resolvers/contracts.py +133 -0
  132. code_atlas/resolvers/coordinator.py +96 -0
  133. code_atlas/resolvers/enrichment.py +318 -0
  134. code_atlas/resolvers/lsp/__init__.py +26 -0
  135. code_atlas/resolvers/lsp/adapter.py +256 -0
  136. code_atlas/resolvers/lsp/lifecycle.py +251 -0
  137. code_atlas/resolvers/lsp/spawn.py +92 -0
  138. code_atlas/resolvers/lsp/transport.py +240 -0
  139. code_atlas/resolvers/registry.py +33 -0
  140. code_atlas/resolvers/scip/__init__.py +49 -0
  141. code_atlas/resolvers/scip/decoder.py +216 -0
  142. code_atlas/resolvers/scip/importer.py +248 -0
  143. code_atlas/resolvers/scip/loader.py +90 -0
  144. code_atlas/resolvers/scip/mapper.py +271 -0
  145. code_atlas/resolvers/scip/provenance.py +211 -0
  146. code_atlas/resolvers/scip/resolver.py +289 -0
  147. code_atlas/resolvers/scip/validation.py +101 -0
  148. code_atlas/resolvers/syntactic/__init__.py +8 -0
  149. code_atlas/resolvers/syntactic/resolver.py +254 -0
  150. code_atlas/search/__init__.py +32 -0
  151. code_atlas/search/contracts.py +176 -0
  152. code_atlas/search/domain.py +561 -0
  153. code_atlas/search/engine.py +530 -0
  154. code_atlas/search/federated.py +276 -0
  155. code_atlas/search/graph.py +282 -0
  156. code_atlas/search/hybrid.py +151 -0
  157. code_atlas/search/intent.py +48 -0
  158. code_atlas/search/lexical.py +176 -0
  159. code_atlas/search/refs.py +359 -0
  160. code_atlas/search/structural.py +505 -0
  161. code_atlas/search/tokenizer.py +43 -0
  162. code_atlas/search/vector/__init__.py +35 -0
  163. code_atlas/search/vector/contracts.py +76 -0
  164. code_atlas/search/vector/embeddings.py +96 -0
  165. code_atlas/search/vector/index.py +169 -0
  166. code_atlas/search/vector/provider.py +50 -0
  167. code_atlas/storage/__init__.py +14 -0
  168. code_atlas/storage/db.py +47 -0
  169. code_atlas/storage/migrations.py +552 -0
  170. code_atlas/storage/store.py +1045 -0
  171. code_atlas/ui/__init__.py +29 -0
  172. code_atlas/ui/i18n.py +143 -0
  173. code_atlas/ui/kit.py +689 -0
  174. code_atlas/ui/wizard.py +317 -0
  175. code_atlas_forge-1.1.0.dist-info/METADATA +246 -0
  176. code_atlas_forge-1.1.0.dist-info/RECORD +181 -0
  177. code_atlas_forge-1.1.0.dist-info/WHEEL +5 -0
  178. code_atlas_forge-1.1.0.dist-info/entry_points.txt +2 -0
  179. code_atlas_forge-1.1.0.dist-info/licenses/LICENSE +202 -0
  180. code_atlas_forge-1.1.0.dist-info/licenses/NOTICE +17 -0
  181. code_atlas_forge-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,192 @@
1
+ """Delegação ``spark.sql(literal)`` → parser do adapter ``sql`` (lf-0112).
2
+
3
+ Costura pública do adapter SQL (lf-0111): ``parse_file`` (stdlib puro)
4
+ produz o ``ParsedFile``; ``extract_parsed`` produz o ``FileExtraction``
5
+ com entidades/relações/diagnósticos de linhagem ANSI/Spark já
6
+ computados — CTEs colapsados, ``SELECT *`` declarado, dinâmico
7
+ diagnosticado.
8
+
9
+ O remap para o domínio ``pyspark`` é mecânico e honesto:
10
+
11
+ - entidades de tabela/view/coluna são re-emitidas com
12
+ ``domain="pyspark"`` (o contrato exige ``identity.domain`` ==
13
+ adapter) — mesma posição catalog/namespace/name;
14
+ - as transformações internas do SQL (``<path>#<i>``) **colapsam** na
15
+ transformação do call-site — ``T_call`` herdada do walker;
16
+ - relações ``READS_FROM``/``WRITES_TO``/``TRANSFORMS``/
17
+ ``DERIVES_FROM`` tabela→tabela e coluna→coluna são re-ancoradas;
18
+ - evidência é **sempre** o span do ``spark.sql(…)`` no ``.py`` — spans
19
+ relativos ao literal descartados (não são endereçáveis no arquivo);
20
+ - diagnósticos do parser/extrator fluem prefixados
21
+ ``spark.sql@L<linha>:`` — ``dynamic-sql``, ``select-star``,
22
+ ``parse-error`` e demais chegam honestos ao envelope.
23
+
24
+ Retorna ``(sources, unresolved)``: chaves lógicas das fontes lidas
25
+ (entram no colapso ``sink DERIVES_FROM fonte`` dos writes) e flag de
26
+ linhagem parcial do literal.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import hashlib
32
+
33
+ # Tipo do literal observado pelo walker — importado apenas para tipagem
34
+ # (walk → sqllit → walk seria circular em runtime; TYPE_CHECKING cobre
35
+ # os dois lados).
36
+ from typing import TYPE_CHECKING
37
+
38
+ from code_atlas.domains.identity import DomainIdentity
39
+ from code_atlas.domains.model import DomainEntityKind, DomainRelationKind
40
+ from code_atlas.domains.spark.emit import Sink
41
+ from code_atlas.domains.sql import parse_file
42
+ from code_atlas.domains.sql.adapter import ADAPTER_VERSION as _SQL_VERSION
43
+ from code_atlas.domains.sql.extract import FileExtraction, extract_parsed
44
+ from code_atlas.parsers.contracts import SourceSpan
45
+
46
+ if TYPE_CHECKING:
47
+ from code_atlas.domains.spark.walk import _SqlLit
48
+
49
+ _WRITE_REL = frozenset(
50
+ {
51
+ DomainRelationKind.WRITES_TO.value,
52
+ DomainRelationKind.TRANSFORMS.value,
53
+ }
54
+ )
55
+
56
+
57
+ def delegate_sql_literal(
58
+ sink: Sink,
59
+ lit: _SqlLit,
60
+ path: str,
61
+ sha: str,
62
+ *,
63
+ call_evi: int,
64
+ call_span: SourceSpan | None,
65
+ t_ident: DomainIdentity,
66
+ ) -> tuple[set[str], bool]:
67
+ """Extrai deps de tabela de ``spark.sql(literal)`` re-ancoradas em ``t_ident``.
68
+
69
+ Devolve ``(logical_keys das fontes, houve_alvo_unresolved)``.
70
+ """
71
+ req = sink.request
72
+ sql_sha = hashlib.sha256(lit.literal.encode("utf-8")).hexdigest()
73
+ pseudo_path = f"{path}#sql@{lit.line}"
74
+
75
+ parsed = parse_file(lit.literal)
76
+ fx = extract_parsed(
77
+ parsed,
78
+ repository=req.repository,
79
+ root=req.root,
80
+ environment=req.environment,
81
+ path=pseudo_path,
82
+ sha=sql_sha,
83
+ adapter_version=_SQL_VERSION,
84
+ )
85
+
86
+ trans_keys = {
87
+ e.identity.logical_key()
88
+ for e in fx.entities
89
+ if e.kind is DomainEntityKind.DATA_TRANSFORMATION
90
+ }
91
+ _remap_entities(sink, fx, trans_keys, path, sha, call_evi, call_span)
92
+ sources, unresolved = _remap_relations(
93
+ sink, fx, trans_keys, path, call_evi, call_span, t_ident
94
+ )
95
+ _flow_diagnostics(sink, fx, path, lit.line, parsed.statements)
96
+ return sources, unresolved
97
+
98
+
99
+ def _role_map(
100
+ sink: Sink, fx: FileExtraction
101
+ ) -> dict[str, set[str]]:
102
+ """Role do alvo por relação — chaveado por logical_key retaggada."""
103
+ role_of: dict[str, set[str]] = {}
104
+ for r in fx.relations:
105
+ if r.to_identity is None:
106
+ continue
107
+ tk = sink.retag(r.to_identity).logical_key()
108
+ bucket = role_of.setdefault(tk, set())
109
+ if r.relation_type in _WRITE_REL:
110
+ bucket.add("write")
111
+ elif r.relation_type == DomainRelationKind.READS_FROM.value:
112
+ bucket.add("read")
113
+ return role_of
114
+
115
+
116
+ def _remap_entities(
117
+ sink: Sink,
118
+ fx: FileExtraction,
119
+ trans_keys: set[str],
120
+ path: str,
121
+ sha: str,
122
+ call_evi: int,
123
+ call_span: SourceSpan | None,
124
+ ) -> None:
125
+ role_of = _role_map(sink, fx)
126
+ for e in fx.entities:
127
+ if e.identity.logical_key() in trans_keys:
128
+ continue # âncora interna do SQL — colapsa em t_ident
129
+ ident = sink.retag(e.identity)
130
+ attrs = dict(e.attributes)
131
+ attrs["via"] = "spark.sql"
132
+ sink.add_entity(
133
+ ident, e.kind, e.name,
134
+ path=path, span=call_span, content_hash=sha,
135
+ attributes=attrs,
136
+ roles=tuple(sorted(role_of.get(ident.logical_key(), ()))),
137
+ evidence=(call_evi,),
138
+ )
139
+
140
+
141
+ def _remap_relations(
142
+ sink: Sink,
143
+ fx: FileExtraction,
144
+ trans_keys: set[str],
145
+ path: str,
146
+ call_evi: int,
147
+ call_span: SourceSpan | None,
148
+ t_ident: DomainIdentity,
149
+ ) -> tuple[set[str], bool]:
150
+ sources: set[str] = set()
151
+ unresolved = False
152
+ for r in fx.relations:
153
+ if r.from_identity is None:
154
+ # relação por símbolo — fora do modelo de linhagem daqui;
155
+ # ancora honestamente no call-site
156
+ from_i = t_ident
157
+ elif r.from_identity.logical_key() in trans_keys:
158
+ from_i = t_ident
159
+ else:
160
+ from_i = sink.retag(r.from_identity)
161
+ to_i = sink.retag(r.to_identity) if r.to_identity else None
162
+ if to_i is None:
163
+ unresolved = True
164
+ sink.add_rel(
165
+ r.relation_type, from_i,
166
+ path=path, to_i=to_i,
167
+ to_text=r.to_unresolved if to_i is None else "",
168
+ span=call_span, evidence=(call_evi,),
169
+ resolution=r.resolution,
170
+ )
171
+ if (
172
+ r.relation_type == DomainRelationKind.READS_FROM.value
173
+ and to_i is not None
174
+ ):
175
+ sources.add(to_i.logical_key())
176
+ return sources, unresolved
177
+
178
+
179
+ def _flow_diagnostics(
180
+ sink: Sink, fx: FileExtraction, path: str, line: int,
181
+ statements: object,
182
+ ) -> None:
183
+ for d in fx.diagnostics:
184
+ sink.diag(
185
+ d.code, d.severity,
186
+ f"spark.sql@L{line}: {d.message}", path,
187
+ )
188
+ if not statements:
189
+ sink.diag(
190
+ "empty-sql", "info",
191
+ f"spark.sql@L{line}: literal has no statements", path,
192
+ )