fixed pyservectl linter errors and formatting

This commit is contained in:
Илья Глазунов 2025-12-04 03:17:21 +03:00
parent 7662a7924a
commit 3b59994fc9
15 changed files with 87 additions and 62 deletions

14
poetry.lock generated
View File

@ -1349,6 +1349,18 @@ files = [
{file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"},
] ]
[[package]]
name = "types-psutil"
version = "7.1.3.20251202"
description = "Typing stubs for psutil"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "types_psutil-7.1.3.20251202-py3-none-any.whl", hash = "sha256:39bfc44780de7ab686c65169e36a7969db09e7f39d92de643b55789292953400"},
{file = "types_psutil-7.1.3.20251202.tar.gz", hash = "sha256:5cfecaced7c486fb3995bb290eab45043d697a261718aca01b9b340d1ab7968a"},
]
[[package]] [[package]]
name = "types-pyyaml" name = "types-pyyaml"
version = "6.0.12.20250822" version = "6.0.12.20250822"
@ -1709,4 +1721,4 @@ wsgi = ["a2wsgi"]
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.12" python-versions = ">=3.12"
content-hash = "359245cc9d83f36b9eff32deaf7a4665b25149c89b4062abab72db1c07607500" content-hash = "653d7b992e2bb133abde2e8b1c44265e948ed90487ab3f2670429510a8aa0683"

View File

@ -101,4 +101,5 @@ flake8 = "^7.3.0"
pytest-asyncio = "^1.3.0" pytest-asyncio = "^1.3.0"
cython = "^3.0.0" cython = "^3.0.0"
setuptools = "^80.0.0" setuptools = "^80.0.0"
types-psutil = "^7.1.3.20251202"

View File

@ -11,9 +11,11 @@ import os
import signal import signal
import sys import sys
from pathlib import Path from pathlib import Path
from types import FrameType
from typing import Optional
def main(): def main() -> None:
parser = argparse.ArgumentParser(description="PyServe Daemon") parser = argparse.ArgumentParser(description="PyServe Daemon")
parser.add_argument("--config", required=True, help="Configuration file path") parser.add_argument("--config", required=True, help="Configuration file path")
parser.add_argument("--state-dir", required=True, help="State directory path") parser.add_argument("--state-dir", required=True, help="State directory path")
@ -63,7 +65,7 @@ def main():
runner = ServiceRunner(config, state_manager) runner = ServiceRunner(config, state_manager)
def signal_handler(signum, frame): def signal_handler(signum: int, frame: Optional[FrameType]) -> None:
logger.info(f"Received signal {signum}, shutting down...") logger.info(f"Received signal {signum}, shutting down...")
runner.stop() runner.stop()

View File

