diff --git a/src/api/app.py b/src/api/app.py index 8386ca0..bc86250 100644 --- a/src/api/app.py +++ b/src/api/app.py @@ -26,6 +26,9 @@ DEFAULT_MEMORY_LIMIT = min(80 * 1024 * MEGABYTE, SAFE_LIMIT) # 80GB MEMORY_USAGE_PERCENTAGE = 0.8 # 80% +ERR_PORT_IN_USE = 98 +ERR_PERMISSION_DENIED = 13 + # Set up logging logging_level = get_env_variable("LOGGING_LEVEL", "INFO") logger = setup_logging(logging_level) @@ -162,10 +165,10 @@ async def run_server(): await server.serve() except OSError as e: logger.error(f"Error starting server: {e}") - if e.errno == 98: + if e.errno == ERR_PORT_IN_USE: logger.error( f"Port {port} is already in use. Try a different port.") - elif e.errno == 13: + elif e.errno == ERR_PERMISSION_DENIED: logger.error( f"Permission denied when trying to bind to port {port}. Try a " "port number > 1024 or run with sudo.") diff --git a/src/api/batch_decoder.py b/src/api/batch_decoder.py index 50b9b35..11f6b70 100644 --- a/src/api/batch_decoder.py +++ b/src/api/batch_decoder.py @@ -194,35 +194,29 @@ def save_prediction_outputs( os.makedirs(temp_dir, exist_ok=True) logging.debug(f"Using temporary directory: {temp_dir}") - try: - for prediction, group_id, image_id, metadata in zip( - prediction_data, group_ids, image_ids, image_metadata - ): - confidence, predicted_text = prediction - output_text = ( - f"{image_id}\t{metadata}\t{confidence}\t{predicted_text}") - output_texts.append(output_text) - - group_output_dir = os.path.join(base_output_path, group_id) - os.makedirs(group_output_dir, exist_ok=True) - logging.debug("Ensured output directory exists: %s", - group_output_dir) - - output_file_path = os.path.join( - group_output_dir, f"{image_id}.txt") - - try: - write_file_atomically(output_text, output_file_path, temp_dir) - logging.debug("Atomically wrote file: %s", output_file_path) - except IOError as e: - logging.error("Failed to write file %s. Error: %s", - output_file_path, e) - raise - finally: - # Clean up the temporary directory - if os.path.exists(temp_dir): - shutil.rmtree(temp_dir) - logging.debug(f"Cleaned up temporary directory: {temp_dir}") + for prediction, group_id, image_id, metadata in zip( + prediction_data, group_ids, image_ids, image_metadata + ): + confidence, predicted_text = prediction + output_text = ( + f"{image_id}\t{metadata}\t{confidence}\t{predicted_text}") + output_texts.append(output_text) + + group_output_dir = os.path.join(base_output_path, group_id) + os.makedirs(group_output_dir, exist_ok=True) + logging.debug("Ensured output directory exists: %s", + group_output_dir) + + output_file_path = os.path.join( + group_output_dir, f"{image_id}.txt") + + try: + write_file_atomically(output_text, output_file_path, temp_dir) + logging.debug("Atomically wrote file: %s", output_file_path) + except IOError as e: + logging.error("Failed to write file %s. Error: %s", + output_file_path, e) + raise return output_texts @@ -250,15 +244,21 @@ def write_file_atomically(content: str, target_path: str, temp_dir: str) \ try: # Create a temporary file in the provided temporary directory with tempfile.NamedTemporaryFile(mode='w', dir=temp_dir, - delete=True, encoding="utf-8") \ + delete=False, encoding="utf-8") \ as temp_file: temp_file.write(content + "\n") temp_file_path = temp_file.name - os.replace(temp_file_path, target_path) # On POSIX systems, this is atomic. On Windows, it's the best we can do + os.replace(temp_file_path, target_path) except IOError as e: + if temp_file_path and os.path.exists(temp_file_path): + # Clean up the temporary file if it exists + os.unlink(temp_file_path) raise IOError( f"Failed to atomically write file: {target_path}. Error: {e}") except Exception as e: + if temp_file_path and os.path.exists(temp_file_path): + # Clean up the temporary file if it exists + os.unlink(temp_file_path) raise e # Re-raise the exception after cleanup diff --git a/src/api/routes.py b/src/api/routes.py index 2bbb04d..f151c22 100644 --- a/src/api/routes.py +++ b/src/api/routes.py @@ -2,6 +2,7 @@ # > Standard library import datetime +import logging from typing import List, Optional from multiprocessing.queues import Full @@ -83,6 +84,7 @@ async def predict( "high volume of requests. Please try again " "later.") + logging.info(f"Request received: {group_id} - {identifier}") return _create_response(202, "Request received", "Your request is " "being processed", extra={"group_id": group_id,