From 9a9338cfdcf9245ea298634372827204f0c7bf0d Mon Sep 17 00:00:00 2001 From: shmyga Date: Fri, 31 Jul 2026 15:52:16 +0300 Subject: [PATCH] refactor: update project code --- .pylintrc | 1 + project_manager/__init__.py | 1 - project_manager/command.py | 6 +++--- project_manager/entry.py | 18 +++++++++--------- project_manager/manager.py | 12 ++++++------ project_manager/pipe/core.py | 17 +++++++++-------- project_manager/pipe/type/file.py | 9 +++++---- project_manager/pipe/type/get.py | 8 ++++---- project_manager/pipe/type/search.py | 4 ++-- project_manager/preset/entry.py | 12 ++++++------ project_manager/preset/resolve.py | 9 ++++----- pyproject.toml | 3 +++ tests/__init__.py | 1 + tests/python/pyproject.toml | 27 ++++++++++++++++++--------- tests/python/pyproject.toml.old | 17 +++++++++++++++++ tests/test_entry.py | 5 ++--- 16 files changed, 90 insertions(+), 60 deletions(-) create mode 100644 tests/python/pyproject.toml.old diff --git a/.pylintrc b/.pylintrc index 1fca246..0d53fc3 100644 --- a/.pylintrc +++ b/.pylintrc @@ -7,6 +7,7 @@ disable= too-few-public-methods, relative-beyond-top-level, subprocess-run-check, + bare-except, fixme [FORMAT] diff --git a/project_manager/__init__.py b/project_manager/__init__.py index 2cbce37..e69de29 100644 --- a/project_manager/__init__.py +++ b/project_manager/__init__.py @@ -1 +0,0 @@ -from .common.logging_custom import * diff --git a/project_manager/command.py b/project_manager/command.py index b8bd16e..d153f64 100644 --- a/project_manager/command.py +++ b/project_manager/command.py @@ -47,7 +47,7 @@ class CommandLogger(threading.Thread): class CommandExecutor: _logger = logger - def __init__(self, command: str) -> None: + def __init__(self, command: str): self._command = command def run(self, basepath: Path): @@ -58,8 +58,8 @@ class CommandExecutor: result = subprocess.run( self._command.split(" "), cwd=basepath, - stdout=out_logger, - stderr=err_logger, + stdout=out_logger, # type: ignore + stderr=err_logger, # type: ignore ) finally: out_logger.close() diff --git a/project_manager/entry.py b/project_manager/entry.py index 7ef9ea5..082141d 100644 --- a/project_manager/entry.py +++ b/project_manager/entry.py @@ -1,6 +1,6 @@ import logging from pathlib import Path -from typing import Dict, Iterable, List, Optional +from typing import Iterable from .command import CommandExecutor @@ -23,18 +23,18 @@ class Entry: def __init__( self, - tasks: Dict[str, List[str]], + tasks: dict[str, list[str]], basepath: Path, ): self._tasks = tasks self._basepath = basepath def show(self): - self._logger.info(f"{self}") + self._logger.info("%s", self) for task_name, commands in self._tasks.items(): - self._logger.info(f" {task_name}") + self._logger.info(" %s", task_name) for command in commands: - self._logger.debug(f" {command}") + self._logger.debug(" %s", command) def run_task(self, task_name: str): if task_name == "show": @@ -42,7 +42,7 @@ class Entry: return if task_name not in self._tasks: raise EntryTaskNotImplementedError(task_name) - self._logger.info(f"{task_name} {self}") + self._logger.info("%s %s", task_name, self) for command in self._tasks[task_name]: CommandExecutor(command).run(self._basepath) @@ -51,12 +51,12 @@ class Entry: class EntryDetector: - def detect_entry(self, basepath: Path) -> Optional["Entry"]: - raise NotImplemented + def detect_entry(self, basepath: Path) -> Entry | None: + raise NotImplementedError class EntryResolver: - def __init__(self, detector: EntryDetector, ignore: List[str]): + def __init__(self, detector: EntryDetector, ignore: list[str]): self._detector = detector self._ignore = ignore diff --git a/project_manager/manager.py b/project_manager/manager.py index 0db2138..e395ba3 100644 --- a/project_manager/manager.py +++ b/project_manager/manager.py @@ -1,7 +1,7 @@ import logging from collections import Counter from pathlib import Path -from typing import Dict, Iterable +from typing import Iterable from .command import CommandError from .entry import Entry, EntryResolver, EntryTaskNotImplementedError @@ -26,20 +26,20 @@ class ProjectManager: def _iter_entries(self) -> Iterable[Entry]: return self._resolver.resolve(self._basepaht) - def run_task(self, task_name: str) -> Dict[str, int]: + def run_task(self, task_name: str) -> dict[str, int]: counter = Counter() for entry in self._iter_entries(): try: entry.run_task(task_name) counter["success"] += 1 except EntryTaskNotImplementedError: - self._logger.warn(f"{task_name} {entry}") + self._logger.warning("%s %s", task_name, entry) counter["skipped"] += 1 except CommandError as error: - self._logger.error(f"{entry}\n{error.output}") + self._logger.error("%s\n%s", entry, error.output) counter["failed"] += 1 - except Exception: + except: self._logger.exception(entry) counter["failed"] += 1 - self._logger.info(f"result {dict(counter)}") + self._logger.info("result %s", dict(counter)) return counter diff --git a/project_manager/pipe/core.py b/project_manager/pipe/core.py index 8454cf5..483b23e 100644 --- a/project_manager/pipe/core.py +++ b/project_manager/pipe/core.py @@ -1,7 +1,7 @@ import importlib import re from pathlib import Path -from typing import Any, Generic, List, Optional, Type, TypeVar +from typing import Any, Generic, TypeVar __all__ = ("Pipe", "PipeRegistry", "PipeParser", "PipeRunner", "PIPE_REGISTRY") @@ -12,7 +12,7 @@ O = TypeVar("O") class Pipe(Generic[I, O]): type: str - def apply(self, value: I, basepath: Path) -> Optional[O]: + def apply(self, value: I, basepath: Path) -> O | None: raise NotImplementedError @@ -20,12 +20,13 @@ class PipeRegistry: def __init__(self): self._pipes = {} - def register(self, pipe_type: Type[Pipe]): + def register(self, pipe_type: type[Pipe]): self._pipes[pipe_type.type] = pipe_type def register_module(self, module_name: str): module = importlib.import_module(module_name) modulepath = Path(module.__file__).parent + # pylint:disable=too-many-nested-blocks for item in modulepath.iterdir(): if item.suffix == ".py" and not item.name.startswith("__"): sub_module = importlib.import_module(f"{module_name}.{item.stem}") @@ -35,14 +36,14 @@ class PipeRegistry: if baseclass == Pipe: self.register(v) - def get(self, pipe_type: str) -> Type[Pipe]: + def get(self, pipe_type: str) -> type[Pipe]: return self._pipes[pipe_type] class PipeParser: _PIPE_PATTERN = re.compile("^(\\w+)\\((.*)\\)") - def parse(self, pipeline: str) -> List[Pipe]: + def parse(self, pipeline: str) -> list[Pipe]: chain = [item.strip() for item in pipeline.split("|")] result = [] for part in chain: @@ -59,11 +60,11 @@ class PipeParser: class PipeRunner: - def __init__(self, basepath: Path, parser: Optional[PipeParser] = None): + def __init__(self, basepath: Path, parser: PipeParser | None = None): self._basepath = basepath self._parser = parser or PipeParser() - def run(self, pipes: List[Pipe]) -> Any: + def run(self, pipes: list[Pipe]) -> Any: result = None for pipe in pipes: result = pipe.apply(result, self._basepath) @@ -75,7 +76,7 @@ class PipeRunner: pipe = self._parser.parse(pipeline) return self.run(pipe) - def check_conditions(self, conditions: Optional[List[str]]) -> bool: + def check_conditions(self, conditions: list[str] | None) -> bool: if conditions: for condition in conditions: if not self.resolve_value(condition): diff --git a/project_manager/pipe/type/file.py b/project_manager/pipe/type/file.py index 49f6456..768ddc4 100644 --- a/project_manager/pipe/type/file.py +++ b/project_manager/pipe/type/file.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Any, Optional +from typing import Any from ...util import reflect from ..core import Pipe @@ -16,7 +16,7 @@ class FileData: ".json": "json:load", } - def __init__(self, filepath: Path, loader: Optional[str] = None): + def __init__(self, filepath: Path, loader: str | None = None): self._filepath = filepath self._loader = loader self._data = None @@ -38,6 +38,7 @@ class FileData: self._load_data() if self._data: return self._data.get(key) + return None def __bool__(self): return self._filepath.exists() @@ -46,11 +47,11 @@ class FileData: class FilePipe(Pipe[None, Any]): type = "file" - def __init__(self, filename: Optional[str], loader: Optional[str] = None): + def __init__(self, filename: str | None, loader: str | None = None): self._filename = filename self._loader = loader - def apply(self, value: Optional[str], basepath: Path) -> Optional[Any]: + def apply(self, value: str | None, basepath: Path) -> Any | None: filepath = Path(value or self._filename) # TODO: raise none filepath exception if not filepath.is_absolute(): filepath = basepath / filepath diff --git a/project_manager/pipe/type/get.py b/project_manager/pipe/type/get.py index ff2f45c..3a4899b 100644 --- a/project_manager/pipe/type/get.py +++ b/project_manager/pipe/type/get.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Any, Optional +from typing import Any from ..core import Pipe @@ -12,7 +12,7 @@ class GetPipe(Pipe[dict, Any]): def __init__(self, keypath: str): self._keypath = keypath - def apply(self, value: dict, basepath: Path) -> Optional[Any]: + def apply(self, value: dict, basepath: Path) -> Any | None: path = self._keypath.split(".") result = value.get(path[0]) for key in path[1:]: @@ -24,9 +24,9 @@ class GetPipe(Pipe[dict, Any]): class KeyPipe(Pipe[dict, Any]): type = "key" - def __init__(self, keyindex: Optional[str]): + def __init__(self, keyindex: str | None): self._keyindex = int(keyindex) if keyindex else 0 - def apply(self, value: dict, basepath: Path) -> Optional[Any]: + def apply(self, value: dict, basepath: Path) -> Any | None: keys = list(value.keys()) return keys[self._keyindex] diff --git a/project_manager/pipe/type/search.py b/project_manager/pipe/type/search.py index 756e646..b005fe9 100644 --- a/project_manager/pipe/type/search.py +++ b/project_manager/pipe/type/search.py @@ -1,6 +1,5 @@ import re from pathlib import Path -from typing import Optional from ..core import Pipe @@ -14,7 +13,8 @@ class SearchPipe(Pipe[str, str]): self._patter = patter self._position = int(position) - def apply(self, value: str, basepath: Path) -> Optional[str]: + def apply(self, value: str, basepath: Path) -> str | None: matched = re.compile(self._patter).search(value) if matched: return matched.group(self._position) + return None diff --git a/project_manager/preset/entry.py b/project_manager/preset/entry.py index 1ce32ed..ee5253c 100644 --- a/project_manager/preset/entry.py +++ b/project_manager/preset/entry.py @@ -1,6 +1,5 @@ from collections import defaultdict from pathlib import Path -from typing import Optional from ..entry import Entry, EntryDetector from ..pipe.core import PipeParser, PipeRunner @@ -12,21 +11,21 @@ __all__ = ("PresetEntryDetector",) class PresetEntryDetector(EntryDetector): _PIPE_PARSER = PipeParser() - def __init__(self, preset_resolver_config: Optional[PresetResolverConfig] = None): + def __init__(self, preset_resolver_config: PresetResolverConfig | None = None): self._pipeparser = PipeParser() self._preset_resolver_config = preset_resolver_config - def detect_entry(self, basepath: Path) -> Optional["Entry"]: + def detect_entry(self, basepath: Path) -> Entry | None: preset = PresetResolver(basepath, self._preset_resolver_config).resolve_preset() if preset.skip: - return + return None piperunner = PipeRunner(basepath) tasks = defaultdict(list) - for preset_name, preset in preset.presets.items(): + for preset in preset.presets.values(): if not piperunner.check_conditions(preset.conditions): continue for task_name, task_pack in preset.tasks.items(): - for task_item_name, task in task_pack.items(): + for task in task_pack.values(): if piperunner.check_conditions(task.conditions): variables = { "entry_path": basepath, @@ -43,3 +42,4 @@ class PresetEntryDetector(EntryDetector): ] if tasks: return Entry(tasks, basepath) + return None diff --git a/project_manager/preset/resolve.py b/project_manager/preset/resolve.py index 740e004..0c3de51 100644 --- a/project_manager/preset/resolve.py +++ b/project_manager/preset/resolve.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from pathlib import Path -from typing import Dict, Iterable, Optional +from typing import Iterable import tomli @@ -21,16 +21,15 @@ class PresetResolverConfig: class PresetResolver: - _source_cache: Dict[Path, dict] = {} + _source_cache: dict[Path, dict] = {} - def __init__(self, basepath: Path, config: Optional[PresetResolverConfig] = None): + def __init__(self, basepath: Path, config: PresetResolverConfig | None = None): self._basepath = basepath self._config = config or PresetResolverConfig() def iter_sources(self) -> Iterable[Path]: if self._config.default: - for filepath in (Path(__file__).parent / "default").iterdir(): - yield filepath + yield from (Path(__file__).parent / "default").iterdir() if self._config.user: user_preset = Path.home() / ".preset.toml" if user_preset.exists(): diff --git a/pyproject.toml b/pyproject.toml index 1b0cd0a..6d2a6ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,3 +29,6 @@ requires-poetry = ">=2.0.0,<3.0.0" [build-system] requires = ["poetry-core (>=2.0.0,<3.0.0)"] build-backend = "poetry.core.masonry.api" + +[tool.black] +target-version = ["py314"] diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..340b29e 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ +import project_manager.common.logging_custom diff --git a/tests/python/pyproject.toml b/tests/python/pyproject.toml index f99b240..c264a7e 100644 --- a/tests/python/pyproject.toml +++ b/tests/python/pyproject.toml @@ -1,17 +1,26 @@ -[tool.poetry] +[project] name = "example" -version = "0.1.0" description = "" +version = "0.1.0" authors = [] +requires-python = ">=3.14,<4.0" +dependencies = [] -[tool.poetry.dependencies] -python = "^3.8" +[dependency-groups] +dev = [ + "black (>=26.5.1,<27.0.0)", + "isort (>=8.0.1,<9.0.0)", + "pylint (>=4.0.6,<5.0.0)", +] +test = ["pytest (>=9.1.1,<10.0.0)"] -[tool.poetry.group.dev.dependencies] -black = "^23.1.0" -pytest = "^7.2.2" -isort = "^5.12.0" +[tool.poetry] +package-mode = false +requires-poetry = ">=2.0.0,<3.0.0" [build-system] -requires = ["poetry-core"] +requires = ["poetry-core (>=2.0.0,<3.0.0)"] build-backend = "poetry.core.masonry.api" + +[tool.black] +target-version = ["py314"] diff --git a/tests/python/pyproject.toml.old b/tests/python/pyproject.toml.old new file mode 100644 index 0000000..f99b240 --- /dev/null +++ b/tests/python/pyproject.toml.old @@ -0,0 +1,17 @@ +[tool.poetry] +name = "example" +version = "0.1.0" +description = "" +authors = [] + +[tool.poetry.dependencies] +python = "^3.8" + +[tool.poetry.group.dev.dependencies] +black = "^23.1.0" +pytest = "^7.2.2" +isort = "^5.12.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/test_entry.py b/tests/test_entry.py index 74ed167..73f882e 100644 --- a/tests/test_entry.py +++ b/tests/test_entry.py @@ -1,7 +1,6 @@ # pylint:disable=redefined-outer-name,protected-access import shutil from pathlib import Path -from typing import Optional import pytest @@ -30,7 +29,7 @@ def entry(entry_detector: EntryDetector, entry_path: Path, tmp_path: Path): @pytest.mark.parametrize("entry_path", [Path(__file__).parent / "python"]) -def test_python_entry(entry: Optional[Entry], entry_detector: EntryDetector): +def test_python_entry(entry: Entry | None, entry_detector: EntryDetector): assert entry is not None assert {"setup", "build"} == entry._tasks.keys() entry.run_task("setup") @@ -40,7 +39,7 @@ def test_python_entry(entry: Optional[Entry], entry_detector: EntryDetector): @pytest.mark.parametrize("entry_path", [Path(__file__).parent / "nodejs"]) -def test_nodejs_entry(entry: Optional[Entry], entry_detector: EntryDetector): +def test_nodejs_entry(entry: Entry | None, entry_detector: EntryDetector): assert entry is not None assert {"setup", "build"} == entry._tasks.keys() entry.run_task("setup")