@ -11,7 +11,7 @@ import yaml
@click.group("config") @click.group("config")
def config_cmd(): def config_cmd() -> None:
""" """
Configuration management commands. Configuration management commands.
@ -40,7 +40,7 @@ def config_cmd():
help="Enable strict validation (warn on unknown fields)", help="Enable strict validation (warn on unknown fields)",
) )
@click.pass_obj @click.pass_obj
def validate_cmd(ctx, config_file: Optional[str], strict: bool): def validate_cmd(ctx: Any, config_file: Optional[str], strict: bool) -> None:
""" """
Validate a configuration file. Validate a configuration file.
@ -146,7 +146,7 @@ def validate_cmd(ctx, config_file: Optional[str], strict: bool):
help="Show only a specific section (e.g., server, logging)", help="Show only a specific section (e.g., server, logging)",
) )
@click.pass_obj @click.pass_obj
def show_cmd(ctx, config_file: Optional[str], output_format: str, section: Optional[str]): def show_cmd(ctx: Any, config_file: Optional[str], output_format: str, section: Optional[str]) -> None:
""" """
Display current configuration. Display current configuration.
@ -192,7 +192,7 @@ def show_cmd(ctx, config_file: Optional[str], output_format: str, section: Optio
elif output_format == "table": elif output_format == "table":
from rich.tree import Tree from rich.tree import Tree
def build_tree(data, tree): def build_tree(data: Any, tree: Any) -> None:
if isinstance(data, dict): if isinstance(data, dict):
for key, value in data.items(): for key, value in data.items():
if isinstance(value, (dict, list)): if isinstance(value, (dict, list)):
@ -227,7 +227,7 @@ def show_cmd(ctx, config_file: Optional[str], output_format: str, section: Optio
help="Path to configuration file", help="Path to configuration file",
) )
@click.pass_obj @click.pass_obj
def get_cmd(ctx, key: str, config_file: Optional[str]): def get_cmd(ctx: Any, key: str, config_file: Optional[str]) -> None:
""" """
Get a specific configuration value. Get a specific configuration value.
@ -291,7 +291,7 @@ def get_cmd(ctx, key: str, config_file: Optional[str]):
help="Path to configuration file", help="Path to configuration file",
) )
@click.pass_obj @click.pass_obj
def set_cmd(ctx, key: str, value: str, config_file: Optional[str]): def set_cmd(ctx: Any, key: str, value: str, config_file: Optional[str]) -> None:
""" """
Set a configuration value. Set a configuration value.
@ -357,7 +357,7 @@ def set_cmd(ctx, key: str, value: str, config_file: Optional[str]):
@config_cmd.command("diff") @config_cmd.command("diff")
@click.argument("file1", type=click.Path(exists=True)) @click.argument("file1", type=click.Path(exists=True))
@click.argument("file2", type=click.Path(exists=True)) @click.argument("file2", type=click.Path(exists=True))
def diff_cmd(file1: str, file2: str): def diff_cmd(file1: str, file2: str) -> None:
""" """
Compare two configuration files. Compare two configuration files.
@ -373,8 +373,8 @@ def diff_cmd(file1: str, file2: str):
with open(file2) as f: with open(file2) as f:
data2 = yaml.safe_load(f) data2 = yaml.safe_load(f)
def compare_dicts(d1, d2, path=""): def compare_dicts(d1: Any, d2: Any, path: str = "") -> list[tuple[str, str, Any, Any]]:
differences = [] differences: list[tuple[str, str, Any, Any]] = []
all_keys = set(d1.keys() if d1 else []) | set(d2.keys() if d2 else []) all_keys = set(d1.keys() if d1 else []) | set(d2.keys() if d2 else [])

View File

@ -5,7 +5,7 @@ pyserve down - Stop all services
import signal import signal
import time import time
from pathlib import Path from pathlib import Path
from typing import cast from typing import Any, cast
import click import click
@ -31,11 +31,11 @@ import click
) )
@click.pass_obj @click.pass_obj
def down_cmd( def down_cmd(
ctx, ctx: Any,
timeout: int, timeout: int,
volumes: bool, volumes: bool,
remove_orphans: bool, remove_orphans: bool,
): ) -> None:
""" """
Stop and remove all services. Stop and remove all services.

View File

@ -5,6 +5,8 @@ pyserve health - Check health of services
import asyncio import asyncio
from pathlib import Path from pathlib import Path
from typing import Any
import click import click
@ -25,7 +27,7 @@ import click
help="Output format", help="Output format",
) )
@click.pass_obj @click.pass_obj
def health_cmd(ctx, services: tuple, timeout: int, output_format: str): def health_cmd(ctx: Any, services: tuple[str, ...], timeout: int, output_format: str) -> None:
""" """
Check health of services. Check health of services.

View File

@ -364,12 +364,12 @@ def get_template_content(template: str) -> str:
) )
@click.pass_context @click.pass_context
def init_cmd( def init_cmd(
ctx, ctx: click.Context,
template: str, template: str,
output_file: str, output_file: str,
force: bool, force: bool,
list_templates: bool, list_templates: bool,
): ) -> None:
""" """
Initialize a new pyserve project. Initialize a new pyserve project.

