Srevarshan1502 commited on
Commit
b3d9cff
·
verified ·
1 Parent(s): 987d7ef

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +22 -86
app.py CHANGED
@@ -10,7 +10,7 @@ from typing import Dict, Any, Optional
10
  import threading
11
  import asyncio
12
  import signal
13
- from concurrent.futures import ThreadPoolExecutor # NEW: for loop.run_in_executor
14
  from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request
15
  from pydantic import BaseModel
16
  from starlette.responses import HTMLResponse
@@ -384,70 +384,31 @@ class SecurePythonExecutor:
384
  result = {'success': False, 'output': '', 'error': ''}
385
  print(f"DEBUG: run_with_timeout called. Client: {websocket_io.websocket.client}")
386
 
387
- # Store original stdin/stdout/stderr
388
- original_stdin = sys.stdin
389
- original_stdout = sys.stdout
390
- original_stderr = sys.stderr
391
-
392
  # Define the synchronous target function to be run in the executor thread
393
- def _execute_in_thread():
394
  # This function runs in a separate thread managed by the event loop's executor
395
  nonlocal result
396
- try:
397
- # Redirect sys.stdin, sys.stdout, sys.stderr for this thread
398
- sys.stdin = websocket_io
399
- sys.stdout = websocket_io
400
- sys.stderr = websocket_io
401
- print(f"DEBUG: Executor thread '{threading.current_thread().name}' started. IO redirected.")
402
-
403
- # Create restricted execution environment
404
- restricted_globals = {
405
- '__builtins__': self.safe_builtins,
406
- '__name__': '__main__',
407
- '__doc__': None,
408
- }
409
- print("DEBUG: Restricted globals prepared in executor thread.") # ADDED THIS LINE
410
-
411
- print(f"DEBUG: Preparing to execute code (executor thread): {code[:100]}...")
412
- exec(code, restricted_globals)
413
- print("DEBUG: Code execution completed successfully in executor thread.")
414
- result['success'] = True
415
-
416
- except EOFError as e:
417
- # This happens if the WebSocket disconnects during input()
418
- result['error'] = f"Input stream closed: {str(e)}"
419
- result['success'] = False
420
- print(f"ERROR: EOFError during execution in executor thread: {e}")
421
- except Exception as e:
422
- error_msg = f"{type(e).__name__}: {str(e)}\n{traceback.format_exc()}"
423
- print(f"ERROR: Unhandled exception during execution in executor thread: {error_msg}")
424
- try:
425
- # If this write fails, it's still good to log the error to stderr buffer
426
- # This asyncio.run call is crucial and runs a new event loop for this async call
427
- asyncio.run(websocket_io.write(f"Error: {error_msg}\n"))
428
- print("DEBUG: Attempted to write error via websocket_io.write from executor thread.")
429
- except Exception as write_e:
430
- print(f"ERROR: Failed to write error to WebSocket from executor thread (inner): {write_e}")
431
- websocket_io.error_buffer.write(error_msg)
432
- result['success'] = False
433
- finally:
434
- # Restore original stdin/stdout/stderr for the thread pool
435
- sys.stdin = original_stdin
436
- sys.stdout = original_stdout
437
- sys.stderr = original_stderr
438
- print(f"DEBUG: Executor thread '{threading.current_thread().name}' finished. IO restored.")
439
-
440
 
441
  # Use the event loop's default ThreadPoolExecutor to run the synchronous code
442
  loop = asyncio.get_running_loop()
443
  try:
444
- print(f"DEBUG: Scheduling _execute_in_thread in executor with timeout {self.timeout}s.")
445
- # Run the synchronous _execute_in_thread in a separate thread
446
  await asyncio.wait_for(
447
- loop.run_in_executor(None, _execute_in_thread), # None uses the default ThreadPoolExecutor
448
  timeout=self.timeout
449
  )
450
- print("DEBUG: _execute_in_thread completed via executor.")
451
  except asyncio.TimeoutError:
