Coverage for src / graphable / parsers / utils.py: 87%
53 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-16 21:32 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-16 21:32 +0000
1from pathlib import Path
2from re import search
3from typing import Any
5from ..graph import Graph
6from ..graphable import Graphable
9def build_graph_from_data(
10 nodes_data: list[dict[str, Any]],
11 edges_data: list[dict[str, Any]],
12 reference_type: type = str,
13) -> Graph[Any]:
14 """
15 Standardized helper to construct a Graph from raw data.
17 Args:
18 nodes_data: List of node dictionaries (id, tags, duration, status).
19 edges_data: List of edge dictionaries (source, target, attributes).
20 reference_type: Type to cast the reference string to.
22 Returns:
23 Graph: The constructed Graph instance.
24 """
25 node_map: dict[str, Graphable[Any]] = {}
27 # 1. Create nodes
28 for node_entry in nodes_data:
29 node_id = str(node_entry["id"])
30 # Use provided reference if available, otherwise fallback to id
31 ref_val = node_entry.get("reference", node_id)
32 if reference_type is not str:
33 try:
34 ref_val = reference_type(ref_val)
35 except (ValueError, TypeError):
36 pass
38 node = Graphable(ref_val)
40 # Metadata
41 for tag in node_entry.get("tags", []):
42 node.add_tag(tag)
44 if "duration" in node_entry:
45 node.duration = float(node_entry["duration"])
46 if "status" in node_entry:
47 node.status = str(node_entry["status"])
49 node_map[node_id] = node
51 # 2. Link edges
52 g = Graph(set(node_map.values()))
53 for edge_entry in edges_data:
54 u_id = str(edge_entry["source"])
55 v_id = str(edge_entry["target"])
57 if u_id in node_map and v_id in node_map:
58 # All other keys are attributes
59 attrs = {
60 k: v for k, v in edge_entry.items() if k not in ("source", "target")
61 }
62 g.add_edge(node_map[u_id], node_map[v_id], **attrs)
64 return g
67def is_path(source: str | Path) -> bool:
68 """
69 Check if the given source is likely a path to a file.
71 Args:
72 source: The source to check (string or Path object).
74 Returns:
75 bool: True if it's a Path object or a string that points to an existing file.
76 """
77 if isinstance(source, Path):
78 return True
80 if isinstance(source, str) and "\n" not in source and len(source) < 1024:
81 try:
82 return Path(source).exists()
83 except OSError:
84 pass
86 return False
89def extract_checksum(source: str | Path) -> str | None:
90 """
91 Extract a blake2b checksum from the first few lines of a source.
92 Looks for the pattern 'blake2b: <hash>'.
94 Args:
95 source: The source to check (string or Path object).
97 Returns:
98 str | None: The hash if found, otherwise None.
99 """
100 content = ""
101 if is_path(source):
102 try:
103 # Read only the first 1KB to find comments
104 with open(source, "r") as f:
105 content = f.read(1024)
106 except Exception:
107 return None
108 else:
109 content = str(source)[:1024]
111 # Match blake2b: followed by hex chars (usually 128 for blake2b)
112 match = search(r"blake2b:\s*([a-fA-F0-9]+)", content)
113 if match:
114 return match.group(1)
116 return None