View File

@ -5,7 +5,7 @@ pyserve logs - View service logs
import asyncio import asyncio
import time import time
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Any, Optional
import click import click
@ -56,8 +56,8 @@ import click
) )
@click.pass_obj @click.pass_obj
def logs_cmd( def logs_cmd(
ctx, ctx: Any,
services: tuple, services: tuple[str, ...],
follow: bool, follow: bool,
tail: int, tail: int,
since: Optional[str], since: Optional[str],
@ -65,7 +65,7 @@ def logs_cmd(
timestamps: bool, timestamps: bool,
no_color: bool, no_color: bool,
filter_pattern: Optional[str], filter_pattern: Optional[str],
): ) -> None:
""" """
View service logs. View service logs.
@ -165,15 +165,15 @@ def _parse_time(time_str: str) -> Optional[float]:
def _read_logs( def _read_logs(
log_files, log_files: list[tuple[str, Path]],
service_colors, service_colors: dict[str, str],
tail: int, tail: int,
since_time: Optional[float], since_time: Optional[float],
until_time: Optional[float], until_time: Optional[float],
timestamps: bool, timestamps: bool,
no_color: bool, no_color: bool,
filter_pattern: Optional[str], filter_pattern: Optional[str],
): ) -> None:
import re import re
from ..output import console from ..output import console
@ -231,12 +231,12 @@ def _read_logs(
async def _follow_logs( async def _follow_logs(
log_files, log_files: list[tuple[str, Path]],
service_colors, service_colors: dict[str, str],
timestamps: bool, timestamps: bool,
no_color: bool, no_color: bool,
filter_pattern: Optional[str], filter_pattern: Optional[str],
): ) -> None:
from ..output import console from ..output import console
positions = {} positions = {}

View File

@ -4,6 +4,7 @@ pyserve scale - Scale services
import asyncio import asyncio
from pathlib import Path from pathlib import Path
from typing import Any
import click import click
@ -23,7 +24,7 @@ import click
help="Don't wait for services to be ready", help="Don't wait for services to be ready",
) )
@click.pass_obj @click.pass_obj
def scale_cmd(ctx, scales: tuple, timeout: int, no_wait: bool): def scale_cmd(ctx: Any, scales: tuple[str, ...], timeout: int, no_wait: bool) -> None:
""" """
Scale services to specified number of workers. Scale services to specified number of workers.
@ -66,7 +67,7 @@ def scale_cmd(ctx, scales: tuple, timeout: int, no_wait: bool):
console.print("[bold]Scaling services...[/bold]") console.print("[bold]Scaling services...[/bold]")
async def do_scale(): async def do_scale() -> None:
for service, workers in scale_map.items(): for service, workers in scale_map.items():
current = all_services[service].workers or 1 current = all_services[service].workers or 1
print_info(f"Scaling {service}: {current}{workers} workers") print_info(f"Scaling {service}: {current}{workers} workers")

View File

