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.
- code_atlas/__init__.py +6 -0
- code_atlas/__main__.py +6 -0
- code_atlas/api/__init__.py +5 -0
- code_atlas/api/handlers.py +1514 -0
- code_atlas/api/server.py +394 -0
- code_atlas/cli.py +2084 -0
- code_atlas/config.py +149 -0
- code_atlas/context/__init__.py +66 -0
- code_atlas/context/contracts.py +277 -0
- code_atlas/context/domain.py +760 -0
- code_atlas/context/engine.py +442 -0
- code_atlas/context/expand.py +133 -0
- code_atlas/context/renderers.py +71 -0
- code_atlas/context/tokenizer.py +37 -0
- code_atlas/contracts/__init__.py +38 -0
- code_atlas/contracts/enums.py +67 -0
- code_atlas/contracts/models.py +175 -0
- code_atlas/contracts/ports.py +70 -0
- code_atlas/discovery/__init__.py +25 -0
- code_atlas/discovery/gitignore.py +119 -0
- code_atlas/discovery/identity.py +52 -0
- code_atlas/discovery/models.py +204 -0
- code_atlas/discovery/scanner.py +495 -0
- code_atlas/domains/__init__.py +107 -0
- code_atlas/domains/api/__init__.py +28 -0
- code_atlas/domains/api/adapter.py +405 -0
- code_atlas/domains/api/common.py +308 -0
- code_atlas/domains/api/consumers.py +224 -0
- code_atlas/domains/api/emit.py +231 -0
- code_atlas/domains/api/openapi.py +398 -0
- code_atlas/domains/api/routes.py +582 -0
- code_atlas/domains/api/yaml_subset.py +683 -0
- code_atlas/domains/aws/__init__.py +28 -0
- code_atlas/domains/aws/adapter.py +382 -0
- code_atlas/domains/aws/common.py +261 -0
- code_atlas/domains/aws/configs.py +1164 -0
- code_atlas/domains/aws/emit.py +411 -0
- code_atlas/domains/aws/pyfiles.py +2220 -0
- code_atlas/domains/aws/sqlbridge.py +448 -0
- code_atlas/domains/cicd/__init__.py +50 -0
- code_atlas/domains/cicd/adapter.py +762 -0
- code_atlas/domains/cicd/classify.py +136 -0
- code_atlas/domains/cicd/workflow.py +504 -0
- code_atlas/domains/cicd/yamlmini.py +946 -0
- code_atlas/domains/cli_surface.py +739 -0
- code_atlas/domains/contracts.py +274 -0
- code_atlas/domains/crossresolve.py +1208 -0
- code_atlas/domains/evidence.py +57 -0
- code_atlas/domains/identity.py +149 -0
- code_atlas/domains/model.py +202 -0
- code_atlas/domains/registry.py +250 -0
- code_atlas/domains/resolution.py +65 -0
- code_atlas/domains/spark/__init__.py +31 -0
- code_atlas/domains/spark/adapter.py +146 -0
- code_atlas/domains/spark/emit.py +344 -0
- code_atlas/domains/spark/extract.py +108 -0
- code_atlas/domains/spark/sqllit.py +192 -0
- code_atlas/domains/spark/walk.py +1943 -0
- code_atlas/domains/sql/__init__.py +30 -0
- code_atlas/domains/sql/adapter.py +382 -0
- code_atlas/domains/sql/extract.py +1271 -0
- code_atlas/domains/sql/nodes.py +264 -0
- code_atlas/domains/sql/parser.py +1510 -0
- code_atlas/domains/sql/tokens.py +318 -0
- code_atlas/domains/summarize.py +126 -0
- code_atlas/domains/terraform/__init__.py +20 -0
- code_atlas/domains/terraform/adapter.py +127 -0
- code_atlas/domains/terraform/catalog.py +43 -0
- code_atlas/domains/terraform/extract.py +1277 -0
- code_atlas/domains/terraform/hcl.py +538 -0
- code_atlas/domains/terraform/refs.py +181 -0
- code_atlas/domains/terraform/tfjson.py +163 -0
- code_atlas/domains/terraform/tfvars.py +98 -0
- code_atlas/domains/validation.py +120 -0
- code_atlas/env_override.py +27 -0
- code_atlas/eval/__init__.py +26 -0
- code_atlas/eval/domain_eval.py +681 -0
- code_atlas/eval/ground_truth.py +688 -0
- code_atlas/eval/impact_truth.py +758 -0
- code_atlas/eval/metrics.py +178 -0
- code_atlas/eval/pack_value.py +610 -0
- code_atlas/federation/__init__.py +72 -0
- code_atlas/federation/federation.py +676 -0
- code_atlas/federation/model.py +355 -0
- code_atlas/federation/store.py +194 -0
- code_atlas/gitbin.py +48 -0
- code_atlas/graph/__init__.py +20 -0
- code_atlas/graph/builder.py +378 -0
- code_atlas/graph/identity.py +54 -0
- code_atlas/graph/queries.py +47 -0
- code_atlas/graph/validation.py +92 -0
- code_atlas/impact/__init__.py +37 -0
- code_atlas/impact/blast_radius.py +171 -0
- code_atlas/impact/contract_analyzer.py +104 -0
- code_atlas/impact/contracts.py +315 -0
- code_atlas/impact/diff_reader.py +142 -0
- code_atlas/impact/domain_traversal.py +768 -0
- code_atlas/impact/engine.py +392 -0
- code_atlas/impact/packs.py +128 -0
- code_atlas/impact/report.py +136 -0
- code_atlas/impact/revision_binding.py +220 -0
- code_atlas/impact/risk_classifier.py +83 -0
- code_atlas/impact/snapshot_resolver.py +136 -0
- code_atlas/impact/symbol_diff.py +210 -0
- code_atlas/impact/test_selector.py +166 -0
- code_atlas/indexing/__init__.py +5 -0
- code_atlas/indexing/cache.py +396 -0
- code_atlas/indexing/change_detector.py +278 -0
- code_atlas/indexing/coordinator.py +1032 -0
- code_atlas/indexing/dependency_planner.py +268 -0
- code_atlas/indexing/persist.py +436 -0
- code_atlas/indexing/reuse.py +85 -0
- code_atlas/install/__init__.py +7 -0
- code_atlas/install/cli.py +168 -0
- code_atlas/install/lifecycle.py +286 -0
- code_atlas/mcp.py +674 -0
- code_atlas/observability/__init__.py +19 -0
- code_atlas/observability/logging.py +199 -0
- code_atlas/parsers/__init__.py +43 -0
- code_atlas/parsers/contracts.py +209 -0
- code_atlas/parsers/engine.py +125 -0
- code_atlas/parsers/languages/__init__.py +1 -0
- code_atlas/parsers/languages/python.py +391 -0
- code_atlas/parsers/languages/tsx.py +28 -0
- code_atlas/parsers/languages/typescript.py +909 -0
- code_atlas/parsers/registry.py +51 -0
- code_atlas/parsers/tree_sitter_runtime.py +118 -0
- code_atlas/resolvers/__init__.py +33 -0
- code_atlas/resolvers/bootstrap.py +246 -0
- code_atlas/resolvers/capabilities.py +33 -0
- code_atlas/resolvers/contracts.py +133 -0
- code_atlas/resolvers/coordinator.py +96 -0
- code_atlas/resolvers/enrichment.py +318 -0
- code_atlas/resolvers/lsp/__init__.py +26 -0
- code_atlas/resolvers/lsp/adapter.py +256 -0
- code_atlas/resolvers/lsp/lifecycle.py +251 -0
- code_atlas/resolvers/lsp/spawn.py +92 -0
- code_atlas/resolvers/lsp/transport.py +240 -0
- code_atlas/resolvers/registry.py +33 -0
- code_atlas/resolvers/scip/__init__.py +49 -0
- code_atlas/resolvers/scip/decoder.py +216 -0
- code_atlas/resolvers/scip/importer.py +248 -0
- code_atlas/resolvers/scip/loader.py +90 -0
- code_atlas/resolvers/scip/mapper.py +271 -0
- code_atlas/resolvers/scip/provenance.py +211 -0
- code_atlas/resolvers/scip/resolver.py +289 -0
- code_atlas/resolvers/scip/validation.py +101 -0
- code_atlas/resolvers/syntactic/__init__.py +8 -0
- code_atlas/resolvers/syntactic/resolver.py +254 -0
- code_atlas/search/__init__.py +32 -0
- code_atlas/search/contracts.py +176 -0
- code_atlas/search/domain.py +561 -0
- code_atlas/search/engine.py +530 -0
- code_atlas/search/federated.py +276 -0
- code_atlas/search/graph.py +282 -0
- code_atlas/search/hybrid.py +151 -0
- code_atlas/search/intent.py +48 -0
- code_atlas/search/lexical.py +176 -0
- code_atlas/search/refs.py +359 -0
- code_atlas/search/structural.py +505 -0
- code_atlas/search/tokenizer.py +43 -0
- code_atlas/search/vector/__init__.py +35 -0
- code_atlas/search/vector/contracts.py +76 -0
- code_atlas/search/vector/embeddings.py +96 -0
- code_atlas/search/vector/index.py +169 -0
- code_atlas/search/vector/provider.py +50 -0
- code_atlas/storage/__init__.py +14 -0
- code_atlas/storage/db.py +47 -0
- code_atlas/storage/migrations.py +552 -0
- code_atlas/storage/store.py +1045 -0
- code_atlas/ui/__init__.py +29 -0
- code_atlas/ui/i18n.py +143 -0
- code_atlas/ui/kit.py +689 -0
- code_atlas/ui/wizard.py +317 -0
- code_atlas_forge-1.1.0.dist-info/METADATA +246 -0
- code_atlas_forge-1.1.0.dist-info/RECORD +181 -0
- code_atlas_forge-1.1.0.dist-info/WHEEL +5 -0
- code_atlas_forge-1.1.0.dist-info/entry_points.txt +2 -0
- code_atlas_forge-1.1.0.dist-info/licenses/LICENSE +202 -0
- code_atlas_forge-1.1.0.dist-info/licenses/NOTICE +17 -0
- 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
|
+
)
|