2025, Nov 06 21:01

Два способа получить URL из [project.urls] в entry point

Показываем, как в Python-CLI получить URL из [project.urls] в pyproject.toml: через tomllib по исходникам и через importlib.metadata по установленному пакету.

Как получить значение из [project.urls] в pyproject.toml во время выполнения — прямо внутри entry point? Это частый сценарий, когда CLI должен показывать метаданные пакета, например ссылку на Homepage, не пришивая её в код. Ниже — краткая инструкция по двум рабочим подходам, которые опираются только на возможности стандартной библиотеки, доступные в экосистеме упаковки Python.

Постановка задачи

У вас есть пакет, настроенный через pyproject.toml, и скрипт, опубликованный как entry point. Цель — получить URL, соответствующий ключу Homepage в разделе [project.urls], из функции, на которую указывает spam:main_cli.

[project.urls]
Homepage = "https://example.com"
Documentation = "https://readthedocs.org"
Repository = "https://github.com/me/spam.git"
"Bug Tracker" = "https://github.com/me/spam/issues"
Changelog = "https://github.com/me/spam/blob/master/CHANGELOG.md"
[project.scripts]
spam-cli = "spam:main_cli"

Что на самом деле происходит

Во время выполнения возможны два сценария: либо у вас есть доступ к дереву исходников проекта, включая pyproject.toml, либо у вас есть только установленный пакет. В первом случае можно напрямую разобрать pyproject.toml и прочитать таблицу [project.urls]. Во втором — запросить метаданные установленного дистрибутива и забрать URL-адреса проекта, которые предоставляет менеджер пакетов.

Решение 1: читаем pyproject.toml с помощью tomllib

Если CLI запускается в окружении, где доступен файл pyproject.toml проекта, разберите его и возьмите значение Homepage напрямую из соответствующего словаря.

import tomllib as tlib
from pathlib import Path
def load_homepage_from_pyproject(pyproject_path: str):
    cfg_path = Path(pyproject_path)
    with cfg_path.open("rb") as fh:
        data = tlib.load(fh)
    urls_map = data.get("project", {}).get("urls", {})
    return urls_map.get("Homepage")
def cli_entry_pyproject():
    homepage_url = load_homepage_from_pyproject("pyproject.toml")
    if homepage_url:
        print(homepage_url)
    else:
        print("Homepage URL not found")

Решение 2: получаем метаданные установленного пакета через importlib.metadata

Если пакет уже установлен, доступ к pyproject.toml не нужен. Получите URL-адреса проекта из метаданных дистрибутива.

from importlib import metadata as pkg_meta
def fetch_project_urls(dist_name: str):
    meta = pkg_meta.metadata(dist_name)
    return meta.get_all("Project-URL")
def cli_entry_installed():
    urls = fetch_project_urls("spam-eggs")
    if urls:
        for item in urls:
            print(item)
    else:
        print("No Project-URL entries found")

Почему это важно

Встраивая получение метаданных прямо в entry points, вы делаете CLI самодокументируемым и избегаете дублирования значений между кодом и конфигурацией. Когда меняется сайт документации, репозиторий или трекер задач, достаточно обновить единственный источник правды — и CLI сразу это подхватит. Это также избавляет от хрупкого парсинга произвольных файлов и зависимости от нестандартных структур. Плюс, подход остаётся простым, даже если из документации это неочевидно: нужен лишь небольшой, надёжный фрагмент кода.

Практические выводы

Выбирайте путь, который соответствует среде выполнения. Если инструмент запускается внутри дерева проекта, разбирайте pyproject.toml и обращайтесь напрямую к [project.urls]. Если работаете с установленным дистрибутивом, используйте importlib.metadata, чтобы получить URL-адреса проекта. Держите логику извлечения минимальной и полагайтесь на метаданные упаковки как на единственный источник истины.

Статья основана на вопросе на StackOverflow от bad_coder и ответе scr.