From df6bc52bb0d2b1f81b7997b055924455b857152c Mon Sep 17 00:00:00 2001 From: Lucien Shui Date: Thu, 1 Oct 2026 20:11:16 +0800 Subject: [PATCH 1/4] feat(common): Make python-common installable --- _project_root.py | 25 +++++++++++++++++++++---- pyproject.toml | 28 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 pyproject.toml diff --git a/_project_root.py b/_project_root.py index ea5ba87..b6f55c1 100644 --- a/_project_root.py +++ b/_project_root.py @@ -1,14 +1,31 @@ +import inspect import os import pathlib from typing import TextIO, Optional +def _find_project_root(path: pathlib.Path) -> pathlib.Path | None: + path = path.resolve() + while path != path.parent: + if "site-packages" not in path.parts and (path / "pyproject.toml").is_file(): + return path + path = path.parent + return None + + class ProjectRoot: def __init__(self): - self.target = "pyproject.toml" - self.project_root = pathlib.Path(__file__).parent - while self.target not in os.listdir(str(self.project_root)): - self.project_root = self.project_root.parent + package_root = pathlib.Path(__file__).parent + if project_root := _find_project_root(package_root): + self.project_root = project_root + return + for frame in inspect.stack()[1:]: + caller = pathlib.Path(frame.filename) + if "site-packages" in caller.parts: + continue + self.project_root = _find_project_root(caller.parent) or caller.parent + return + self.project_root = package_root def path(self, path: Optional[str] = None) -> str: return os.path.join(self.project_root, path or "") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3bd3f82 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,28 @@ +[build-system] +requires = ["setuptools>=61"] +build-backend = "setuptools.build_meta" + +[project] +name = "python-common" +version = "0.1.0" +description = "Shared Python utilities for OmniBox services" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "chardet>=5.2.0", + "fastapi>=0.116.1", + "jinja2>=3.1.6", + "opentelemetry-api>=1.36.0", + "opentelemetry-exporter-otlp>=1.36.0", + "opentelemetry-instrumentation-fastapi>=0.57b0", + "opentelemetry-sdk>=1.36.0", + "pydantic>=2.11.7", + "pyyaml>=6.0.2", + "shortuuid>=1.0.13", +] + +[tool.setuptools] +packages = ["common"] + +[tool.setuptools.package-dir] +common = "." From 82ab7cdb619220c4c7ab0ddbdd9993a746d08988 Mon Sep 17 00:00:00 2001 From: Lucien Shui Date: Thu, 1 Oct 2026 21:07:23 +0800 Subject: [PATCH 2/4] chore(common): Add Ruff pre-commit and CI checks --- .github/workflows/ci.yml | 13 +++++++++++++ .pre-commit-config.yaml | 5 +++++ pyproject.toml | 12 ++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b7a3854 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,13 @@ +name: CI + +on: + push: + pull_request: + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v6 + - run: uvx --from ruff==0.16.9 ruff check . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..2de4589 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.16.9 + hooks: + - id: ruff-check diff --git a/pyproject.toml b/pyproject.toml index 3bd3f82..e1d7870 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,3 +26,15 @@ packages = ["common"] [tool.setuptools.package-dir] common = "." + +[dependency-groups] +dev = [ + "pre-commit>=4.5.0,<5.0.0", + "ruff>=0.16.9,<0.17.0", +] + +[tool.ruff] +target-version = "py312" + +[tool.ruff.lint] +select = ["E4", "E7", "E9", "F"] From a11bb52be6c1e8b8a0d7961adcfadbbe1cd96c13 Mon Sep 17 00:00:00 2001 From: Lucien Shui Date: Thu, 1 Oct 2026 21:12:09 +0800 Subject: [PATCH 3/4] style(common): Format code with Ruff --- .github/workflows/ci.yml | 1 + .pre-commit-config.yaml | 1 + __init__.py | 2 +- _project_root.py | 2 +- app_factory.py | 7 +++---- config_loader.py | 15 +++++++-------- pyproject.toml | 2 +- template_parser.py | 3 +-- trace_info.py | 1 - tracing.py | 2 +- utils.py | 2 +- 11 files changed, 18 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7a3854..ffd4c10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,3 +11,4 @@ jobs: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v6 - run: uvx --from ruff==0.16.9 ruff check . + - run: uvx --from ruff==0.16.9 ruff format --check . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2de4589..781f638 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,3 +3,4 @@ repos: rev: v0.16.9 hooks: - id: ruff-check + - id: ruff-format diff --git a/__init__.py b/__init__.py index 4af3e2d..31fe4a0 100644 --- a/__init__.py +++ b/__init__.py @@ -1,2 +1,2 @@ -from ._project_root import project_root as project_root from ._env import env as env +from ._project_root import project_root as project_root diff --git a/_project_root.py b/_project_root.py index b6f55c1..72cb2de 100644 --- a/_project_root.py +++ b/_project_root.py @@ -1,7 +1,7 @@ import inspect import os import pathlib -from typing import TextIO, Optional +from typing import Optional, TextIO def _find_project_root(path: pathlib.Path) -> pathlib.Path | None: diff --git a/app_factory.py b/app_factory.py index 06d15b9..3cf2e73 100644 --- a/app_factory.py +++ b/app_factory.py @@ -1,15 +1,14 @@ import os.path import tomllib from contextlib import asynccontextmanager -from typing import Callable, Awaitable +from typing import Awaitable, Callable +from common import project_root +from common.exception import CommonException from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse, Response -from common import project_root -from common.exception import CommonException - async def exception_handler(_: Request, e: Exception) -> Response: if isinstance(e, CommonException): diff --git a/config_loader.py b/config_loader.py index d786254..349156a 100644 --- a/config_loader.py +++ b/config_loader.py @@ -4,23 +4,22 @@ from types import UnionType from typing import ( Dict, - Type, - TypeVar, - Optional, - List, Generic, + List, Literal, + Optional, Tuple, - get_origin, - get_args, + Type, + TypeVar, Union, + get_args, + get_origin, ) +from common.logger import get_logger from pydantic import BaseModel from pydantic.fields import FieldInfo # noqa -from common.logger import get_logger - try: import yaml except ImportError: diff --git a/pyproject.toml b/pyproject.toml index e1d7870..b18754b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,4 +37,4 @@ dev = [ target-version = "py312" [tool.ruff.lint] -select = ["E4", "E7", "E9", "F"] +select = ["E4", "E7", "E9", "F", "I"] diff --git a/template_parser.py b/template_parser.py index 434cf62..02417fa 100644 --- a/template_parser.py +++ b/template_parser.py @@ -1,9 +1,8 @@ import re from datetime import datetime -from jinja2 import Environment, FileSystemLoader, Template - from common import project_root +from jinja2 import Environment, FileSystemLoader, Template continuous_bl: re.Pattern = re.compile(r"\n\n+") template_dir = project_root.path("wizard_common/resources/prompt_templates") diff --git a/trace_info.py b/trace_info.py index fcdb483..8c70f45 100644 --- a/trace_info.py +++ b/trace_info.py @@ -2,7 +2,6 @@ from typing import Optional import shortuuid - from common.logger import get_logger diff --git a/tracing.py b/tracing.py index fb1b6c3..5c499be 100644 --- a/tracing.py +++ b/tracing.py @@ -4,7 +4,7 @@ from opentelemetry import trace from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor -from opentelemetry.sdk.resources import Resource, SERVICE_NAME, DEPLOYMENT_ENVIRONMENT +from opentelemetry.sdk.resources import DEPLOYMENT_ENVIRONMENT, SERVICE_NAME, Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor diff --git a/utils.py b/utils.py index 7ab9f3d..c1757ec 100644 --- a/utils.py +++ b/utils.py @@ -1,5 +1,5 @@ -import re import json as jsonlib +import re from functools import partial json_dumps = partial(jsonlib.dumps, ensure_ascii=False, separators=(",", ":")) From 0da89d2225b9493011ce419bb2311c28522573ff Mon Sep 17 00:00:00 2001 From: Lucien Shui Date: Thu, 1 Oct 2026 21:20:39 +0800 Subject: [PATCH 4/4] ci(common): Run checks only for pull requests --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ffd4c10..fdcdfcb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,6 @@ name: CI on: - push: pull_request: jobs: