Skip to content

Commit 6355f7b

Browse files
committed
Merge upstream/dev and resolve conflicts in concore.py
2 parents b3268f3 + 3e06d5b commit 6355f7b

24 files changed

Lines changed: 1005 additions & 197 deletions

concore.py

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
import time
22
import logging
33
import os
4+
import atexit
45
from ast import literal_eval
56
import sys
67
import re
78
import zmq
89
import numpy as np
9-
import atexit
1010
import signal
1111

1212
logging.basicConfig(
1313
level=logging.INFO,
14-
format='%(levelname)s - %(message)s'
15-
) # Added for ZeroMQ
14+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
15+
force=True
16+
)
17+
18+
#these lines mute the noisy library
19+
logging.getLogger('matplotlib').setLevel(logging.WARNING)
20+
logging.getLogger('PIL').setLevel(logging.WARNING)
21+
logging.getLogger('urllib3').setLevel(logging.WARNING)
22+
logging.getLogger('requests').setLevel(logging.WARNING)
23+
1624

1725
# if windows, create script to kill this process
1826
# because batch files don't provide easy way to know pid of last command
@@ -193,26 +201,50 @@ def safe_literal_eval(filename, defaultValue):
193201
# ===================================================================
194202
# Parameter Parsing
195203
# ===================================================================
204+
def parse_params(sparams: str) -> dict:
205+
params = {}
206+
if not sparams:
207+
return params
208+
209+
s = sparams.strip()
210+
211+
#full dict literal
212+
if s.startswith("{") and s.endswith("}"):
213+
try:
214+
val = literal_eval(s)
215+
if isinstance(val, dict):
216+
return val
217+
except (ValueError, SyntaxError):
218+
pass
219+
220+
for item in s.split(";"):
221+
if "=" in item:
222+
key, value = item.split("=", 1) # split only once
223+
key=key.strip()
224+
value=value.strip()
225+
#try to convert to python type (int, float, list, etc.)
226+
# Use literal_eval to preserve backward compatibility (integers/lists)
227+
# Fallback to string for unquoted values (paths, URLs)
228+
try:
229+
params[key] = literal_eval(value)
230+
except (ValueError, SyntaxError):
231+
params[key] = value
232+
return params
233+
196234
try:
197235
sparams_path = concore_params_file
198236
if os.path.exists(sparams_path):
199237
with open(sparams_path, "r") as f:
200-
sparams = f.read()
238+
sparams = f.read().strip()
201239
if sparams: # Ensure sparams is not empty
202240
# Windows sometimes keeps quotes
203241
if sparams[0] == '"' and sparams[-1] == '"': #windows keeps "" need to remove
204242
sparams = sparams[1:-1]
205243

206-
# Convert key=value;key2=value2 to Python dict format
207-
if sparams != '{' and not (sparams.startswith('{') and sparams.endswith('}')): # Check if it needs conversion
208-
logging.debug("converting sparams: "+sparams)
209-
sparams = "{'"+re.sub(';',",'",re.sub('=',"':",re.sub(' ','',sparams)))+"}"
210-
logging.debug("converted sparams: " + sparams)
211-
try:
212-
params = literal_eval(sparams)
213-
except Exception as e:
214-
logging.warning(f"bad params content: {sparams}, error: {e}")
215-
params = dict()
244+
# Parse params using clean function instead of regex
245+
logging.debug("parsing sparams: "+sparams)
246+
params = parse_params(sparams)
247+
logging.debug("parsed params: " + str(params))
216248
else:
217249
params = dict()
218250
else:

concore_cli/commands/run.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,17 @@
55
from rich.panel import Panel
66
from rich.progress import Progress, SpinnerColumn, TextColumn
77

8+
def _find_mkconcore_path():
9+
for parent in Path(__file__).resolve().parents:
10+
candidate = parent / "mkconcore.py"
11+
if candidate.exists():
12+
return candidate
13+
return None
14+
815
def run_workflow(workflow_file, source, output, exec_type, auto_build, console):
916
workflow_path = Path(workflow_file).resolve()
1017
source_path = Path(source).resolve()
11-
output_path = Path(output)
18+
output_path = Path(output).resolve()
1219

1320
if not source_path.exists():
1421
raise FileNotFoundError(f"Source directory '{source}' not found")
@@ -24,6 +31,10 @@ def run_workflow(workflow_file, source, output, exec_type, auto_build, console):
2431
console.print(f"[cyan]Type:[/cyan] {exec_type}")
2532
console.print()
2633

34+
mkconcore_path = _find_mkconcore_path()
35+
if mkconcore_path is None:
36+
raise FileNotFoundError("mkconcore.py not found. Please install concore from source.")
37+
2738
with Progress(
2839
SpinnerColumn(),
2940
TextColumn("[progress.description]{task.description}"),
@@ -33,7 +44,8 @@ def run_workflow(workflow_file, source, output, exec_type, auto_build, console):
3344

3445
try:
3546
result = subprocess.run(
36-
[sys.executable, 'mkconcore.py', str(workflow_path), str(source_path), str(output_path), exec_type],
47+
[sys.executable, str(mkconcore_path), str(workflow_path), str(source_path), str(output_path), exec_type],
48+
cwd=mkconcore_path.parent,
3749
capture_output=True,
3850
text=True,
3951
check=True

concore_cli/commands/validate.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from rich.panel import Panel
44
from rich.table import Table
55
import re
6+
import xml.etree.ElementTree as ET
67

78
def validate_workflow(workflow_file, console):
89
workflow_path = Path(workflow_file)
@@ -22,15 +23,34 @@ def validate_workflow(workflow_file, console):
2223
errors.append("File is empty")
2324
return show_results(console, errors, warnings, info)
2425

26+
# strict XML syntax check
27+
try:
28+
ET.fromstring(content)
29+
except ET.ParseError as e:
30+
errors.append(f"Invalid XML: {str(e)}")
31+
return show_results(console, errors, warnings, info)
32+
2533
try:
2634
soup = BeautifulSoup(content, 'xml')
2735
except Exception as e:
2836
errors.append(f"Invalid XML: {str(e)}")
2937
return show_results(console, errors, warnings, info)
3038

31-
if not soup.find('graphml'):
39+
root = soup.find('graphml')
40+
if not root:
3241
errors.append("Not a valid GraphML file - missing <graphml> root element")
3342
return show_results(console, errors, warnings, info)
43+
44+
# check the graph attributes
45+
graph = soup.find('graph')
46+
if not graph:
47+
errors.append("Missing <graph> element")
48+
else:
49+
edgedefault = graph.get('edgedefault')
50+
if not edgedefault:
51+
errors.append("Graph missing required 'edgedefault' attribute")
52+
elif edgedefault not in ['directed', 'undirected']:
53+
errors.append(f"Invalid edgedefault value '{edgedefault}' (must be 'directed' or 'undirected')")
3454

3555
nodes = soup.find_all('node')
3656
edges = soup.find_all('edge')
@@ -47,8 +67,19 @@ def validate_workflow(workflow_file, console):
4767

4868
node_labels = []
4969
for node in nodes:
70+
#check the node id
71+
node_id = node.get('id')
72+
if not node_id:
73+
errors.append("Node missing required 'id' attribute")
74+
#skip further checks for this node to avoid noise
75+
continue
76+
5077
try:
78+
#robust find: try with namespace prefix first, then without
5179
label_tag = node.find('y:NodeLabel')
80+
if not label_tag:
81+
label_tag = node.find('NodeLabel')
82+
5283
if label_tag and label_tag.text:
5384
label = label_tag.text.strip()
5485
node_labels.append(label)
@@ -60,13 +91,13 @@ def validate_workflow(workflow_file, console):
6091
if len(parts) != 2:
6192
warnings.append(f"Node '{label}' has invalid format")
6293
else:
63-
node_id, filename = parts
94+
nodeId_part, filename = parts
6495
if not filename:
6596
errors.append(f"Node '{label}' has no filename")
6697
elif not any(filename.endswith(ext) for ext in ['.py', '.cpp', '.m', '.v', '.java']):
6798
warnings.append(f"Node '{label}' has unusual file extension")
6899
else:
69-
warnings.append(f"Node {node.get('id', 'unknown')} has no label")
100+
warnings.append(f"Node {node_id} has no label")
70101
except Exception as e:
71102
warnings.append(f"Error parsing node: {str(e)}")
72103

@@ -91,6 +122,9 @@ def validate_workflow(workflow_file, console):
91122
for edge in edges:
92123
try:
93124
label_tag = edge.find('y:EdgeLabel')
125+
if not label_tag:
126+
label_tag = edge.find('EdgeLabel')
127+
94128
if label_tag and label_tag.text:
95129
if edge_label_regex.match(label_tag.text.strip()):
96130
zmq_edges += 1

0 commit comments

Comments
 (0)