@ -5,6 +5,8 @@ pyserve start/stop/restart - Service management commands
import asyncio import asyncio
from pathlib import Path from pathlib import Path
from typing import Any, Dict
import click import click
@ -18,7 +20,7 @@ import click
help="Timeout in seconds for service startup", help="Timeout in seconds for service startup",
) )
@click.pass_obj @click.pass_obj
def start_cmd(ctx, services: tuple, timeout: int): def start_cmd(ctx: Any, services: tuple[str, ...], timeout: int) -> None:
""" """
Start one or more services. Start one or more services.
@ -43,8 +45,8 @@ def start_cmd(ctx, services: tuple, timeout: int):
console.print(f"[bold]Starting services: {', '.join(services)}[/bold]") console.print(f"[bold]Starting services: {', '.join(services)}[/bold]")
async def do_start(): async def do_start() -> Dict[str, bool]:
results = {} results: Dict[str, bool] = {}
for service in services: for service in services:
try: try:
success = await runner.start_service(service, timeout=timeout) success = await runner.start_service(service, timeout=timeout)
@ -83,7 +85,7 @@ def start_cmd(ctx, services: tuple, timeout: int):
help="Force stop (SIGKILL)", help="Force stop (SIGKILL)",
) )
@click.pass_obj @click.pass_obj
def stop_cmd(ctx, services: tuple, timeout: int, force: bool): def stop_cmd(ctx: Any, services: tuple[str, ...], timeout: int, force: bool) -> None:
""" """
Stop one or more services. Stop one or more services.
@ -106,8 +108,8 @@ def stop_cmd(ctx, services: tuple, timeout: int, force: bool):
console.print(f"[bold]Stopping services: {', '.join(services)}[/bold]") console.print(f"[bold]Stopping services: {', '.join(services)}[/bold]")
async def do_stop(): async def do_stop() -> Dict[str, bool]:
results = {} results: Dict[str, bool] = {}
for service in services: for service in services:
try: try:
success = await runner.stop_service(service, timeout=timeout, force=force) success = await runner.stop_service(service, timeout=timeout, force=force)
@ -140,7 +142,7 @@ def stop_cmd(ctx, services: tuple, timeout: int, force: bool):
help="Timeout in seconds for restart", help="Timeout in seconds for restart",
) )
@click.pass_obj @click.pass_obj
def restart_cmd(ctx, services: tuple, timeout: int): def restart_cmd(ctx: Any, services: tuple[str, ...], timeout: int) -> None:
""" """
Restart one or more services. Restart one or more services.
@ -165,7 +167,7 @@ def restart_cmd(ctx, services: tuple, timeout: int):
console.print(f"[bold]Restarting services: {', '.join(services)}[/bold]") console.print(f"[bold]Restarting services: {', '.join(services)}[/bold]")
async def do_restart(): async def do_restart() -> Dict[str, bool]:
results = {} results = {}
for service in services: for service in services:
try: try:

View File

@ -4,7 +4,7 @@ pyserve ps / status - Show service status
import json import json
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Any, Optional
import click import click
@ -39,13 +39,13 @@ import click
) )
@click.pass_obj @click.pass_obj
def ps_cmd( def ps_cmd(
ctx, ctx: Any,
services: tuple, services: tuple[str, ...],
show_all: bool, show_all: bool,
quiet: bool, quiet: bool,
output_format: str, output_format: str,
filter_status: Optional[str], filter_status: Optional[str],
): ) -> None:
""" """
Show status of services. Show status of services.

View File

