33import os
44import subprocess
55import logging
6+ import json
67
78logging .basicConfig (
89 level = logging .INFO ,
910 format = '%(message)s' ,
1011 datefmt = '%Y-%m-%d %H:%M:%S'
1112)
1213
13- def run_copy_script (template_script_path_arg , new_port_name_arg , new_zmq_port_arg , output_directory_arg , python_exe ):
14- base_template_name = os .path .basename (template_script_path_arg )
14+ def run_specialization_script (template_script_path , output_dir , edge_params_list , python_exe , copy_script_path ):
15+ """
16+ Calls the copy script to generate a specialized version of a node's script.
17+ Returns the basename of the generated script on success, None on failure.
18+ """
19+ # The new copy script generates a standardized filename, e.g., "original.py"
20+ base_template_name = os .path .basename (template_script_path )
1521 template_root , template_ext = os .path .splitext (base_template_name )
1622 output_filename = f"{ template_root } { template_ext } "
17- expected_output_path = os .path .join (output_directory_arg , output_filename )
23+ expected_output_path = os .path .join (output_dir , output_filename )
1824
25+ # If the specialized file already exists, we don't need to regenerate it.
1926 if os .path .exists (expected_output_path ):
20- logging .info (f"Specialized script '{ expected_output_path } ' already exists. Skipping generation ." )
27+ logging .info (f"Specialized script '{ expected_output_path } ' already exists. Using existing ." )
2128 return output_filename
22-
23- copy_script_path = os .path .join ("." ,"copy_with_port_portname.py" )
29+
30+ # Convert the list of parameters to a JSON string for command line argument
31+ edge_params_json_str = json .dumps (edge_params_list )
2432
2533 cmd = [
2634 python_exe ,
2735 copy_script_path ,
28- new_port_name_arg ,
29- new_zmq_port_arg ,
30- template_script_path_arg ,
31- output_directory_arg
36+ template_script_path ,
37+ output_dir ,
38+ edge_params_json_str # Pass the JSON string as the last argument
3239 ]
33- logging .info (f"Running: { ' ' .join (cmd )} " )
40+ logging .info (f"Running specialization for ' { base_template_name } ' : { ' ' .join (cmd )} " )
3441 try :
3542 result = subprocess .run (cmd , capture_output = True , text = True , check = True , encoding = 'utf-8' )
36- logging .info (f"Successfully generated '{ output_filename } ' using copy_with_port_portname.py ." )
37- if result .stdout : logging .debug (f"copy_with_port_portname.py stdout:\n { result .stdout } " )
38- if result .stderr : logging .warning (f"copy_with_port_portname.py stderr:\n { result .stderr } " )
43+ logging .info (f"Successfully generated specialized script '{ output_filename } '." )
44+ if result .stdout : logging .debug (f"copy_with_port_portname.py stdout:\n { result .stdout . strip () } " )
45+ if result .stderr : logging .warning (f"copy_with_port_portname.py stderr:\n { result .stderr . strip () } " )
3946 return output_filename
4047 except subprocess .CalledProcessError as e :
41- logging .error (f"Error calling copy_with_port_portname.py for '{ template_script_path_arg } ' with port_name ' { new_port_name_arg } ':" )
48+ logging .error (f"Error calling specialization script for '{ template_script_path } ':" )
4249 logging .error (f"Command: { ' ' .join (e .cmd )} " )
4350 logging .error (f"Return code: { e .returncode } " )
44- logging .error (f"Stdout: { e .stdout } " )
45- logging .error (f"Stderr: { e .stderr } " )
46- return None
47- except FileNotFoundError :
48- logging .error (f"Error: Python executable or copy_with_port_portname.py script not found." )
49- logging .error (f"Attempted command: { ' ' .join (cmd )} " )
51+ logging .error (f"Stdout: { e .stdout .strip ()} " )
52+ logging .error (f"Stderr: { e .stderr .strip ()} " )
5053 return None
5154 except Exception as e :
52- logging .error (f"An unexpected error occurred while trying to run copy_script : { e } " )
55+ logging .error (f"An unexpected error occurred while trying to run specialization script : { e } " )
5356 return None
5457
5558
56- def create_modified_script (template_script_path , zmq_port_name_val , zmq_port_val , output_dir ):
59+ def create_modified_script (template_script_path , output_dir , edge_params_json_str ):
60+ """
61+ Creates a modified Python script by injecting ZMQ port and port name
62+ definitions from a JSON object.
63+
64+ Args:
65+ template_script_path (str): The path to the source template script.
66+ output_dir (str): The directory to save the new script in.
67+ edge_params_json_str (str): A JSON string representing a list of
68+ edge parameter dictionaries.
69+ """
5770 try :
5871 with open (template_script_path , 'r' ) as f :
5972 lines = f .readlines ()
@@ -64,28 +77,60 @@ def create_modified_script(template_script_path, zmq_port_name_val, zmq_port_val
6477 print (f"Error reading template script '{ template_script_path } ': { e } " )
6578 sys .exit (1 )
6679
67- definitions = [
68- ' \n ' ,
69- f'ZMQ_PORT_NAME = " { zmq_port_name_val } " \n ' ,
70- f'ZMQ_PORT = " { zmq_port_val } " \n ' ,
71- ' \n '
72- ]
80+ try :
81+ edge_params_list = json . loads ( edge_params_json_str )
82+ except json . JSONDecodeError as e :
83+ print ( f"Error: Invalid JSON string provided for edge parameters: { e } " )
84+ print ( f"Received: { edge_params_json_str } " )
85+ sys . exit ( 1 )
7386
87+ # --- Generate definitions from the list of edge parameters ---
88+ definitions = ['\n # --- ZMQ Port and Name Definitions (Auto-generated by mkconcore.py) ---\n ' ]
89+ print (f"Generating definitions for { len (edge_params_list )} edge(s):" )
90+
91+ for params in edge_params_list :
92+ port = params .get ("port" )
93+ port_name = params .get ("port_name" )
94+ source_label = params .get ("source_node_label" , "UNKNOWN_SOURCE" )
95+ target_label = params .get ("target_node_label" , "UNKNOWN_TARGET" )
96+
97+ # Sanitize labels to be valid Python variable parts
98+ safe_source = "" .join (c if c .isalnum () else '_' for c in source_label )
99+ safe_target = "" .join (c if c .isalnum () else '_' for c in target_label )
100+
101+ # Create unique variable names
102+ port_var_name = f"PORT_{ safe_source } _{ safe_target } "
103+ port_name_var_name = f"PORT_NAME_{ safe_source } _{ safe_target } "
104+
105+ definitions .append (f'{ port_name_var_name } = "{ port_name } "\n ' )
106+ definitions .append (f'{ port_var_name } = "{ port } "\n ' )
107+
108+ print (f" - { port_name_var_name } = \" { port_name } \" " )
109+ print (f" - { port_var_name } = \" { port } \" " )
110+
111+ definitions .append ('# --- End of Auto-generated Definitions ---\n \n ' )
112+
113+ # --- Insert definitions into the script ---
74114 insert_index = 0
75115 for i , line in enumerate (lines ):
76116 stripped_line = line .strip ()
117+ # Find the last import statement to insert after
77118 if stripped_line .startswith ('import ' ) or stripped_line .startswith ('from ' ):
78119 insert_index = i + 1
120+ # Stop searching after the first non-import, non-comment line after imports are found
79121 elif insert_index > 0 and stripped_line and not stripped_line .startswith ('#' ):
80122 break
123+ # Handle case where script starts with shebang
81124 if insert_index == 0 and lines and lines [0 ].startswith ('#!' ):
82125 insert_index = 1
83126
84127 modified_lines = lines [:insert_index ] + definitions + lines [insert_index :]
85128
86- # Determine output filename
129+ # --- Determine and create output file ---
87130 base_template_name = os .path .basename (template_script_path )
88131 template_root , template_ext = os .path .splitext (base_template_name )
132+
133+ # Standardized output filename for a node with one or more specializations
89134 output_filename = f"{ template_root } { template_ext } "
90135 output_script_path = os .path .join (output_dir , output_filename )
91136
@@ -97,23 +142,21 @@ def create_modified_script(template_script_path, zmq_port_name_val, zmq_port_val
97142 with open (output_script_path , 'w' ) as f :
98143 f .writelines (modified_lines )
99144
100- print (f"Successfully created '{ output_script_path } ' with:" )
101- print (f" ZMQ_PORT_NAME = \" { zmq_port_name_val } \" " )
102- print (f" ZMQ_PORT = \" { zmq_port_val } \" " )
145+ print (f"Successfully created specialized script: '{ output_script_path } '" )
103146
104147 except Exception as e :
105148 print (f"Error writing output script '{ output_script_path } ': { e } " )
106149 sys .exit (1 )
107150
108151if __name__ == "__main__" :
109- if len (sys .argv ) != 5 :
110- print ("Usage: python3 copy_with_port_portname.py <New_ZMQ_PORT_NAME> <New_ZMQ_PORT> <TEMPLATE_SCRIPT_PATH> <OUTPUT_DIRECTORY>" )
111- print ("Example: python3 copy_with_port_portname.py FUNBODY_REP_1 \" 2355\" \" ./templates/funbody_base.py\" \" ./generated_scripts/\" " )
152+ if len (sys .argv ) != 4 :
153+ print ("\n Usage: python3 copy_with_port_portname.py <TEMPLATE_SCRIPT_PATH> <OUTPUT_DIRECTORY> '<JSON_PARAMETERS>'\n " )
154+ print ("Example JSON: '[{\" port\" : \" 2355\" , \" port_name\" : \" FUNBODY_REP_1\" , \" source_node_label\" : \" nodeA\" , \" target_node_label\" : \" nodeB\" }]'" )
155+ print ("Note: The JSON string must be enclosed in single quotes in shell.\n " )
112156 sys .exit (1 )
113157
114- new_port_name_arg = sys .argv [1 ]
115- new_bind_address_arg = sys .argv [2 ]
116- template_script_path_arg = sys .argv [3 ]
117- output_directory_arg = sys .argv [4 ]
158+ template_script_path_arg = sys .argv [1 ]
159+ output_directory_arg = sys .argv [2 ]
160+ json_params_arg = sys .argv [3 ]
118161
119- create_modified_script (template_script_path_arg , new_port_name_arg , new_bind_address_arg , output_directory_arg )
162+ create_modified_script (template_script_path_arg , output_directory_arg , json_params_arg )
0 commit comments