mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-10-29 19:37:54 +08:00
6.7 KiB
6.7 KiB
06 — Code Standards and Style
General Principles
- Preserve the style of the surrounding file. When in doubt, read neighboring code first.
- Prefer the smallest correct change. Do not introduce a new abstraction layer without a clear payoff.
- Do not add features, refactors, or abstractions beyond what the task requires.
- Do not add error handling or validation for scenarios that cannot happen. Trust internal code and framework guarantees; only validate at system boundaries (user input, external API responses).
Python Version and Typing
- Target: Python 3.14+. Python 3.14 is the primary CI version; dependency CI also verifies supported platforms and both Linux runtime profiles.
- Type annotations are required on all public methods and function signatures.
- Use
Optional[X]for nullable types (do not useX | None— keep consistency with the existing codebase style). - Use
Union[X, Y]for multi-type parameters. - Prefer
list[X],dict[K, V],tuple[X, Y]built-in generics in new code (Python 3.9+); match the style of the surrounding file. - Use
pathlib.Pathfor all file path operations. Never use raw string concatenation for paths.
Pydantic Models
- All request body and response models must be defined as Pydantic
BaseModelsubclasses inapp/schemas/. - Use
Field(...)for required fields; useField(default=...)orField(None)for optional fields. - Do not define ad-hoc
dictreturn types for API responses — define a schema class. - Settings and deployment configuration live in
ConfigModel/Settingsinapp/runtime/config.pyusingpydantic-settings. - Use
model_validatorfor cross-field validation logic.
Async and Concurrency
- Prefer
async deffor I/O-bound operations (network requests, database queries, file operations). - Use
awaitconsistently; do not mix sync and async code paths in the same function without usingrun_in_threadpoolfrom FastAPI orasyncio.to_thread. - For CPU-bound work that must not block the event loop, submit to
ThreadHelper(seeapp/runtime/thread.py). - Use
ThreadHelper.submit()for finite background work. A long-lived protocol loop may use a dedicatedthreading.Threadonly when its existing lifecycle owner signals and joins that child, while the external caller bounded-waits and retains the parent owner on non-convergence; do not create a dedicated thread pool for that exception.
Imports
Order imports as follows, separated by blank lines:
- Standard library (
import os,import json, etc.) - Third-party packages (
from fastapi import ...,from pydantic import ...) - Local application packages (
from app.chain import ...,from app.schemas import ...)
Within each group, sort alphabetically. Do not use wildcard imports (from module import *) in application code.
String Formatting
- Use f-strings for all string interpolation. Do not use
%formatting or.format(). - For log messages, use
logger.info(f"...")— do not use lazy%sformat in logger calls (the project does not rely on lazy evaluation here).
Error Handling
- In chain and module layers: do not raise HTTP exceptions. Catch exceptions, log them, and return
Noneor a domain-level error object so the caller can decide how to proceed. - In endpoint layer: use FastAPI's
HTTPExceptionor the project's standard response schemas for errors. - Application and adapter layers must not swallow operational failures silently. Log or re-raise them according to the owning contract. Foundation primitives do not log; they return their documented fallback value or raise, leaving operational reporting to the caller.
- Do not use bare
except:— always catch a specific exception type or at minimumException. logger.exception(...)records at ERROR level and includes the current traceback in console output by default. File and resource logs receive the formatted ERROR message through the usual routing. An explicitexc_infovalue follows standard-library logging semantics.
# Correct
try:
result = self.do_work()
except Exception as err:
logger.error(f"Failed to do work: {str(err)}")
return None
# Wrong — swallowing silently
try:
result = self.do_work()
except:
pass
Logging
- Host code uses
loggerfromapp.runtime.log; new plugins useapp.sdk.logging. The historicalapp.logpath is compatibility-only. Do not import the standard libraryloggingdirectly in application code. - Log levels:
logger.debug(...)— detailed diagnostic information, disabled by default.logger.info(...)— normal operational events.logger.warning(...)— unexpected but recoverable situations.logger.error(...)— failures that affect functionality.
- Keep log messages in Chinese unless the surrounding file consistently uses English.
Constants and Magic Values
- Do not scatter raw string keys for
SystemConfig. Add aSystemConfigKeyenum entry and reference it. - Do not use magic numbers or magic strings inline. Define a named constant or enum value.
File Organization
- One primary class per file is the norm for chains, modules, services, and adapters.
- Private functions in the same file are preferable to extracting a new module for single-use logic.
- Add code to the canonical capability package that owns it, and extend an existing domain file whenever that domain already exists.
- Do not recreate generic
core,helper, orutilsbuckets; see05-architecture.mdfor placement rules. - New files use one focused lowercase noun. When the capability already names the package, put the role in a single-word child such as
plugin/manager.py; do not flatten it back intoplugin_manager.py. - Keep files focused on one domain concern.
What Not To Do
- Do not introduce new third-party libraries without placing them in the correct
pyproject.tomldependency group and updatinguv.lock: runtime packages belong in[project].dependencies, test/lint/build tooling in[dependency-groups].dev. - Do not use
requestsorhttpxdirectly for external HTTP calls - host code usesRequestUtilsfromapp/adapters/network/http.py; plugins useapp.sdk.network. - Do not issue raw SQLAlchemy queries or import Oper classes from chains, modules,
or endpoints. Define/consume an Application persistence Port; its concrete
implementation under
app/db/adapters/may use Oper classes fromapp/db/oper/. - Do not add TODO or FIXME without context. Only keep one if it is genuinely deferred and cannot be addressed in the current task.
- Do not add noisy markers like
# change starts here,# important, or# this is a fix. - Do not write comments that restate what the code already clearly says.
Last Updated: 2026-08-19