forked from aegis/pyserveX
feat: Add CLI for PyServe with configuration options
- Introduced a new CLI module (`cli.py`) to manage server configurations via command line arguments. - Added script entry point in `pyproject.toml` for easy access to the CLI. - Enhanced `Config` class to load configurations from a YAML file. - Updated `__init__.py` to include `__version__` in the module exports. - Added optional dependencies for development tools in `pyproject.toml`. - Implemented logging improvements and error handling in various modules. - Created tests for the CLI functionality to ensure proper behavior. - Removed the old `run.py` implementation in favor of the new CLI approach.
This commit is contained in:
+22
-24
@@ -1,7 +1,6 @@
|
||||
import yaml
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, List, Optional
|
||||
from typing import Dict, Any, List
|
||||
from dataclasses import dataclass, field
|
||||
import logging
|
||||
from .logging_utils import setup_logging
|
||||
@@ -60,26 +59,26 @@ class Config:
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
data = yaml.safe_load(f)
|
||||
|
||||
|
||||
return cls._from_dict(data)
|
||||
except FileNotFoundError:
|
||||
logging.warning(f"Конфигурационный файл {file_path} не найден. Используются значения по умолчанию.")
|
||||
logging.warning(f"Configuration file {file_path} not found. Using default values.")
|
||||
return cls()
|
||||
except yaml.YAMLError as e:
|
||||
logging.error(f"Ошибка парсинга YAML файла {file_path}: {e}")
|
||||
logging.error(f"YAML file parsing error {file_path}: {e}")
|
||||
raise
|
||||
|
||||
@classmethod
|
||||
def _from_dict(cls, data: Dict[str, Any]) -> "Config":
|
||||
config = cls()
|
||||
|
||||
|
||||
if 'http' in data:
|
||||
http_data = data['http']
|
||||
config.http = HttpConfig(
|
||||
static_dir=http_data.get('static_dir', config.http.static_dir),
|
||||
templates_dir=http_data.get('templates_dir', config.http.templates_dir)
|
||||
)
|
||||
|
||||
|
||||
if 'server' in data:
|
||||
server_data = data['server']
|
||||
config.server = ServerConfig(
|
||||
@@ -89,7 +88,7 @@ class Config:
|
||||
default_root=server_data.get('default_root', config.server.default_root),
|
||||
redirect_instructions=server_data.get('redirect_instructions', {})
|
||||
)
|
||||
|
||||
|
||||
if 'ssl' in data:
|
||||
ssl_data = data['ssl']
|
||||
config.ssl = SSLConfig(
|
||||
@@ -97,7 +96,7 @@ class Config:
|
||||
cert_file=ssl_data.get('cert_file', config.ssl.cert_file),
|
||||
key_file=ssl_data.get('key_file', config.ssl.key_file)
|
||||
)
|
||||
|
||||
|
||||
if 'logging' in data:
|
||||
log_data = data['logging']
|
||||
config.logging = LoggingConfig(
|
||||
@@ -105,7 +104,7 @@ class Config:
|
||||
console_output=log_data.get('console_output', config.logging.console_output),
|
||||
log_file=log_data.get('log_file', config.logging.log_file)
|
||||
)
|
||||
|
||||
|
||||
if 'extensions' in data:
|
||||
for ext_data in data['extensions']:
|
||||
extension = ExtensionConfig(
|
||||
@@ -113,40 +112,39 @@ class Config:
|
||||
config=ext_data.get('config', {})
|
||||
)
|
||||
config.extensions.append(extension)
|
||||
|
||||
|
||||
return config
|
||||
|
||||
def validate(self) -> bool:
|
||||
errors = []
|
||||
|
||||
|
||||
if not os.path.exists(self.http.static_dir):
|
||||
errors.append(f"Статическая директория не существует: {self.http.static_dir}")
|
||||
|
||||
errors.append(f"Static directory does not exist: {self.http.static_dir}")
|
||||
|
||||
if self.ssl.enabled:
|
||||
if not os.path.exists(self.ssl.cert_file):
|
||||
errors.append(f"SSL сертификат не найден: {self.ssl.cert_file}")
|
||||
errors.append(f"SSL certificate not found: {self.ssl.cert_file}")
|
||||
if not os.path.exists(self.ssl.key_file):
|
||||
errors.append(f"SSL ключ не найден: {self.ssl.key_file}")
|
||||
|
||||
errors.append(f"SSL key not found: {self.ssl.key_file}")
|
||||
|
||||
if not (1 <= self.server.port <= 65535):
|
||||
errors.append(f"Некорректный порт: {self.server.port}")
|
||||
|
||||
errors.append(f"Invalid port: {self.server.port}")
|
||||
|
||||
log_dir = os.path.dirname(self.logging.log_file)
|
||||
if log_dir and not os.path.exists(log_dir):
|
||||
try:
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
except OSError as e:
|
||||
errors.append(f"Невозможно создать директорию для логов: {e}")
|
||||
|
||||
errors.append(f"Unable to create log directory: {e}")
|
||||
|
||||
if errors:
|
||||
for error in errors:
|
||||
logging.error(f"Ошибка конфигурации: {error}")
|
||||
logging.error(f"Configuration error: {error}")
|
||||
return False
|
||||
|
||||
|
||||
return True
|
||||
|
||||
def setup_logging(self) -> None:
|
||||
"""Настройка системы логирования через кастомный менеджер"""
|
||||
config_dict = {
|
||||
'level': self.logging.level,
|
||||
'console_output': self.logging.console_output,
|
||||
|
||||
Reference in New Issue
Block a user