ckgraphify 0.2.2__tar.gz → 0.2.4__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.
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/PKG-INFO +1 -1
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/__main__.py +4 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/business_map.py +74 -1
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/graph_main_backend.py +117 -1
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/graph_main_frontend.py +803 -1
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/graph_main_frontend_sdk.py +15 -6
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/graph_main_merge.py +198 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/graph_main_trace.py +128 -44
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/pyproject.toml +1 -1
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/LICENSE +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/MANIFEST.in +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/README.md +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/ckgraphify.egg-info/SOURCES.txt +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/__init__.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/analyze.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/benchmark.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/bridge_mtop.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/build.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/cache.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/callflow_html.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/cluster.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/dedup.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/detect.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/export.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/extract.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/global_graph.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/google_workspace.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/graph_main_html.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/hooks.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/ingest.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/llm.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/manifest.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/repo_registry.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/report.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/security.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/serve.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/transcribe.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/tree_html.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/validate.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/watch.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/graphify/wiki.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/setup.cfg +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/skill/__init__.py +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/skill/skill-codex.md +0 -0
- {ckgraphify-0.2.2 → ckgraphify-0.2.4}/skill/skill.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ckgraphify
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: AI coding assistant skill for Claude Code and Codex - graph-main boundary graphs, multi-repo call chains, business-map concepts, and business search
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/safishamsi/graphify
|
|
@@ -1588,6 +1588,10 @@ def main() -> None:
|
|
|
1588
1588
|
print("Graph matches:")
|
|
1589
1589
|
for cand in result.get("graph_matches", [])[:10]:
|
|
1590
1590
|
print(f"- {cand.get('kind')} {cand.get('label')} repo={cand.get('repo') or '-'} score={cand.get('score')}")
|
|
1591
|
+
if result.get("repo_candidates"):
|
|
1592
|
+
print("Repository routing hints (not evidence):")
|
|
1593
|
+
for cand in result.get("repo_candidates", [])[:10]:
|
|
1594
|
+
print(f"- {cand.get('repo_id')} {cand.get('position') or '-'}: {cand.get('reason') or '-'}")
|
|
1591
1595
|
print(f"Next action: {result.get('next_action')}")
|
|
1592
1596
|
if result.get("trace"):
|
|
1593
1597
|
print("")
|
|
@@ -10,7 +10,7 @@ from pathlib import Path
|
|
|
10
10
|
from graphify.graph_main_trace import trace_graph_main
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
_STATUS_ORDER = {"verified": 0, "
|
|
13
|
+
_STATUS_ORDER = {"verified": 0, "boundary": 1, "candidate": 2, "partial": 3, "unexplored": 4, "stale": 5}
|
|
14
14
|
_KG_BANKS_DIR_NAME = "kg-banks"
|
|
15
15
|
_KG_BANKS_ROOT_MARKER = "__root__"
|
|
16
16
|
|
|
@@ -1096,6 +1096,19 @@ def lint_business_map(map_path: Path) -> BusinessLintResult:
|
|
|
1096
1096
|
if status == "verified" and require_scope_for_verified and not str(owner.get("verification_scope", "")).strip():
|
|
1097
1097
|
add("WARN", path, "verified flow should declare verification_scope")
|
|
1098
1098
|
if status == "verified":
|
|
1099
|
+
has_flow_evidence_navigation = any(
|
|
1100
|
+
isinstance(anchor, dict) and str(anchor.get("ref", "")).strip()
|
|
1101
|
+
for anchor in owner.get("anchors", [])
|
|
1102
|
+
) or any(
|
|
1103
|
+
isinstance(hint, dict) and str(hint.get("from", "")).strip()
|
|
1104
|
+
for hint in owner.get("trace_hints", [])
|
|
1105
|
+
)
|
|
1106
|
+
if require_scope_for_verified and not has_flow_evidence_navigation:
|
|
1107
|
+
add(
|
|
1108
|
+
"WARN",
|
|
1109
|
+
path,
|
|
1110
|
+
"verified flow has no flow-level anchors or trace_hints; add target-specific evidence navigation or narrow its status/scope",
|
|
1111
|
+
)
|
|
1099
1112
|
candidate_refs = [
|
|
1100
1113
|
str(anchor.get("ref", ""))
|
|
1101
1114
|
for anchor in owner.get("anchors", [])
|
|
@@ -1446,6 +1459,7 @@ def query_business(
|
|
|
1446
1459
|
) -> dict:
|
|
1447
1460
|
data = load_business_map(map_path)
|
|
1448
1461
|
match = match_business_query(data, query, top_k=top_k)
|
|
1462
|
+
repo_candidates = _repo_profile_candidates(map_path.parent / "repo-profiles.json", query, top_k=top_k)
|
|
1449
1463
|
result: dict = {
|
|
1450
1464
|
"query": query,
|
|
1451
1465
|
"map": str(map_path),
|
|
@@ -1454,6 +1468,8 @@ def query_business(
|
|
|
1454
1468
|
"business_context": _business_context(data, query),
|
|
1455
1469
|
"trace": None,
|
|
1456
1470
|
"graph_matches": [],
|
|
1471
|
+
"repo_candidates": repo_candidates,
|
|
1472
|
+
"repo_profile_notice": "Optional routing hints only; not factual evidence. Missing profiles are normal.",
|
|
1457
1473
|
"next_action": "",
|
|
1458
1474
|
}
|
|
1459
1475
|
best = match.get("best")
|
|
@@ -1479,3 +1495,60 @@ def query_business(
|
|
|
1479
1495
|
if graph_path is not None and match.get("status") == "none":
|
|
1480
1496
|
result["graph_matches"] = search_graph_main_matches(graph_path, query, top_k=10)
|
|
1481
1497
|
return result
|
|
1498
|
+
|
|
1499
|
+
|
|
1500
|
+
def _repo_profile_candidates(profile_path: Path, query: str, top_k: int = 5) -> list[dict]:
|
|
1501
|
+
"""Return deterministic search hints without treating profile text as business evidence."""
|
|
1502
|
+
try:
|
|
1503
|
+
catalog = json.loads(profile_path.read_text(encoding="utf-8"))
|
|
1504
|
+
except (OSError, ValueError, TypeError):
|
|
1505
|
+
return []
|
|
1506
|
+
profiles = catalog.get("profiles", []) if isinstance(catalog, dict) else []
|
|
1507
|
+
query_text = str(query or "").strip().lower()
|
|
1508
|
+
if not query_text:
|
|
1509
|
+
return []
|
|
1510
|
+
query_terms = set(_ascii_terms(query_text))
|
|
1511
|
+
candidates: list[tuple[int, str, dict]] = []
|
|
1512
|
+
for profile in profiles if isinstance(profiles, list) else []:
|
|
1513
|
+
if not isinstance(profile, dict):
|
|
1514
|
+
continue
|
|
1515
|
+
fields = {
|
|
1516
|
+
"repo_id": [profile.get("repo_id", "")],
|
|
1517
|
+
"position": [profile.get("position", "")],
|
|
1518
|
+
"aliases": profile.get("aliases", []),
|
|
1519
|
+
"tags": profile.get("tags", []),
|
|
1520
|
+
"capabilities": profile.get("capabilities", []),
|
|
1521
|
+
"summary": [profile.get("summary", "")],
|
|
1522
|
+
}
|
|
1523
|
+
weights = {"repo_id": 12, "position": 10, "aliases": 9, "tags": 7, "capabilities": 6, "summary": 3}
|
|
1524
|
+
score = 0
|
|
1525
|
+
reasons: list[str] = []
|
|
1526
|
+
for field, raw_values in fields.items():
|
|
1527
|
+
values = raw_values if isinstance(raw_values, list) else [raw_values]
|
|
1528
|
+
matched_values: list[str] = []
|
|
1529
|
+
for raw in values:
|
|
1530
|
+
value = str(raw or "").strip()
|
|
1531
|
+
lowered = value.lower()
|
|
1532
|
+
if not lowered:
|
|
1533
|
+
continue
|
|
1534
|
+
direct = lowered in query_text or query_text in lowered
|
|
1535
|
+
cjk_terms = [term for term in re.findall(r"[\u3400-\u9fff]{2,}", lowered) if term in query_text]
|
|
1536
|
+
term_hit = bool(query_terms.intersection(_ascii_terms(lowered))) or bool(cjk_terms)
|
|
1537
|
+
if direct or term_hit:
|
|
1538
|
+
score += weights[field] + (3 if direct else 0)
|
|
1539
|
+
matched_values.append(value)
|
|
1540
|
+
if matched_values:
|
|
1541
|
+
reasons.append(f"{field}: {', '.join(matched_values[:3])}")
|
|
1542
|
+
if score:
|
|
1543
|
+
repo_id = str(profile.get("repo_id", "")).strip()
|
|
1544
|
+
candidates.append((score, repo_id, {
|
|
1545
|
+
"repo_id": repo_id,
|
|
1546
|
+
"position": str(profile.get("position", "")).strip(),
|
|
1547
|
+
"score": score,
|
|
1548
|
+
"reason": "; ".join(reasons),
|
|
1549
|
+
"boundaries": [str(item) for item in profile.get("boundaries", []) if str(item).strip()],
|
|
1550
|
+
"routing_hint_only": True,
|
|
1551
|
+
"supports_factual_claims": False,
|
|
1552
|
+
}))
|
|
1553
|
+
candidates.sort(key=lambda item: (-item[0], item[1]))
|
|
1554
|
+
return [item[2] for item in candidates[:max(1, int(top_k or 5))]]
|
|
@@ -99,7 +99,7 @@ _METAQ_PRODUCER_RE = re.compile(r"(?:Metaq|MetaQ).{0,40}(?:send|publish|produce)
|
|
|
99
99
|
_TOPIC_RE = re.compile(r"""topic\s*=\s*["']([A-Za-z0-9_.-]+)["']""")
|
|
100
100
|
_STR_ASSIGN_RE = re.compile(r'(?m)\bString\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*"([A-Za-z0-9_.-]+)"\s*;')
|
|
101
101
|
_STATIC_STRING_CONST_RE = re.compile(
|
|
102
|
-
r'(?m)\b(?:public|protected|private)?\s*static\s+final\s+String\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*"([^"]+)"\s*;'
|
|
102
|
+
r'(?m)\b(?:public|protected|private)?\s*(?:static\s+final\s+)?String\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*"([^"]+)"\s*;'
|
|
103
103
|
)
|
|
104
104
|
_BINDING_HEADER_RE = re.compile(r"\bBinding\.header\s*\(([^)]*)\)")
|
|
105
105
|
_SET_LISTENER_RE = re.compile(r"\.setMessageListener\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)")
|
|
@@ -116,6 +116,10 @@ _TOPIC_TOKEN_RE = re.compile(r"\b([A-Za-z0-9_$.]*TOPIC[A-Za-z0-9_$.]*)\b")
|
|
|
116
116
|
_ANTX_CONSUMER_PROP_RE = re.compile(
|
|
117
117
|
r"(?m)^spring\.metaq\.consumers\[(\d+)\]\.([A-Za-z0-9_.-]+)\s*=\s*(.+?)\s*$"
|
|
118
118
|
)
|
|
119
|
+
_TAG_SPI_EXTENSION_RE = re.compile(r"@TagSpiExtension\s*\((?P<body>.*?)\)", re.S)
|
|
120
|
+
_COMPONENT_TAG_RE = re.compile(r"@ComponentTag\s*\((?P<body>.*?)\)", re.S)
|
|
121
|
+
_BLOCK_EXTENSION_RE = re.compile(r"@BlockExtension\s*\((?P<body>.*?)\)", re.S)
|
|
122
|
+
_ANNOTATION_ARG_RE_TEMPLATE = r"{name}\s*=\s*(?P<value>\{{.*?\}}|\"[^\"]+\"|'[^']+'|[A-Za-z0-9_$.]+)"
|
|
119
123
|
|
|
120
124
|
|
|
121
125
|
def _safe_id(s: str) -> str:
|
|
@@ -355,6 +359,33 @@ def _resolve_metaq_token(token: str, constants: dict[str, str]) -> str:
|
|
|
355
359
|
return constants.get(raw) or constants.get(raw.rsplit(".", 1)[-1]) or raw
|
|
356
360
|
|
|
357
361
|
|
|
362
|
+
def _resolve_java_string_token(token: str, constants: dict[str, str]) -> str:
|
|
363
|
+
raw = str(token or "").strip().strip()
|
|
364
|
+
if not raw:
|
|
365
|
+
return ""
|
|
366
|
+
if (raw.startswith('"') and raw.endswith('"')) or (raw.startswith("'") and raw.endswith("'")):
|
|
367
|
+
return raw[1:-1]
|
|
368
|
+
return constants.get(raw) or constants.get(raw.rsplit(".", 1)[-1]) or raw
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _annotation_arg_values(body: str, name: str, constants: dict[str, str]) -> list[str]:
|
|
372
|
+
pattern = re.compile(_ANNOTATION_ARG_RE_TEMPLATE.format(name=re.escape(name)), re.S)
|
|
373
|
+
m = pattern.search(body or "")
|
|
374
|
+
if not m:
|
|
375
|
+
return []
|
|
376
|
+
raw_value = m.group("value").strip()
|
|
377
|
+
if raw_value.startswith("{") and raw_value.endswith("}"):
|
|
378
|
+
raw_items = [part.strip() for part in raw_value[1:-1].split(",")]
|
|
379
|
+
else:
|
|
380
|
+
raw_items = [raw_value]
|
|
381
|
+
values: list[str] = []
|
|
382
|
+
for item in raw_items:
|
|
383
|
+
resolved = _resolve_java_string_token(item, constants)
|
|
384
|
+
if resolved:
|
|
385
|
+
values.append(resolved)
|
|
386
|
+
return list(dict.fromkeys(values))
|
|
387
|
+
|
|
388
|
+
|
|
358
389
|
def _extract_metaq_producer_topics(body_text: str, constants: dict[str, str] | None = None) -> list[tuple[str, str]]:
|
|
359
390
|
constants = constants or {}
|
|
360
391
|
topics: list[tuple[str, str]] = []
|
|
@@ -1048,6 +1079,7 @@ def build_graph_main_backend(*, repo_root: Path, out_path: Path) -> GraphMainBac
|
|
|
1048
1079
|
dep_method_by_key: dict[str, str] = {}
|
|
1049
1080
|
prod_by_key: dict[str, str] = {}
|
|
1050
1081
|
cons_by_key: dict[str, str] = {}
|
|
1082
|
+
t3_ext_by_key: dict[str, str] = {}
|
|
1051
1083
|
|
|
1052
1084
|
def ensure_api(interface_fqcn: str, source_file: str, line: int) -> str:
|
|
1053
1085
|
canonical = f"hsf_api:{interface_fqcn.lower()}"
|
|
@@ -1275,6 +1307,51 @@ def build_graph_main_backend(*, repo_root: Path, out_path: Path) -> GraphMainBac
|
|
|
1275
1307
|
cons_by_key[canonical] = nid
|
|
1276
1308
|
return nid
|
|
1277
1309
|
|
|
1310
|
+
def ensure_t3_extension(
|
|
1311
|
+
*,
|
|
1312
|
+
extension_type: str,
|
|
1313
|
+
class_fqcn: str,
|
|
1314
|
+
component_code: str,
|
|
1315
|
+
source_file: str,
|
|
1316
|
+
line: int,
|
|
1317
|
+
scene_code: str = "",
|
|
1318
|
+
) -> str:
|
|
1319
|
+
component_code = component_code.strip()
|
|
1320
|
+
scene_code = scene_code.strip()
|
|
1321
|
+
if not component_code:
|
|
1322
|
+
component_code = "*"
|
|
1323
|
+
key_scene = scene_code.lower() if scene_code else "*"
|
|
1324
|
+
canonical = f"t3_extension:{extension_type}:{key_scene}:{component_code.lower()}:{class_fqcn.lower()}"
|
|
1325
|
+
if canonical in t3_ext_by_key:
|
|
1326
|
+
return t3_ext_by_key[canonical]
|
|
1327
|
+
label_prefix = f"{scene_code}:{component_code}" if scene_code else component_code
|
|
1328
|
+
label = f"{label_prefix} -> {class_fqcn.split('.')[-1]}"
|
|
1329
|
+
nid = f"t3_extension__{_safe_id(extension_type + '_' + label_prefix + '_' + class_fqcn)}"
|
|
1330
|
+
match_key = (
|
|
1331
|
+
f"t3_component:{scene_code.lower()}:{component_code.lower()}"
|
|
1332
|
+
if scene_code and component_code != "*"
|
|
1333
|
+
else f"t3_component:*:{component_code.lower()}"
|
|
1334
|
+
)
|
|
1335
|
+
add_node(
|
|
1336
|
+
{
|
|
1337
|
+
"id": nid,
|
|
1338
|
+
"label": label,
|
|
1339
|
+
"canonical_key": canonical,
|
|
1340
|
+
"match_key": match_key,
|
|
1341
|
+
"file_type": "code",
|
|
1342
|
+
"source_file": source_file,
|
|
1343
|
+
"source_location": f"L{line}",
|
|
1344
|
+
"main_kind": "t3_extension",
|
|
1345
|
+
"extension_type": extension_type,
|
|
1346
|
+
"class_fqcn": class_fqcn,
|
|
1347
|
+
"scene_code": scene_code,
|
|
1348
|
+
"component_code": component_code,
|
|
1349
|
+
"module_code": component_code,
|
|
1350
|
+
}
|
|
1351
|
+
)
|
|
1352
|
+
t3_ext_by_key[canonical] = nid
|
|
1353
|
+
return nid
|
|
1354
|
+
|
|
1278
1355
|
map_pairs: list[tuple[int, str, str, str]] = []
|
|
1279
1356
|
mtop_by_method_key: dict[str, str] = {}
|
|
1280
1357
|
code_method_keys: set[str] = set()
|
|
@@ -1299,6 +1376,45 @@ def build_graph_main_backend(*, repo_root: Path, out_path: Path) -> GraphMainBac
|
|
|
1299
1376
|
map_method_candidates.add(f"hsf_method:{method_key}")
|
|
1300
1377
|
map_pairs.append((idx + 1, api_name, iface, method))
|
|
1301
1378
|
|
|
1379
|
+
for info in parsed:
|
|
1380
|
+
class_text = _read_text(repo_root / info.rel_path)
|
|
1381
|
+
class_m = _CLASS_RE.search(class_text)
|
|
1382
|
+
class_line = _line_of(class_text, class_m.start()) if class_m else 1
|
|
1383
|
+
for m in _TAG_SPI_EXTENSION_RE.finditer(info.class_ann):
|
|
1384
|
+
scenes = _annotation_arg_values(m.group("body"), "sceneCode", string_constants)
|
|
1385
|
+
modules = _annotation_arg_values(m.group("body"), "moduleCode", string_constants) or [""]
|
|
1386
|
+
for scene in scenes or [""]:
|
|
1387
|
+
for module in modules:
|
|
1388
|
+
ensure_t3_extension(
|
|
1389
|
+
extension_type="tag_spi",
|
|
1390
|
+
class_fqcn=info.class_fqcn,
|
|
1391
|
+
scene_code=scene,
|
|
1392
|
+
component_code=module,
|
|
1393
|
+
source_file=info.rel_path,
|
|
1394
|
+
line=class_line,
|
|
1395
|
+
)
|
|
1396
|
+
for m in _COMPONENT_TAG_RE.finditer(info.class_ann):
|
|
1397
|
+
sources = _annotation_arg_values(m.group("body"), "source", string_constants)
|
|
1398
|
+
if not sources:
|
|
1399
|
+
sources = _annotation_arg_values(m.group("body"), "value", string_constants)
|
|
1400
|
+
for source in sources:
|
|
1401
|
+
ensure_t3_extension(
|
|
1402
|
+
extension_type="component_tag",
|
|
1403
|
+
class_fqcn=info.class_fqcn,
|
|
1404
|
+
component_code=source,
|
|
1405
|
+
source_file=info.rel_path,
|
|
1406
|
+
line=class_line,
|
|
1407
|
+
)
|
|
1408
|
+
for m in _BLOCK_EXTENSION_RE.finditer(info.class_ann):
|
|
1409
|
+
for code in _annotation_arg_values(m.group("body"), "blockExtensionCode", string_constants):
|
|
1410
|
+
ensure_t3_extension(
|
|
1411
|
+
extension_type="block_extension",
|
|
1412
|
+
class_fqcn=info.class_fqcn,
|
|
1413
|
+
component_code=code,
|
|
1414
|
+
source_file=info.rel_path,
|
|
1415
|
+
line=class_line,
|
|
1416
|
+
)
|
|
1417
|
+
|
|
1302
1418
|
resolved_map_method_keys: set[str] = set()
|
|
1303
1419
|
|
|
1304
1420
|
for info in parsed:
|