Skip to content

Commit

Permalink
fix input overlap, next fix restart of server
Browse files Browse the repository at this point in the history
  • Loading branch information
vysakh0 committed Aug 9, 2024
1 parent bd6154d commit a5884d1
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 117 deletions.
5 changes: 5 additions & 0 deletions src/drd/cli/commands.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import click
import sys
import ast
Expand All @@ -15,6 +16,10 @@
VERSION = "0.13.9" # Update this as you release new versions


logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')


def parse_multiline_input(input_string):
try:
# Use ast.literal_eval to safely evaluate the string as a Python literal
Expand Down
37 changes: 24 additions & 13 deletions src/drd/cli/monitor/error_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@
from ..query.file_operations import get_files_to_modify
from ...utils.file_utils import get_file_content
from ...utils.input import confirm_with_user
import logging
import time


def monitoring_handle_error_with_dravid(error, line, monitor):
time.sleep(2)
input = confirm_with_user("Allow Dravid to handle the current error?")
if not input:
return True

print_error(f"Error detected: {error}")
logger = logging.getLogger(__name__)
logger.info(f"Starting error handling for: {error}")

error_message = str(error)
error_type = type(error).__name__
Expand Down Expand Up @@ -72,18 +81,20 @@ def monitoring_handle_error_with_dravid(error, line, monitor):

print_success("Fix applied.")

if requires_restart:
print_info("The applied fix requires a server restart.")
restart_input = confirm_with_user(
"Do you want to restart the server now? [y/N]: "
)
if restart_input:
print_info("Requesting server restart...")
monitor.request_restart()
else:
print_info(
"Server restart postponed. You may need to restart manually if issues persist.")
else:
print_info("The applied fix does not require a server restart.")
logger.info(f"User response to restart: ")
# if requires_restart:
# print_info("The applied fix requires a server restart.")
# restart_input = confirm_with_user(
# "Do you want to restart the server now? [y/N]: "
# )
# if restart_input:
print_info("Requesting server restart...")
monitor.request_restart()
# else:
# print_info(
# "Server restart postponed. You may need to restart manually if issues persist.")
# else:
# print_info("The applied fix does not require a server restart.")

logger.info("Error handling completed")
return True
4 changes: 4 additions & 0 deletions src/drd/cli/monitor/input_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@
from ...prompts.instructions import get_instruction_prompt
from .input_parser import InputParser
from ..query.main import execute_dravid_command
import logging


class InputHandler:
def __init__(self, monitor):
self.monitor = monitor
self.logger = logging.getLogger(__name__)

def handle_input(self):
self.logger.info("InputHandler triggered to handle input")
print_info("\nNo more tasks to auto-process. What can I do next?")
self._show_options()
user_input = input("> ")
self.logger.info(f"Received user input: {user_input}")
self._process_input(user_input)

def _show_options(self):
Expand Down
32 changes: 22 additions & 10 deletions src/drd/cli/monitor/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,31 @@ def run_dev_server_with_monitoring(command: str):


def handle_module_not_found(error_msg, monitor):
match = re.search(
r"(?:Cannot find module|Module not found|ImportError|No module named).*['\"](.*?)['\"]", error_msg, re.IGNORECASE)
if match:
module_name = match.group(1)
error = ImportError(f"Module '{module_name}' not found")
monitoring_handle_error_with_dravid(error, error_msg, monitor)
monitor.error_handling_in_progress.set()
try:
match = re.search(
r"(?:Cannot find module|Module not found|ImportError|No module named).*['\"](.*?)['\"]", error_msg, re.IGNORECASE)
if match:
module_name = match.group(1)
error = ImportError(f"Module '{module_name}' not found")
monitoring_handle_error_with_dravid(error, error_msg, monitor)
finally:
monitor.error_handling_in_progress.clear()


def handle_syntax_error(error_msg, monitor):
error = SyntaxError(f"Syntax error detected: {error_msg}")
monitoring_handle_error_with_dravid(error, error_msg, monitor)
monitor.error_handling_in_progress.set()
try:
error = SyntaxError(f"Syntax error detected: {error_msg}")
monitoring_handle_error_with_dravid(error, error_msg, monitor)
finally:
monitor.error_handling_in_progress.clear()


