Rename package installer helper to package

This commit is contained in:
jxxghp
2026-06-23 13:45:55 +08:00
parent 0c53fb86fd
commit c1cefa3f40
9 changed files with 25 additions and 17 deletions

View File

@@ -29,7 +29,7 @@ from requests import Response
from app.core.cache import cached, is_fresh
from app.core.config import settings
from app.db.systemconfig_oper import SystemConfigOper
from app.helper.package_installer import PackageInstallRequest, build_package_install_strategies
from app.helper.package import PackageInstallRequest, build_package_install_strategies
from app.log import logger
from app.schemas.types import SystemConfigKey
from app.utils.http import RequestUtils, AsyncRequestUtils

View File

@@ -28,6 +28,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
wget \
git \
gh \
busybox \
tini \
cron \

View File

@@ -31,6 +31,10 @@ The application is structured as four distinct layers. Each layer has a defined
## Layer Responsibilities and Boundaries
### Shared File Placement Rule
Before creating a new file under `app/api/endpoints/`, `app/chain/`, `app/helper/`, or `app/utils/`, first check whether the capability belongs in an existing domain file. Prefer extending that file when the domain already exists. Create a new file only for a genuinely new domain or standalone reusable concern, and name it with a single noun according to `07-naming-conventions.md`.
### Entrypoint Layer
**Directories:** `app/api/endpoints/`, `moviepilot` (CLI), `app/agent/`, scheduler callbacks, webhook handlers, message interactions.
@@ -41,7 +45,7 @@ The application is structured as four distinct layers. Each layer has a defined
- Any logic that coordinates multiple modules, triggers events, touches caches, or combines workflows must be moved into `chain`.
**Rules:**
- Prefer adding new endpoints to an existing domain file. Create a new endpoint file only when introducing a new top-level resource domain.
- Prefer adding new endpoints to an existing domain file. Create a new endpoint file only when introducing a new top-level resource domain, and use a single-noun filename.
- After adding a new endpoint, register it in `app/api/apiv1.py`.
- Endpoints must not contain business logic that belongs in `chain`.
@@ -166,4 +170,4 @@ The application is structured as four distinct layers. Each layer has a defined
| Few dozen lines of private logic in one chain or module | Private function in the same file; do not create a new helper |
| New module category or subtype | Also update `app/schemas/types.py` |
*Last Updated: 2026-05-25*
*Last Updated: 2026-06-23*

View File

@@ -105,6 +105,8 @@ except:
- One primary class per file is the norm for chains, modules, and helpers.
- Private helper functions in the same file are preferable to extracting a new helper for single-use logic.
- Under `app/api/endpoints/`, `app/chain/`, `app/helper/`, and `app/utils/`, add code to an existing domain file whenever the domain already exists.
- New files under those directories must use a single noun filename such as `package.py`; avoid role-suffix names such as `package_installer.py` unless an established framework convention requires it.
- Keep files focused on one domain concern.
---
@@ -118,4 +120,4 @@ except:
- 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-05-25*
*Last Updated: 2026-06-23*

View File

@@ -8,7 +8,8 @@ All new code must follow these conventions. Consistent naming is how the codebas
| Context | Convention | Examples |
|---|---|---|
| Python source files | `snake_case.py` | `download_chain.py`, `qbittorrent.py` |
| Python source files | `snake_case.py` | `download.py`, `qbittorrent.py`, `package.py` |
| New domain files under `app/api/endpoints/`, `app/chain/`, `app/helper/`, `app/utils/` | Single noun `snake_case.py`; prefer an existing domain file before adding a new one | `package.py`, `plugin.py`, `torrent.py` |
| Module package directories | `snake_case/` | `qbittorrent/`, `synologychat/` |
| Test files | `test_<domain>.py` | `test_download_chain.py`, `test_subscribe_endpoint.py` |
| Alembic migrations | Auto-generated by Alembic; do not rename | `20240101_add_column.py` |
@@ -99,4 +100,4 @@ All new code must follow these conventions. Consistent naming is how the codebas
| `SystemConfigOper().get("RssUrls")` | `SystemConfigOper().get(SystemConfigKey.RssUrls)` |
| `class subscribe_oper:` | `class SubscribeOper:` |
*Last Updated: 2026-05-25*
*Last Updated: 2026-06-23*

View File

@@ -8,7 +8,7 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from app.helper.package_installer import PackageInstallRequest, build_package_install_strategies
from app.helper.package import PackageInstallRequest, build_package_install_strategies
def sample(name: str, request: PackageInstallRequest) -> None:

