0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

uv 0.12でmain.py前提CIを検出するPython

0
Posted at

uv 0.12 へ上げるとき、依存解決より先に CI を見たほうがいい。uv init の既定がパッケージ構成へ戻ったためだ。以前の main.py 前提を残すと、ローカルでは気付かず、CI だけが古い入口を実行する。

7月28日公開の uv 0.12.0 では、uv init examplesrc/exampleuv_build を持つパッケージを既定で作る。さらに uv run example 用の project.scripts も入る。既存プロジェクトは勝手に書き換わらないが、テンプレートを更新したリポジトリや、新旧の手順が混ざった monorepo は要注意だ。

自分はこういう移行で、README より先に .github/workflows を開く。uv run main.py が一行残っているだけで、実行対象が古いままになるからだ。今回も疑似プロジェクトを二つ作って検査し、古い入口を含む方だけを失敗にできることを確認した。

uv 0.12 の uv init で何が変わるか

0.11 系までの既定では、main.py を置いた「パッケージ化しない」構成が作られていた。0.12 では次のような構成が既定になる。

観点 以前の既定 uv 0.12 の既定
実装の置き場 main.py src/<package>/
build system なし uv_build
実行入口 uv run main.py uv run <project.scripts の名前>
テストからの import プロジェクトを自動では install しない パッケージとして import できる

既存のスクリプト型プロジェクトを保ちたいなら、uv init --no-package を選べばよい。問題になるのは、パッケージ構成なのに実行コマンドだけ main.py のまま残るケースだ。uv run main.py は「パッケージの console script を実行する」意味ではない。ファイルを直接指定している。

CI を直す前に、まず旧入口を参照している場所だけを洗い出す。

uv 0.12のパッケージ構成と、main.pyを参照するCIを検出する流れ図

main.py 前提を止める小さな検査

次のスクリプトは、uv_build を使い、かつ src/<正規化したプロジェクト名>/__init__.py があるリポジトリだけを対象にする。GitHub Actions、Makefile、Dockerfile、scripts/ の中にある python main.py または uv run main.py を見つけたら終了コード 1 を返す。Python 3.11 以降で動く。

#!/usr/bin/env python3
from __future__ import annotations

import argparse
from pathlib import Path
import re
import sys
import tomllib

def find_old_entrypoints(root: Path) -> list[str]:
    pyproject = root / "pyproject.toml"
    if not pyproject.is_file():
        return []

    config = tomllib.loads(pyproject.read_text(encoding="utf-8"))
    name = config.get("project", {}).get("name")
    backend = config.get("build-system", {}).get("build-backend", "")
    if not name or "uv_build" not in backend:
        return []

    package = re.sub(r"[-.]", "_", name)
    if not (root / "src" / package / "__init__.py").is_file():
        return []

    candidates = [root / "Makefile", root / "Dockerfile"]
    candidates += list((root / ".github" / "workflows").glob("*.y*ml"))
    if (root / "scripts").is_dir():
        candidates += list((root / "scripts").glob("*"))

    old_entry = re.compile(
        r"(?<![\w./-])(?:python(?:3)?|uv\s+run)\s+(?:\./)?main\.py\b"
    )
    hits: list[str] = []
    for path in candidates:
        if not path.is_file():
            continue
        for line_no, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
            if old_entry.search(line):
                hits.append(f"{path.relative_to(root)}:{line_no}: {line.strip()}")
    return hits

def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("root", nargs="?", type=Path, default=Path("."))
    args = parser.parse_args()

    hits = find_old_entrypoints(args.root.resolve())
    if not hits:
        return

    print("uv 0.12 packaged project still runs main.py:", file=sys.stderr)
    print(*hits, sep="\n", file=sys.stderr)
    raise SystemExit(1)

if __name__ == "__main__":
    main()

たとえば workflow に次がある場合、出力はこうなる。

- run: uv run main.py
uv 0.12 packaged project still runs main.py:
.github/workflows/test.yml:2: - run: uv run main.py

同じ構成で uv run demo-app だけを置いた fixture では、何も出力せず終了した。検査対象を uv_buildsrc/ の両方で絞っているので、従来の単発スクリプトまで一律に止めない。

CI の直し方は二択

パッケージとして育てるなら、pyproject.toml[project.scripts] に入口を置き、workflow は uv run <script-name> に寄せる。コードを import してテストするなら、この形のほうがパスの扱いも揃う。

単発の運用スクリプトなら、無理にパッケージ化する理由はない。uv init --no-package で作り、main.py を入口として明示したほうが読み手にも意図が伝わる。ここを曖昧にしたまま両方のコマンドを置くと、CI と開発者の端末で別の入口が育つ。

おわりに

uv 0.12 の変更は、新規プロジェクトを普通の Python パッケージとして始めやすくするものだ。ただ、main.py から src/ への移動はディレクトリ名の差では終わらない。実行入口、テストの import、CI のコマンドまで一緒に決め直す境目になる。

アップグレード時は、まず uv.lock を更新する前に入口を一つにする。この検査を CI に一度入れておけば、新しいテンプレートと昔の workflow が静かに混ざる事故は防げる。

参照: https://github.com/astral-sh/uv/releases/tag/0.12.0

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?