452
  print(f"ERROR: Code execution timed out after {self.timeout} seconds in run_in_executor.")
453
  result['error'] = f"Code execution timed out after {self.timeout} seconds"
@@ -457,7 +418,7 @@ class SecurePythonExecutor:
457
  except Exception:
458
  pass # Ignore if websocket is already closed
459
  except Exception as e:
460
- # This catches exceptions from _execute_in_thread that propagate up
461
  # (e.g., if the thread itself crashes in an unrecoverable way before our try/except)
462
  print(f"ERROR: Unexpected exception from executor: {e}")
463
  result['error'] = f"Unexpected executor error: {type(e).__name__}: {str(e)}"
@@ -468,7 +429,7 @@ class SecurePythonExecutor:
468
  pass
469
 
470
  # After execution (or timeout), collect output
471
- if result['success']: # Only if execution was successful in _execute_in_thread
472
  result['output'] = websocket_io.output_buffer.getvalue()
473
  if websocket_io.error_buffer.getvalue(): # Error buffer always checked
474
  result['error'] = result['error'] + "\n" + websocket_io.error_buffer.getvalue() if result['error'] else websocket_io.error_buffer.getvalue()
@@ -523,17 +484,17 @@ async def websocket_endpoint(websocket: WebSocket):
523
 
524
  if data["type"] == "code":
525
  code = data["content"]
526
- print(f"Received code from {websocket.client}: {code[:50]}...")
527
  # Execute code asynchronously in the background
528
  asyncio.create_task(executor.execute_code(code, websocket_io))
529
  elif data["type"] == "input":
530
  user_input = data["content"]
531
- print(f"Received input from {websocket.client}: {user_input.strip()[:50]}...")
532
  await websocket_io.input_queue.put(user_input + "\n") # Add newline for readline
533
  elif data["type"] == "ping":
534
  # Respond to pings to keep connection alive
535
  await websocket.send_text(json.dumps({"type": "pong"}))
536
- print(f"DEBUG: Sent pong to {websocket.client}")
537
  else:
538
  await websocket_io.write(f"Unknown message type: {data['type']}\n")
539
 
@@ -551,29 +512,4 @@ async def websocket_endpoint(websocket: WebSocket):
551
  websocket_io.input_queue.get_nowait()
552
  except asyncio.QueueEmpty:
553
  pass
554
- print(f"WebSocket connection closed for {websocket_io.websocket.client}") # Use websocket_io.websocket.client here too
555
-
556
- # This block is for local development using `uvicorn`
557
- # Hugging Face Spaces will use `uvicorn` directly via the Dockerfile CMD
558
- if __name__ == "__main__":
559
- import uvicorn
560
- print("Starting Interactive Python Code Executor API (FastAPI)...")
561
- print(f"Python version: {sys.version}")
562
- print(f"Working directory: {os.getcwd()}")
563
-
564
- server_name = os.getenv('FASTAPI_SERVER_NAME', '0.0.0.0')
565
- server_port = int(os.getenv('FASTAPI_SERVER_PORT', 7860))
566
-
567
- print(f"Starting server on {server_name}:{server_port}")
568
-
569
- try:
570
- uvicorn.run(
571
- "app:app", # app:app means the `app` object in `app.py`
572
- host=server_name,
573
- port=server_port,
574
- reload=False, # Set to True for local dev to auto-reload on code changes
575
- log_level="info"
576
- )
577
- except Exception as e:
578
- print(f"Failed to start server: {e}")
579
- sys.exit(1)
 
10
  import threading
11
  import asyncio
12
  import signal
13
+ from concurrent.futures import ThreadPoolExecutor
14
  from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request
15
  from pydantic import BaseModel
16
  from starlette.responses import HTMLResponse
 
384
  result = {'success': False, 'output': '', 'error': ''}
385
  print(f"DEBUG: run_with_timeout called. Client: {websocket_io.websocket.client}")
