2929
3030class MessageType :
3131 """Message types matching C++ enum"""
32+
3233 HANDSHAKE = 0x01
3334 HANDSHAKE_ACK = 0x02
3435 SHUTDOWN = 0x03
@@ -52,6 +53,7 @@ class MessageType:
5253@dataclass
5354class MessageHeader :
5455 """Message header structure"""
56+
5557 magic : int = MAGIC
5658 version : int = VERSION
5759 msg_type : int = 0
@@ -63,24 +65,25 @@ class MessageHeader:
6365 def serialize (self ) -> bytes :
6466 """Serialize header to bytes"""
6567 return struct .pack (
66- ' >IBBIIBB' ,
68+ " >IBBIIBB" ,
6769 self .magic ,
6870 self .version ,
6971 self .msg_type ,
7072 self .payload_size ,
7173 self .sequence_id ,
7274 self .flags ,
73- self .reserved
75+ self .reserved ,
7476 )
7577
7678 @classmethod
77- def deserialize (cls , data : bytes ) -> ' MessageHeader' :
79+ def deserialize (cls , data : bytes ) -> " MessageHeader" :
7880 """Deserialize header from bytes"""
7981 if len (data ) < HEADER_SIZE :
8082 raise ValueError ("Insufficient data for header" )
8183
82- magic , version , msg_type , payload_size , sequence_id , flags , reserved = \
83- struct .unpack ('>IBBIIBB' , data [:HEADER_SIZE ])
84+ magic , version , msg_type , payload_size , sequence_id , flags , reserved = (
85+ struct .unpack (">IBBIIBB" , data [:HEADER_SIZE ])
86+ )
8487
8588 if magic != MAGIC :
8689 raise ValueError (f"Invalid magic: { magic :#x} " )
@@ -92,24 +95,23 @@ def deserialize(cls, data: bytes) -> 'MessageHeader':
9295 payload_size = payload_size ,
9396 sequence_id = sequence_id ,
9497 flags = flags ,
95- reserved = reserved
98+ reserved = reserved ,
9699 )
97100
98101
99102@dataclass
100103class Message :
101104 """IPC Message"""
105+
102106 header : MessageHeader
103107 payload : bytes
104108
105109 @classmethod
106- def create (cls , msg_type : int , payload : dict , sequence_id : int = 0 ) -> ' Message' :
110+ def create (cls , msg_type : int , payload : dict , sequence_id : int = 0 ) -> " Message" :
107111 """Create a message from JSON payload"""
108- payload_bytes = json .dumps (payload ).encode (' utf-8' )
112+ payload_bytes = json .dumps (payload ).encode (" utf-8" )
109113 header = MessageHeader (
110- msg_type = msg_type ,
111- payload_size = len (payload_bytes ),
112- sequence_id = sequence_id
114+ msg_type = msg_type , payload_size = len (payload_bytes ), sequence_id = sequence_id
113115 )
114116 return cls (header = header , payload = payload_bytes )
115117
@@ -121,7 +123,7 @@ def get_payload_json(self) -> dict:
121123 """Get payload as JSON"""
122124 if not self .payload :
123125 return {}
124- return json .loads (self .payload .decode (' utf-8' ))
126+ return json .loads (self .payload .decode (" utf-8" ))
125127
126128
127129class IPCChannel :
@@ -163,15 +165,15 @@ def receive(self, timeout: float = None) -> Optional[Message]:
163165 if payload is None :
164166 return None
165167 else :
166- payload = b''
168+ payload = b""
167169
168170 return Message (header = header , payload = payload )
169171
170172 def _read_exact (self , size : int , timeout : float = None ) -> Optional [bytes ]:
171173 """Read exactly size bytes"""
172174 import select
173175
174- data = b''
176+ data = b""
175177 while len (data ) < size :
176178 if timeout is not None :
177179 ready , _ , _ = select .select ([self .read_fd ], [], [], timeout )
@@ -270,12 +272,15 @@ def _handle_handshake(self) -> None:
270272 raise RuntimeError ("Handshake failed: no request received" )
271273
272274 # Send handshake acknowledgment
273- self .channel .send_json (MessageType .HANDSHAKE_ACK , {
274- 'version' : '1.0' ,
275- 'python_version' : sys .version ,
276- 'capabilities' : ['execute' , 'progress' , 'cancel' ],
277- 'pid' : os .getpid ()
278- })
275+ self .channel .send_json (
276+ MessageType .HANDSHAKE_ACK ,
277+ {
278+ "version" : "1.0" ,
279+ "python_version" : sys .version ,
280+ "capabilities" : ["execute" , "progress" , "cancel" ],
281+ "pid" : os .getpid (),
282+ },
283+ )
279284
280285 def _handle_message (self , msg : Message ) -> None :
281286 """Handle incoming message"""
@@ -300,27 +305,27 @@ def _handle_execute(self, msg: Message) -> None:
300305 self .start_time = time .time ()
301306
302307 payload = msg .get_payload_json ()
303- script_content = payload .get (' script_content' , '' )
304- script_path = payload .get (' script_path' , '' )
305- function_name = payload .get (' function_name' , '' )
306- arguments = payload .get (' arguments' , {})
307- working_directory = payload .get (' working_directory' , '' )
308- self .allowed_imports = payload .get (' allowed_imports' , [])
308+ script_content = payload .get (" script_content" , "" )
309+ script_path = payload .get (" script_path" , "" )
310+ function_name = payload .get (" function_name" , "" )
311+ arguments = payload .get (" arguments" , {})
312+ working_directory = payload .get (" working_directory" , "" )
313+ self .allowed_imports = payload .get (" allowed_imports" , [])
309314
310315 # Change working directory if specified
311316 if working_directory :
312317 os .chdir (working_directory )
313318
314319 result = {
315- ' success' : False ,
316- ' result' : None ,
317- ' output' : '' ,
318- ' error_output' : '' ,
319- ' exception' : '' ,
320- ' exception_type' : '' ,
321- ' traceback' : '' ,
322- ' execution_time_ms' : 0 ,
323- ' peak_memory_bytes' : 0
320+ " success" : False ,
321+ " result" : None ,
322+ " output" : "" ,
323+ " error_output" : "" ,
324+ " exception" : "" ,
325+ " exception_type" : "" ,
326+ " traceback" : "" ,
327+ " execution_time_ms" : 0 ,
328+ " peak_memory_bytes" : 0 ,
324329 }
325330
326331 try :
@@ -330,32 +335,32 @@ def _handle_execute(self, msg: Message) -> None:
330335 exec_result = self ._execute_script (script_content , arguments )
331336 elif function_name :
332337 exec_result = self ._execute_function (
333- payload .get ('module_name' , '' ),
334- function_name ,
335- arguments
338+ payload .get ("module_name" , "" ), function_name , arguments
336339 )
337340 else :
338341 raise ValueError ("No script content or function specified" )
339342
340- result [' success' ] = True
341- result [' result' ] = self ._make_serializable (exec_result )
342- result [' output' ] = capture .get_stdout ()
343- result [' error_output' ] = capture .get_stderr ()
343+ result [" success" ] = True
344+ result [" result" ] = self ._make_serializable (exec_result )
345+ result [" output" ] = capture .get_stdout ()
346+ result [" error_output" ] = capture .get_stderr ()
344347
345348 except Exception as e :
346- result [' success' ] = False
347- result [' exception' ] = str (e )
348- result [' exception_type' ] = type (e ).__name__
349- result [' traceback' ] = traceback .format_exc ()
349+ result [" success" ] = False
350+ result [" exception" ] = str (e )
351+ result [" exception_type" ] = type (e ).__name__
352+ result [" traceback" ] = traceback .format_exc ()
350353
351354 finally :
352- result [' execution_time_ms' ] = int ((time .time () - self .start_time ) * 1000 )
355+ result [" execution_time_ms" ] = int ((time .time () - self .start_time ) * 1000 )
353356
354357 # Try to get memory usage
355358 try :
356359 import resource
357- result ['peak_memory_bytes' ] = resource .getrusage (
358- resource .RUSAGE_SELF ).ru_maxrss * 1024
360+
361+ result ["peak_memory_bytes" ] = (
362+ resource .getrusage (resource .RUSAGE_SELF ).ru_maxrss * 1024
363+ )
359364 except :
360365 pass
361366
@@ -365,22 +370,23 @@ def _execute_script(self, script_content: str, arguments: dict) -> Any:
365370 """Execute a script string"""
366371 # Create namespace with arguments
367372 namespace = {
368- ' __name__' : ' __main__' ,
369- ' __args__' : arguments ,
370- ' args' : arguments ,
371- ' report_progress' : self ._report_progress ,
372- ' log' : self ._log
373+ " __name__" : " __main__" ,
374+ " __args__" : arguments ,
375+ " args" : arguments ,
376+ " report_progress" : self ._report_progress ,
377+ " log" : self ._log ,
373378 }
374379
375380 # Compile and execute
376- code = compile (script_content , ' <script>' , ' exec' )
381+ code = compile (script_content , " <script>" , " exec" )
377382 exec (code , namespace )
378383
379384 # Return result if defined
380- return namespace .get (' result' , None )
385+ return namespace .get (" result" , None )
381386
382- def _execute_function (self , module_name : str , function_name : str ,
383- arguments : dict ) -> Any :
387+ def _execute_function (
388+ self , module_name : str , function_name : str , arguments : dict
389+ ) -> Any :
384390 """Execute a function from a module"""
385391 import importlib
386392
@@ -398,31 +404,31 @@ def _execute_function(self, module_name: str, function_name: str,
398404 else :
399405 return func (arguments )
400406
401- def _report_progress (self , percentage : float , message : str = '' ,
402- current_step : str = '' ) -> None :
407+ def _report_progress (
408+ self , percentage : float , message : str = "" , current_step : str = ""
409+ ) -> None :
403410 """Report progress to parent process"""
404- elapsed_ms = int ((time .time () - self .start_time ) * 1000 ) if self .start_time else 0
411+ elapsed_ms = (
412+ int ((time .time () - self .start_time ) * 1000 ) if self .start_time else 0
413+ )
405414
406- self .channel .send_json (MessageType .PROGRESS , {
407- 'percentage' : percentage ,
408- 'message' : message ,
409- 'current_step' : current_step ,
410- 'elapsed_ms' : elapsed_ms
411- })
415+ self .channel .send_json (
416+ MessageType .PROGRESS ,
417+ {
418+ "percentage" : percentage ,
419+ "message" : message ,
420+ "current_step" : current_step ,
421+ "elapsed_ms" : elapsed_ms ,
422+ },
423+ )
412424
413425 def _log (self , level : str , message : str ) -> None :
414426 """Send log message to parent process"""
415- self .channel .send_json (MessageType .LOG , {
416- 'level' : level ,
417- 'message' : message
418- })
427+ self .channel .send_json (MessageType .LOG , {"level" : level , "message" : message })
419428
420- def _send_error (self , message : str , tb : str = '' ) -> None :
429+ def _send_error (self , message : str , tb : str = "" ) -> None :
421430 """Send error message"""
422- self .channel .send_json (MessageType .ERROR , {
423- 'message' : message ,
424- 'traceback' : tb
425- })
431+ self .channel .send_json (MessageType .ERROR , {"message" : message , "traceback" : tb })
426432
427433 def _make_serializable (self , obj : Any ) -> Any :
428434 """Convert object to JSON-serializable form"""
@@ -434,12 +440,13 @@ def _make_serializable(self, obj: Any) -> Any:
434440 return [self ._make_serializable (item ) for item in obj ]
435441 if isinstance (obj , dict ):
436442 return {str (k ): self ._make_serializable (v ) for k , v in obj .items ()}
437- if hasattr (obj , ' __dict__' ):
443+ if hasattr (obj , " __dict__" ):
438444 return self ._make_serializable (obj .__dict__ )
439445
440446 # Try numpy array
441447 try :
442448 import numpy as np
449+
443450 if isinstance (obj , np .ndarray ):
444451 return obj .tolist ()
445452 if isinstance (obj , (np .integer , np .floating )):
@@ -450,8 +457,9 @@ def _make_serializable(self, obj: Any) -> Any:
450457 # Try pandas
451458 try :
452459 import pandas as pd
460+
453461 if isinstance (obj , pd .DataFrame ):
454- return obj .to_dict (orient = ' records' )
462+ return obj .to_dict (orient = " records" )
455463 if isinstance (obj , pd .Series ):
456464 return obj .to_list ()
457465 except ImportError :
@@ -479,5 +487,5 @@ def main():
479487 executor .run ()
480488
481489
482- if __name__ == ' __main__' :
490+ if __name__ == " __main__" :
483491 main ()
0 commit comments