View File

@@ -3,7 +3,7 @@ from __future__ import annotations
from pathlib import Path
from unittest.mock import patch
from app.helper.package_installer import (
from app.helper.package import (
PackageInstallRequest,
build_package_install_env,
build_package_install_strategies,
@@ -106,7 +106,7 @@ def test_build_strategies_uses_pip_only_when_uv_missing(tmp_path):
config_dir=tmp_path / "config",
)
with patch("app.helper.package_installer._find_uv", return_value=None):
with patch("app.helper.package._find_uv", return_value=None):
strategies = build_package_install_strategies(request)
assert [strategy.strategy_name for strategy in strategies] == ["pip:直连"]

View File

@@ -667,7 +667,7 @@ class TestPluginHelper:
uv_bin.parent.mkdir(parents=True)
uv_bin.write_text("", encoding="utf-8")
with patch("app.helper.package_installer._find_uv", return_value=uv_bin), \
with patch("app.helper.package._find_uv", return_value=uv_bin), \
patch.object(PluginHelper, "_PluginHelper__get_protected_runtime_packages", return_value={}), \
patch.object(PluginHelper, "_PluginHelper__run_runtime_healthcheck", return_value=(True, "")), \
patch("app.helper.plugin.SystemUtils.execute_with_subprocess", side_effect=fake_execute), \
@@ -830,7 +830,7 @@ class TestPluginHelper:
return_value={}
):
with patch("app.helper.plugin.SystemUtils.execute_with_subprocess", side_effect=fake_execute):
with patch("app.helper.package_installer._find_uv", return_value=None):
with patch("app.helper.package._find_uv", return_value=None):
success, message = PluginHelper.pip_install_with_fallback(requirements_file)
assert success
@@ -867,7 +867,7 @@ class TestPluginHelper:
return_value={"fastapi": Version("0.115.14")}
):
with patch("app.helper.plugin.SystemUtils.execute_with_subprocess", side_effect=fake_execute):
with patch("app.helper.package_installer._find_uv", return_value=None):
with patch("app.helper.package._find_uv", return_value=None):
success, message = PluginHelper.pip_install_with_fallback(requirements_file)
assert success
@@ -913,7 +913,7 @@ class TestPluginHelper:
return_value={"fastapi": Version("0.115.14")}
):
with patch("app.helper.plugin.SystemUtils.execute_with_subprocess", side_effect=fake_execute):
with patch("app.helper.package_installer._find_uv", return_value=None):
with patch("app.helper.package._find_uv", return_value=None):
success, message = PluginHelper.pip_install_with_fallback(requirements_file)
assert not success
@@ -942,7 +942,7 @@ class TestPluginHelper:
req = root / "plugin-requirements.txt"
req.write_text("demo\n", encoding="utf-8")
with patch("app.helper.package_installer._find_uv", return_value=None), \
with patch("app.helper.package._find_uv", return_value=None), \
patch.object(PluginHelper, "_PluginHelper__get_protected_runtime_packages", return_value={}), \
patch.object(
PluginHelper,
@@ -990,7 +990,7 @@ class TestPluginHelper:
uv_bin.parent.mkdir(parents=True)
uv_bin.write_text("", encoding="utf-8")
with patch("app.helper.package_installer._find_uv", return_value=uv_bin), \
with patch("app.helper.package._find_uv", return_value=uv_bin), \
patch.object(PluginHelper, "_PluginHelper__get_protected_runtime_packages", return_value={}), \
patch.object(
PluginHelper,
@@ -1014,7 +1014,7 @@ class TestPluginHelper:
assert len(seen_install_commands) == 1
assert repair_calls
def test_repair_main_runtime_dependencies_uses_package_installer_semantics(self):
def test_repair_main_runtime_dependencies_uses_package_helper_semantics(self):
"""
主运行环境恢复与插件安装使用同一套 cache、index、proxy 和安全日志语义。
"""
@@ -1037,7 +1037,7 @@ class TestPluginHelper:
uv_bin.parent.mkdir(parents=True)
uv_bin.write_text("", encoding="utf-8")
with patch("app.helper.package_installer._find_uv", return_value=uv_bin), \
with patch("app.helper.package._find_uv", return_value=uv_bin), \
patch("app.helper.plugin.settings.CONFIG_DIR", str(root / "config")), \
patch("app.helper.plugin.settings.PACKAGE_CACHE_ROOT", str(root / "custom-package-cache")), \
patch("app.helper.plugin.settings.PIP_PROXY", "https://user:pass@mirror.example/simple"), \