Srevarshan1502 commited on
Commit
157adb8
·
verified ·
1 Parent(s): 2baa515

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +38 -36
app.py CHANGED
@@ -60,7 +60,6 @@ class WebSocketInputOutput:
60
  """Reads a line from stdin, waiting for input from WebSocket."""
61
  try:
62
  print("DEBUG: Sending input_request to client.")
63
- # CORRECTED: Access send_text via self.websocket
64
  await self.websocket.send_text(json.dumps({"type": "input_request"}))
65
  line = await self.input_queue.get() # Wait for input from client
66
  print(f"DEBUG: Received input from queue: {line.strip()[:50]}...")
@@ -79,7 +78,6 @@ class WebSocketInputOutput:
79
  """Synchronous wrapper for readline for use with exec."""
80
  try:
81
  print("DEBUG: Synchronous input wrapper called.")
82
- # This is called from the executor thread, so asyncio.run is appropriate here
83
  return asyncio.run(self.readline())
84
  except Exception as e:
85
  print(f"ERROR: Synchronous input wrapper error: {e}")
@@ -386,30 +384,44 @@ class SecurePythonExecutor:
386
  print(f"DEBUG: run_with_timeout called. Client: {websocket_io.websocket.client}")
387
 
388
  # Define the synchronous target function to be run in the executor thread
389
- def _execute_in_thread_test1():
390
- # This function runs in a separate thread managed by the event loop's executor
391
  nonlocal result
392
-
393
- # --- START TEST 1 MODIFICATIONS ---
394
- # DO NOT redirect sys.stdin/stdout/stderr here yet
395
- print(f"DEBUG: Executor thread '{threading.current_thread().name}' started. (No IO redirected yet)")
396
- result['success'] = True
397
- # Do not call exec yet. Just write directly to output_buffer for testing.
398
- websocket_io.output_buffer.write("DEBUG: Test message from executor thread (no exec).\n")
399
- # Do not call asyncio.run(websocket_io.write) here. The main loop will check buffer.
400
- print("DEBUG: _execute_in_thread finished with direct buffer write.")
401
- # --- END TEST 1 MODIFICATIONS ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402
 
403
  # Use the event loop's default ThreadPoolExecutor to run the synchronous code
404
  loop = asyncio.get_running_loop()
405
  try:
406
- print(f"DEBUG: Scheduling _execute_in_thread_test1 in executor with timeout {self.timeout}s.")
407
- # Run the synchronous _execute_in_thread_test1 in a separate thread
408
  await asyncio.wait_for(
409
- loop.run_in_executor(None, _execute_in_thread_test1), # None uses the default ThreadPoolExecutor
410
  timeout=self.timeout
411
  )
412
- print("DEBUG: _execute_in_thread_test1 completed via executor.")
413
  except asyncio.TimeoutError:
414
  print(f"ERROR: Code execution timed out after {self.timeout} seconds in run_in_executor.")
415
  result['error'] = f"Code execution timed out after {self.timeout} seconds"
@@ -419,8 +431,7 @@ class SecurePythonExecutor:
419
  except Exception:
420
  pass # Ignore if websocket is already closed
421
  except Exception as e:
422
- # This catches exceptions from _execute_in_thread_test1 that propagate up
423
- # (e.g., if the thread itself crashes in an unrecoverable way before our try/except)
424
  print(f"ERROR: Unexpected exception from executor: {e}")
425
  result['error'] = f"Unexpected executor error: {type(e).__name__}: {str(e)}"
426
  result['success'] = False
@@ -430,21 +441,20 @@ class SecurePythonExecutor:
430
  pass
431
 
432
  # After execution (or timeout), collect output
433
- if result['success']: # Only if execution was successful in _execute_in_thread_test1
434
  result['output'] = websocket_io.output_buffer.getvalue()
435
- if websocket_io.error_buffer.getvalue(): # Error buffer always checked
436
  result['error'] = result['error'] + "\n" + websocket_io.error_buffer.getvalue() if result['error'] else websocket_io.error_buffer.getvalue()
437
- result['success'] = False # Ensure success is false if there's any error output
438
 
439
  # Send a final message to the client indicating execution is complete
440
  try:
441
  print(f"DEBUG: Attempting to send execution_complete message (success={result['success']}).")
442
- # CORRECTED: Access send_text via websocket_io.websocket
443
  await websocket_io.websocket.send_text(json.dumps({"type": "execution_complete", "result": result}))
444
  print("DEBUG: execution_complete message sent.")