@ -5,7 +5,7 @@ pyserve top - Live monitoring dashboard
import asyncio import asyncio
import time import time
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Any, Optional
import click import click
@ -25,7 +25,7 @@ import click
help="Disable colored output", help="Disable colored output",
) )
@click.pass_obj @click.pass_obj
def top_cmd(ctx, services: tuple, refresh_interval: float, no_color: bool): def top_cmd(ctx: Any, services: tuple[str, ...], refresh_interval: float, no_color: bool) -> None:
""" """
Live monitoring dashboard for services. Live monitoring dashboard for services.
@ -60,11 +60,11 @@ def top_cmd(ctx, services: tuple, refresh_interval: float, no_color: bool):
async def _run_dashboard( async def _run_dashboard(
state_manager, state_manager: Any,
filter_services: Optional[list], filter_services: Optional[list[str]],
refresh_interval: float, refresh_interval: float,
no_color: bool, no_color: bool,
): ) -> None:
from rich.layout import Layout from rich.layout import Layout
from rich.live import Live from rich.live import Live
from rich.panel import Panel from rich.panel import Panel
@ -81,7 +81,7 @@ async def _run_dashboard(
start_time = time.time() start_time = time.time()
def make_dashboard(): def make_dashboard() -> Any:
all_services = state_manager.get_all_services() all_services = state_manager.get_all_services()
if filter_services: if filter_services:

View File

@ -7,6 +7,7 @@ import signal
import sys import sys
import time import time
from pathlib import Path from pathlib import Path
from typing import Any
import click import click
@ -54,16 +55,16 @@ import click
) )
@click.pass_obj @click.pass_obj
def up_cmd( def up_cmd(
ctx, ctx: Any,
services: tuple, services: tuple[str, ...],
detach: bool, detach: bool,
build: bool, build: bool,
force_recreate: bool, force_recreate: bool,
scales: tuple, scales: tuple[str, ...],
timeout: int, timeout: int,
wait: bool, wait: bool,
remove_orphans: bool, remove_orphans: bool,
): ) -> None:
""" """
Start services defined in configuration. Start services defined in configuration.
@ -147,7 +148,7 @@ def up_cmd(
else: else:
console.print("[bold]Starting PyServe...[/bold]") console.print("[bold]Starting PyServe...[/bold]")
def signal_handler(signum, frame): def signal_handler(signum: int, frame: Any) -> None:
console.print("\n[yellow]Received shutdown signal...[/yellow]") console.print("\n[yellow]Received shutdown signal...[/yellow]")
runner.stop() runner.stop()
sys.exit(0) sys.exit(0)

View File

@ -7,7 +7,7 @@ Usage:
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Optional from typing import TYPE_CHECKING, Optional
import click import click
@ -28,22 +28,26 @@ from .commands import (
from .. import __version__ from .. import __version__
if TYPE_CHECKING:
from ..config import Config
from .state import StateManager
DEFAULT_CONFIG = "config.yaml" DEFAULT_CONFIG = "config.yaml"
DEFAULT_STATE_DIR = ".pyserve" DEFAULT_STATE_DIR = ".pyserve"
class Context: class Context:
def __init__(self): def __init__(self) -> None:
self.config_file: str = DEFAULT_CONFIG self.config_file: str = DEFAULT_CONFIG
self.state_dir: Path = Path(DEFAULT_STATE_DIR) self.state_dir: Path = Path(DEFAULT_STATE_DIR)
self.verbose: bool = False self.verbose: bool = False
self.debug: bool = False self.debug: bool = False
self.project: Optional[str] = None self.project: Optional[str] = None
self._config = None self._config: Optional["Config"] = None
self._state = None self._state: Optional["StateManager"] = None
@property @property
def config(self): def config(self) -> "Config":
if self._config is None: if self._config is None:
from ..config import Config from ..config import Config
@ -54,7 +58,7 @@ class Context:
return self._config return self._config
@property @property
def state(self): def state(self) -> "StateManager":
if self._state is None: if self._state is None:
from .state import StateManager from .state import StateManager
@ -96,7 +100,7 @@ pass_context = click.make_pass_decorator(Context, ensure=True)
) )
@click.version_option(version=__version__, prog_name="pyservectl") @click.version_option(version=__version__, prog_name="pyservectl")
@click.pass_context @click.pass_context
def cli(ctx, config_file: str, project: Optional[str], verbose: bool, debug: bool): def cli(ctx: click.Context, config_file: str, project: Optional[str], verbose: bool, debug: bool) -> None:
""" """
PyServeCTL - Service management CLI for PyServe. PyServeCTL - Service management CLI for PyServe.
@ -145,7 +149,7 @@ cli.add_command(scale_cmd, name="scale")
cli.add_command(ps_cmd, name="status") cli.add_command(ps_cmd, name="status")
def main(): def main() -> None:
try: try:
cli(standalone_mode=False) cli(standalone_mode=False)
except click.ClickException as e: except click.ClickException as e:

View File

@ -148,7 +148,7 @@ class StateManager:
def get_state(self) -> ProjectState: def get_state(self) -> ProjectState:
return self.load() return self.load()
def update_service(self, name: str, **kwargs) -> ServiceState: def update_service(self, name: str, **kwargs: Any) -> ServiceState:
state = self.load() state = self.load()
if name not in state.services: if name not in state.services: