fix: silence "invalid file_type 'None'" warning on legacy graphs (#660)

Users running `graphify update .` against a graph that contains nodes
preserved from older graphify versions see the warning:

  [graphify] Extraction warning (31 issues): Node 1661 (id='telepathy_app')
  has invalid file_type 'None' - must be one of [...]

The cause: `_rebuild_code` in watch.py preserves nodes from the existing
graph.json that aren't in the new AST output. Some of those nodes
(stubs, semantic concepts created before file_type was always
populated) have file_type=null in the on-disk JSON, which becomes
Python None on load. The validator in validate.py:33 then flags every
such node.

Add a normalization step in `build_from_json` next to the existing
`source` -> `source_file` legacy canonicalization: if a node's
file_type is None or empty, default it to "concept" before validation.
This silences the false positive while keeping the validator strict for
genuinely invalid values like "weird_type".
This commit is contained in:
azizur100389
2026-05-03 00:46:46 +01:00
committed by azizur1992
parent 893acb19df
commit f87d0649c5
2 changed files with 62 additions and 1 deletions
+9 -1
View File
@@ -51,7 +51,9 @@ def build_from_json(extraction: dict, *, directed: bool = False) -> nx.Graph:
# Canonicalize legacy node/edge schema before validation.
for node in extraction.get("nodes", []):
if isinstance(node, dict) and "source" in node and "source_file" not in node:
if not isinstance(node, dict):
continue
if "source" in node and "source_file" not in node:
# Count edges that reference this node so the warning is actionable (#479)
node_id = node.get("id", "?")
affected_edges = sum(
@@ -65,6 +67,12 @@ def build_from_json(extraction: dict, *, directed: bool = False) -> nx.Graph:
file=sys.stderr,
)
node["source_file"] = node.pop("source")
# Default missing/None file_type to "concept" so legacy graph.json
# entries (and stub nodes preserved by `_rebuild_code` from older
# graphify versions that didn't always populate file_type) don't
# trigger spurious "invalid file_type 'None'" validator warnings (#660).
if node.get("file_type") in (None, ""):
node["file_type"] = "concept"
errors = validate_extraction(extraction)
# Dangling edges (stdlib/external imports) are expected - only warn about real schema errors.
+53
View File
@@ -60,3 +60,56 @@ def test_build_merges_multiple_extractions():
G = build([ext1, ext2])
assert G.number_of_nodes() == 2
assert G.number_of_edges() == 1
def test_none_file_type_defaults_to_concept(capsys):
"""Legacy nodes with file_type=None (e.g. preserved from older graph.json
by `_rebuild_code`) must not trigger 'invalid file_type None' warnings (#660)."""
ext = {
"nodes": [
{"id": "n1", "label": "Stub", "file_type": None, "source_file": "a.py"},
{"id": "n2", "label": "Real", "file_type": "code", "source_file": "b.py"},
],
"edges": [],
"input_tokens": 0,
"output_tokens": 0,
}
G = build_from_json(ext)
err = capsys.readouterr().err
assert "invalid file_type" not in err
# The legacy node still exists in the graph and has been canonicalized
assert G.nodes["n1"]["file_type"] == "concept"
assert G.nodes["n2"]["file_type"] == "code"
def test_missing_file_type_defaults_to_concept(capsys):
"""Nodes missing file_type entirely should also be canonicalized to 'concept'."""
ext = {
"nodes": [
{"id": "n1", "label": "Bare", "source_file": "a.py"},
],
"edges": [],
"input_tokens": 0,
"output_tokens": 0,
}
G = build_from_json(ext)
err = capsys.readouterr().err
assert "invalid file_type" not in err
assert "missing required field 'file_type'" not in err
assert G.nodes["n1"]["file_type"] == "concept"
def test_real_invalid_file_type_still_warns(capsys):
"""Truly invalid file_type values (not None, not empty) must still warn."""
ext = {
"nodes": [
{"id": "n1", "label": "Bad", "file_type": "weird_type", "source_file": "a.py"},
],
"edges": [],
"input_tokens": 0,
"output_tokens": 0,
}
build_from_json(ext)
err = capsys.readouterr().err
assert "invalid file_type" in err
assert "weird_type" in err