def handle_general_error(error_msg, monitor):
error = Exception(f"General error detected: {error_msg}")
monitoring_handle_error_with_dravid(error, error_msg, monitor)
monitor.error_handling_in_progress.set()
try:
error = Exception(f"General error detected: {error_msg}")
monitoring_handle_error_with_dravid(error, error_msg, monitor)
finally:
monitor.error_handling_in_progress.clear()
86 changes: 45 additions & 41 deletions src/drd/cli/monitor/output_monitor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import re
import time
import logging
import select
import time
import threading
import re

from ...utils import print_info, print_error
logger = logging.getLogger(__name__)


class OutputMonitor:
Expand All @@ -12,65 +13,68 @@ def __init__(self, monitor):
self.thread = None
self.last_output_time = None
self.idle_detected = threading.Event()
self.stop_event = threading.Event()

def start(self):
self.stop_event.clear()
self.thread = threading.Thread(
target=self._monitor_output, daemon=True)
self.thread.start()

def stop(self):
self.stop_event.set()
if self.thread:
self.thread.join(timeout=5)

def _monitor_output(self):
error_buffer = []
self.last_output_time = time.time()
while not self.monitor.should_stop.is_set():
if self.monitor.process.poll() is not None and not self.monitor.processing_input.is_set():
if not self.monitor.restart_requested.is_set():
print_info("Server process ended unexpectedly.")
if self.monitor.retry_count < self.monitor.MAX_RETRIES:
self.monitor.retry_count += 1
print_info(
f"Restarting... (Attempt {self.monitor.retry_count}/{self.monitor.MAX_RETRIES})")
self.monitor.perform_restart()
return # Exit the method after attempting restart
else:
print_error(
f"Server failed to start after {self.monitor.MAX_RETRIES} attempts. Exiting.")
self.monitor.stop()
break
else:
break # Exit the loop if restart is already requested

ready, _, _ = select.select(
[self.monitor.process.stdout], [], [], 0.1)
if self.monitor.process.stdout in ready:
line = self.monitor.process.stdout.readline()
if line:
print(line, end='', flush=True)
error_buffer.append(line)
if len(error_buffer) > 10:
error_buffer.pop(0)
self.last_output_time = time.time()
self.monitor.retry_count = 0 # Reset retry count on successful output
if not self.monitor.processing_input.is_set():
self._check_for_errors(line, error_buffer)
while not self.stop_event.is_set():
if self.monitor.process is None or self.monitor.process.poll() is not None:
logger.info(
"Server process ended or not started. Waiting for restart...")
time.sleep(1)
continue

try:
ready, _, _ = select.select(
[self.monitor.process.stdout], [], [], 0.1)
if self.monitor.process.stdout in ready:
line = self.monitor.process.stdout.readline()
if line:
line = line.strip()
print(line, flush=True)
logger.debug(f"Server output: {line}")
error_buffer.append(line)
if len(error_buffer) > 10:
error_buffer.pop(0)
self.last_output_time = time.time()
self.monitor.retry_count = 0
if not self.monitor.processing_input.is_set():
self._check_for_errors(line, error_buffer)
else:
self._check_idle_state()
else:
self._check_idle_state()
else:
self._check_idle_state()
except Exception as e:
logger.error(f"Error in output monitoring: {str(e)}")
time.sleep(1) # Prevent rapid error logging

if self.monitor.restart_requested.is_set() and not self.monitor.processing_input.is_set():
self.monitor.perform_restart()
return
logger.info("Output monitoring stopped.")

def _check_for_errors(self, line, error_buffer):
for error_pattern, handler in self.monitor.error_handlers.items():
for error_pattern in self.monitor.error_handlers.keys():
if re.search(error_pattern, line, re.IGNORECASE):
full_error = ''.join(error_buffer)
handler(full_error, self.monitor)
full_error = '\n'.join(error_buffer)
logger.error(f"Error detected: {full_error}")
self.monitor.handle_error(full_error)
error_buffer.clear()
break

def _check_idle_state(self):
current_time = time.time()
if (current_time - self.last_output_time > 5 and
not self.monitor.error_handling_in_progress.is_set() and
not self.monitor.processing_input.is_set()):
self.idle_detected.set()
Loading

0 comments on commit a5884d1

Please sign in to comment.