386
 
 
 
 
 
 
387
  # Define the synchronous target function to be run in the executor thread
388
+ def _execute_in_thread_test1():
389
  # This function runs in a separate thread managed by the event loop's executor
390
  nonlocal result
391
+
392
+ # --- START TEST 1 MODIFICATIONS ---
393
+ # DO NOT redirect sys.stdin/stdout/stderr here yet
394
+ print(f"DEBUG: Executor thread '{threading.current_thread().name}' started. (No IO redirected yet)") # UPDATED PRINT
395
+ result['success'] = True
396
+ # Do not call exec yet. Just write directly to output_buffer for testing.
397
+ websocket_io.output_buffer.write("DEBUG: Test message from executor thread (no exec).\n") # Write to buffer directly
398
+ # Do not call asyncio.run(websocket_io.write) here. The main loop will check buffer.
399
+ print("DEBUG: _execute_in_thread finished with direct buffer write.") # ADDED THIS LINE
400
+ # --- END TEST 1 MODIFICATIONS ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
 
402
  # Use the event loop's default ThreadPoolExecutor to run the synchronous code
403
  loop = asyncio.get_running_loop()
404
  try:
405
+ print(f"DEBUG: Scheduling _execute_in_thread_test1 in executor with timeout {self.timeout}s.")
406
+ # Run the synchronous _execute_in_thread_test1 in a separate thread
407
  await asyncio.wait_for(
408
+ loop.run_in_executor(None, _execute_in_thread_test1), # None uses the default ThreadPoolExecutor
409
  timeout=self.timeout
410
  )
411
+ print("DEBUG: _execute_in_thread_test1 completed via executor.")
412
  except asyncio.TimeoutError:
413
  print(f"ERROR: Code execution timed out after {self.timeout} seconds in run_in_executor.")
414
  result['error'] = f"Code execution timed out after {self.timeout} seconds"
 
418
  except Exception:
419
  pass # Ignore if websocket is already closed
420
  except Exception as e:
421
+ # This catches exceptions from _execute_in_thread_test1 that propagate up
422
  # (e.g., if the thread itself crashes in an unrecoverable way before our try/except)
423
  print(f"ERROR: Unexpected exception from executor: {e}")
424
  result['error'] = f"Unexpected executor error: {type(e).__name__}: {str(e)}"
 
429
  pass
430
 
431
  # After execution (or timeout), collect output
432
+ if result['success']: # Only if execution was successful in _execute_in_thread_test1
433
  result['output'] = websocket_io.output_buffer.getvalue()
434
  if websocket_io.error_buffer.getvalue(): # Error buffer always checked
435
  result['error'] = result['error'] + "\n" + websocket_io.error_buffer.getvalue() if result['error'] else websocket_io.error_buffer.getvalue()
 
484
 
485
  if data["type"] == "code":
486
  code = data["content"]
487
+ print(f"Received code from {websocket_io.websocket.client}: {code[:50]}...") # Use websocket_io.websocket.client here
488
  # Execute code asynchronously in the background
489
  asyncio.create_task(executor.execute_code(code, websocket_io))
490
  elif data["type"] == "input":
491
  user_input = data["content"]
492
+ print(f"Received input from {websocket_io.websocket.client}: {user_input.strip()[:50]}...") # Use websocket_io.websocket.client here
493
  await websocket_io.input_queue.put(user_input + "\n") # Add newline for readline
494
  elif data["type"] == "ping":
495
  # Respond to pings to keep connection alive
496
  await websocket.send_text(json.dumps({"type": "pong"}))
497
+ print(f"DEBUG: Sent pong to {websocket_io.websocket.client}") # Use websocket_io.websocket.client here
498
  else:
499
  await websocket_io.write(f"Unknown message type: {data['type']}\n")
500
 
 
512
  websocket_io.input_queue.get_nowait()
513
  except asyncio.QueueEmpty:
514
  pass
515
+ print(f"WebSocket connection closed for {websocket_io.websocket.client}")