Core Systems:

Component-based architecture with lifecycle management
Enhanced error handling and recovery mechanisms
Comprehensive state management and tracking
Event-driven architecture with monitoring
Queue Management:

Multiple processing strategies for different scenarios
Advanced state management with recovery
Comprehensive metrics and health monitoring
Sophisticated cleanup system with multiple strategies
Processing Pipeline:

Enhanced message handling with validation
Improved URL extraction and processing
Better queue management and monitoring
Advanced cleanup mechanisms
Overall Benefits:

Better code organization and maintainability
Improved error handling and recovery
Enhanced monitoring and reporting
More robust and reliable system
This commit is contained in:
pacnpal
2024-11-16 05:01:29 +00:00
parent 537a325807
commit a4ca6e8ea6
47 changed files with 11085 additions and 2110 deletions

View File

@@ -7,92 +7,217 @@ import stat
import logging
import contextlib
import time
from typing import Generator, List, Optional
from pathlib import Path
from .exceptions import FileCleanupError
from .permission_manager import PermissionManager
logger = logging.getLogger("VideoArchiver")
logger = logging.getLogger("PathManager")
@contextlib.contextmanager
def temp_path_context():
"""Context manager for temporary path creation and cleanup
Yields:
str: Path to temporary directory
class TempDirectoryManager:
"""Manages temporary directory creation and cleanup"""
def __init__(self):
self.permission_manager = PermissionManager()
self.max_retries = 3
self.retry_delay = 1
async def create_temp_dir(self, prefix: str = "videoarchiver_") -> str:
"""Create a temporary directory with proper permissions
Raises:
FileCleanupError: If directory creation or cleanup fails
"""
temp_dir = None
try:
# Create temp directory with proper permissions
temp_dir = tempfile.mkdtemp(prefix="videoarchiver_")
logger.debug(f"Created temporary directory: {temp_dir}")
# Ensure directory has rwx permissions for user only
try:
os.chmod(temp_dir, stat.S_IRWXU)
except OSError as e:
raise FileCleanupError(f"Failed to set permissions on temporary directory: {str(e)}")
# Verify directory exists and is writable
if not os.path.exists(temp_dir):
raise FileCleanupError(f"Failed to create temporary directory: {temp_dir}")
if not os.access(temp_dir, os.W_OK):
raise FileCleanupError(f"Temporary directory is not writable: {temp_dir}")
Args:
prefix: Prefix for temporary directory name
yield temp_dir
except FileCleanupError:
raise
except Exception as e:
logger.error(f"Error in temp_path_context: {str(e)}")
raise FileCleanupError(f"Temporary directory error: {str(e)}")
finally:
if temp_dir and os.path.exists(temp_dir):
cleanup_errors = []
try:
# Ensure all files are deletable with retries
max_retries = 3
for attempt in range(max_retries):
try:
# Set permissions recursively
for root, dirs, files in os.walk(temp_dir):
for d in dirs:
try:
dir_path = os.path.join(root, d)
os.chmod(dir_path, stat.S_IRWXU)
except OSError as e:
cleanup_errors.append(f"Failed to set permissions on directory {dir_path}: {e}")
for f in files:
try:
file_path = os.path.join(root, f)
os.chmod(file_path, stat.S_IRWXU)
except OSError as e:
cleanup_errors.append(f"Failed to set permissions on file {file_path}: {e}")
# Try to remove the directory
shutil.rmtree(temp_dir, ignore_errors=True)
# Verify directory is gone
if not os.path.exists(temp_dir):
logger.debug(f"Successfully cleaned up temporary directory: {temp_dir}")
break
if attempt < max_retries - 1:
time.sleep(1) # Wait before retry
except Exception as e:
if attempt == max_retries - 1:
cleanup_errors.append(f"Failed to clean up temporary directory {temp_dir} after {max_retries} attempts: {e}")
elif attempt < max_retries - 1:
time.sleep(1) # Wait before retry
continue
except Exception as e:
cleanup_errors.append(f"Error during temp directory cleanup: {str(e)}")
Returns:
str: Path to temporary directory
Raises:
FileCleanupError: If directory creation fails
"""
try:
# Create temp directory
temp_dir = tempfile.mkdtemp(prefix=prefix)
logger.debug(f"Created temporary directory: {temp_dir}")
# Set proper permissions
await self.permission_manager.set_permissions(
temp_dir,
stat.S_IRWXU, # rwx for user only
recursive=False
)
# Verify directory
if not await self._verify_directory(temp_dir):
raise FileCleanupError(f"Failed to verify temporary directory: {temp_dir}")
if cleanup_errors:
error_msg = "\n".join(cleanup_errors)
logger.error(error_msg)
# Don't raise here as we're in finally block and don't want to mask original error
return temp_dir
except Exception as e:
logger.error(f"Error creating temporary directory: {e}")
raise FileCleanupError(f"Failed to create temporary directory: {str(e)}")
async def cleanup_temp_dir(self, temp_dir: str) -> List[str]:
"""Clean up a temporary directory
Args:
temp_dir: Path to temporary directory
Returns:
List[str]: List of any cleanup errors
"""
if not temp_dir or not os.path.exists(temp_dir):
return []
cleanup_errors = []
try:
# Set permissions recursively
await self._prepare_for_cleanup(temp_dir, cleanup_errors)
# Attempt cleanup with retries
for attempt in range(self.max_retries):
try:
# Remove directory
shutil.rmtree(temp_dir, ignore_errors=True)
# Verify removal
if not os.path.exists(temp_dir):
logger.debug(f"Successfully cleaned up temporary directory: {temp_dir}")
break
if attempt < self.max_retries - 1:
await self._retry_delay()
except Exception as e:
if attempt == self.max_retries - 1:
cleanup_errors.append(
f"Failed to clean up temporary directory {temp_dir} "
f"after {self.max_retries} attempts: {e}"
)
elif attempt < self.max_retries - 1:
await self._retry_delay()
continue
except Exception as e:
cleanup_errors.append(f"Error during temp directory cleanup: {str(e)}")
return cleanup_errors
async def _prepare_for_cleanup(
self,
temp_dir: str,
cleanup_errors: List[str]
) -> None:
"""Prepare directory for cleanup by setting permissions"""
for root, dirs, files in os.walk(temp_dir):
# Set directory permissions
for d in dirs:
try:
dir_path = os.path.join(root, d)
await self.permission_manager.set_permissions(
dir_path,
stat.S_IRWXU
)
except Exception as e:
cleanup_errors.append(
f"Failed to set permissions on directory {dir_path}: {e}"
)
# Set file permissions
for f in files:
try:
file_path = os.path.join(root, f)
await self.permission_manager.set_permissions(
file_path,
stat.S_IRWXU
)
except Exception as e:
cleanup_errors.append(
f"Failed to set permissions on file {file_path}: {e}"
)
async def _verify_directory(self, directory: str) -> bool:
"""Verify a directory exists and is writable"""
if not os.path.exists(directory):
return False
return await self.permission_manager.check_permissions(
directory,
require_writable=True,
require_readable=True,
require_executable=True
)
async def _retry_delay(self) -> None:
"""Sleep between retry attempts"""
await asyncio.sleep(self.retry_delay)
class PathManager:
"""Manages path operations and validation"""
def __init__(self):
self.temp_dir_manager = TempDirectoryManager()
@contextlib.asynccontextmanager
async def temp_path_context(
self,
prefix: str = "videoarchiver_"
) -> Generator[str, None, None]:
"""Async context manager for temporary path creation and cleanup
Args:
prefix: Prefix for temporary directory name
Yields:
str: Path to temporary directory
Raises:
FileCleanupError: If directory creation or cleanup fails
"""
temp_dir = None
try:
# Create temporary directory
temp_dir = await self.temp_dir_manager.create_temp_dir(prefix)
yield temp_dir
except FileCleanupError:
raise
except Exception as e:
logger.error(f"Error in temp_path_context: {str(e)}")
raise FileCleanupError(f"Temporary directory error: {str(e)}")
finally:
if temp_dir:
# Clean up directory
cleanup_errors = await self.temp_dir_manager.cleanup_temp_dir(temp_dir)
if cleanup_errors:
error_msg = "\n".join(cleanup_errors)
logger.error(error_msg)
# Don't raise here as we're in finally block
async def ensure_directory(self, directory: str) -> None:
"""Ensure a directory exists with proper permissions
Args:
directory: Path to ensure exists
Raises:
FileCleanupError: If directory cannot be created or accessed
"""
try:
path = Path(directory)
path.mkdir(parents=True, exist_ok=True)
# Set proper permissions
await self.temp_dir_manager.permission_manager.set_permissions(
directory,
stat.S_IRWXU
)
# Verify directory
if not await self.temp_dir_manager._verify_directory(directory):
raise FileCleanupError(f"Failed to verify directory: {directory}")
except Exception as e:
logger.error(f"Error ensuring directory {directory}: {e}")
raise FileCleanupError(f"Failed to ensure directory: {str(e)}")