445
  except WebSocketDisconnect:
446
  print("DEBUG: WebSocketDisconnect when sending execution_complete.")
447
- pass # Ignore if websocket is already closed
448
  except Exception as e:
449
  print(f"ERROR: Error sending execution_complete message: {e}")
450
 
@@ -466,8 +476,6 @@ app = FastAPI(
466
  @app.get("/", response_class=HTMLResponse)
467
  async def get_index():
468
  """Serves the interactive frontend."""
469
- # This assumes index.html is in the same directory as app.py
470
- # Explicitly copy index.html to /app/index.html to avoid any ambiguity with '.'
471
  with open("index.html", "r") as f:
472
  return HTMLResponse(content=f.read())
473
 
@@ -487,15 +495,12 @@ async def websocket_endpoint(websocket: WebSocket):
487
  if data["type"] == "code":
488
  code = data["content"]
489
  print(f"Received code from {websocket_io.websocket.client}: {code[:50]}...")
490
- # Execute code asynchronously in the background
491
  asyncio.create_task(executor.execute_code(code, websocket_io))
492
  elif data["type"] == "input":
493
  user_input = data["content"]
494
  print(f"Received input from {websocket_io.websocket.client}: {user_input.strip()[:50]}...")
495
- await websocket_io.input_queue.put(user_input + "\n") # Add newline for readline
496
  elif data["type"] == "ping":
497
- # Respond to pings to keep connection alive
498
- # CORRECTED: Access send_text via websocket_io.websocket
499
  await websocket_io.websocket.send_text(json.dumps({"type": "pong"}))
500
  print(f"DEBUG: Sent pong to {websocket_io.websocket.client}")
501
  else:
@@ -509,7 +514,6 @@ async def websocket_endpoint(websocket: WebSocket):
509
  print(f"WebSocket error for {websocket.client}: {e}")
510
  finally:
511
  websocket_io.closed = True
512
- # Clean up any pending input requests if the client disconnects
513
  while not websocket_io.input_queue.empty():
514
  try:
515
  websocket_io.input_queue.get_nowait()
@@ -517,8 +521,6 @@ async def websocket_endpoint(websocket: WebSocket):
517
  pass
518
  print(f"WebSocket connection closed for {websocket_io.websocket.client}")
519
 
520
- # This block is for local development using `uvicorn`
521
- # Hugging Face Spaces will use `uvicorn` directly via the Dockerfile CMD
522
  if __name__ == "__main__":
523
  import uvicorn
524
  print("Starting Interactive Python Code Executor API (FastAPI)...")
@@ -532,10 +534,10 @@ if __name__ == "__main__":
532
 
533
  try:
534
  uvicorn.run(
535
- "app:app", # app:app means the `app` object in `app.py`
536
  host=server_name,
537
  port=server_port,
538
- reload=False, # Set to True for local dev to auto-reload on code changes
539
  log_level="info"
540
  )
541
  except Exception as e:
 
60
  """Reads a line from stdin, waiting for input from WebSocket."""
61
  try:
62
  print("DEBUG: Sending input_request to client.")
 
63
  await self.websocket.send_text(json.dumps({"type": "input_request"}))
64
  line = await self.input_queue.get() # Wait for input from client
65
  print(f"DEBUG: Received input from queue: {line.strip()[:50]}...")
 
78
  """Synchronous wrapper for readline for use with exec."""
79
  try:
80
  print("DEBUG: Synchronous input wrapper called.")
 
81
  return asyncio.run(self.readline())
82
  except Exception as e:
83
  print(f"ERROR: Synchronous input wrapper error: {e}")
 
384
  print(f"DEBUG: run_with_timeout called. Client: {websocket_io.websocket.client}")
385
 
386
  # Define the synchronous target function to be run in the executor thread
387
+ def _execute_in_thread_test2(): # Renamed to test2 for clarity
 
388
  nonlocal result
389
+ original_stdin = sys.stdin
390
+ original_stdout = sys.stdout
391
+ original_stderr = sys.stderr
392
+ try:
393
+ sys.stdin = websocket_io
394
+ sys.stdout = websocket_io
395
+ sys.stderr = websocket_io
396
+ print(f"DEBUG: Executor thread '{threading.current_thread().name}' started. IO redirected.")
397
+ print("DEBUG: This should now go via websocket_io.write!")
398
+
399
+ # We are still not calling exec here, just testing sys.stdout print
400
+ print("Hello from redirected stdout!")
401
+
402
+ result['success'] = True
403
+ except Exception as e:
404
+ error_msg = f"{type(e).__name__}: {str(e)}\n{traceback.format_exc()}"
405
+ print(f"ERROR: Exception in _execute_in_thread_test2 during sys redirection test: {error_msg}")
406
+ websocket_io.error_buffer.write(error_msg)
407
+ result['success'] = False
408
+ finally:
409
+ sys.stdin = original_stdin
410
+ sys.stdout = original_stdout
411
+ sys.stderr = original_stderr
412
+ print(f"DEBUG: Executor thread '{threading.current_thread().name}' finished. IO restored.")
413
+
414
 
415
  # Use the event loop's default ThreadPoolExecutor to run the synchronous code
416
  loop = asyncio.get_running_loop()
417
  try:
418
+ print(f"DEBUG: Scheduling _execute_in_thread_test2 in executor with timeout {self.timeout}s.")
419
+ # Run the synchronous _execute_in_thread_test2 in a separate thread
420
  await asyncio.wait_for(
421
+ loop.run_in_executor(None, _execute_in_thread_test2), # Changed to _execute_in_thread_test2
422
  timeout=self.timeout
423
  )
424
+ print("DEBUG: _execute_in_thread_test2 completed via executor.")
425
  except asyncio.TimeoutError:
426
  print(f"ERROR: Code execution timed out after {self.timeout} seconds in run_in_executor.")
427
  result['error'] = f"Code execution timed out after {self.timeout} seconds"
 
431
  except Exception:
432
  pass # Ignore if websocket is already closed
433
  except Exception as e:
434
+ # This catches exceptions from _execute_in_thread_test2 that propagate up
 
435
  print(f"ERROR: Unexpected exception from executor: {e}")
436
  result['error'] = f"Unexpected executor error: {type(e).__name__}: {str(e)}"
437
  result['success'] = False
 
441
  pass
442
 
443
  # After execution (or timeout), collect output
444
+ if result['success']:
445
  result['output'] = websocket_io.output_buffer.getvalue()
446
+ if websocket_io.error_buffer.getvalue():
447
  result['error'] = result['error'] + "\n" + websocket_io.error_buffer.getvalue() if result['error'] else websocket_io.error_buffer.getvalue()
448
+ result['success'] = False
449
 
450
  # Send a final message to the client indicating execution is complete
451
  try:
452
  print(f"DEBUG: Attempting to send execution_complete message (success={result['success']}).")
 
453
  await websocket_io.websocket.send_text(json.dumps({"type": "execution_complete", "result": result}))
454
  print("DEBUG: execution_complete message sent.")
455
  except WebSocketDisconnect:
456
  print("DEBUG: WebSocketDisconnect when sending execution_complete.")
457
+ pass
458
  except Exception as e:
459
  print(f"ERROR: Error sending execution_complete message: {e}")
460
 
 
476
  @app.get("/", response_class=HTMLResponse)
477
  async def get_index():
478
  """Serves the interactive frontend."""
 
 
479
  with open("index.html", "r") as f:
480
  return HTMLResponse(content=f.read())
481
 
 
495
  if data["type"] == "code":
496
  code = data["content"]
497
  print(f"Received code from {websocket_io.websocket.client}: {code[:50]}...")
 
498
  asyncio.create_task(executor.execute_code(code, websocket_io))
499
  elif data["type"] == "input":
500
  user_input = data["content"]
501
  print(f"Received input from {websocket_io.websocket.client}: {user_input.strip()[:50]}...")
502
+ await websocket_io.input_queue.put(user_input + "\n")
503
  elif data["type"] == "ping":
 
 
504
  await websocket_io.websocket.send_text(json.dumps({"type": "pong"}))
505
  print(f"DEBUG: Sent pong to {websocket_io.websocket.client}")
506
  else:
 
514
  print(f"WebSocket error for {websocket.client}: {e}")
515
  finally:
516
  websocket_io.closed = True
 
517
  while not websocket_io.input_queue.empty():
518
  try:
519
  websocket_io.input_queue.get_nowait()
 
521
  pass
522
  print(f"WebSocket connection closed for {websocket_io.websocket.client}")
523
 
 
 
524
  if __name__ == "__main__":
525
  import uvicorn
526
  print("Starting Interactive Python Code Executor API (FastAPI)...")
 
534
 
535
  try:
536
  uvicorn.run(
537
+ "app:app",
538
  host=server_name,
539
  port=server_port,
540
+ reload=False,
541
  log_level="info"
542
  )
543
  except Exception as e: