Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable reading dependency specifiers from PEP 621 pyproject.toml #845

Open
Tracked by #238
paduszyk opened this issue Aug 30, 2024 · 3 comments
Open
Tracked by #238

Enable reading dependency specifiers from PEP 621 pyproject.toml #845

paduszyk opened this issue Aug 30, 2024 · 3 comments

Comments

@paduszyk
Copy link

paduszyk commented Aug 30, 2024

How would this feature be useful?

Let us assume I develop a Python package. In my PEP 621 pyproject.toml I have:

[project]
# ...
dependencies = [
   "django >= 3.2, < 5.2",
   "typing-extensions >= 4.12.2, < 5",
]

# ...

[project.optional-dependencies]
# dev = [ ... ]
lint = [
  "ruff >= 0.6.3, < 1",
  "django-stubs[compatible-mypy] >= 5.0.4, < 6",
]
# test = [ ... ]

Then, I would like to have separate sessions for checking and formatting my codebase with ruff and type-checking it with mypy. It would go like this:

@nox.session(tags=["lint"])
@nox.parametrize(
    "command",
    [
        "check",
        "format",
    ],
)
def ruff(session: nox.Session, command: str) -> None:
    session.install("-e", ".[lint]")

    session.run("ruff", command)


@nox.session(tags=["lint"])
def mypy(session: nox.Session) -> None:
    session.install("-e", ".[lint]")
 
    session.run("mypy", ".")

It works, but:

  • actually, ruff session does not need the package as well as any other lint dependencies to be installed — we need ruff ONLY;
  • mypy does not need ruff — nevertheless, it's installed because it's in the same dependency group.

A nice feature would be to tell Nox which dependencies should be installed, with version specifiers automatically read from pyproject.toml.

Describe the solution you'd like

This is a draft for my noxfile.py implementing a potential solution:

from __future__ import annotations

__all__ = [
    "mypy",
    "ruff",
]

import re
from functools import partial
from pathlib import Path
from typing import cast

import nox

PYPROJECT_TOML_PATH = Path(__file__).resolve().parent / "pyproject.toml"

def get_dependency_specifiers(pattern: str, *, group: str | None = None) -> list[str]:
    project = nox.project.load_toml(PYPROJECT_TOML_PATH).get("project")

    if not project:
        msg = (
            f"{PYPROJECT_TOML_PATH} is not a valid PEP 621 metadata file as "
            f"it does not contain a [project] table"
        )

        raise LookupError(msg)

    if group is None:
        dependencies = project.get("dependencies", [])
    else:
        try:
            dependencies = project.get("optional-dependencies", {})[group]
        except KeyError as e:
            msg = f"{group!r} dependencies are not defined in {PYPROJECT_TOML_PATH}"

            raise LookupError(msg) from e

    dependencies = cast(list[str], dependencies)

    if not (dependencies := list(filter(partial(re.match, pattern), dependencies))):
        msg = (
            f"{PYPROJECT_TOML_PATH} does not define any dependencies "
            f"that match {pattern!r}"
        )

        raise LookupError(msg)

    return dependencies


@nox.session(tags=["lint"])
@nox.parametrize(
    "command",
    [
        "check",
        "format",
    ],
)
def ruff(session: nox.Session, command: str) -> None:
    session.install(
        *get_dependency_specifiers(r"^ruff(\[.*\])?", group="lint"),
    )

    session.run("ruff", command)


@nox.session(tags=["lint"])
def mypy(session: nox.Session) -> None:
    session.install("-e", ".")
    session.install(
        *get_dependency_specifiers(r"^django\-stubs\[compatible-mypy\]", group="lint"),
    )

    session.run("mypy", ".")

Now, each environment contains only the relevant dependencies with the versions retrieved directly from the project's metadata.

Describe alternatives you've considered

No response

Anything else?

The function get_dependency_specifiers could be a part of the nox.project module.

Of course, I am open to any changes in the design. If you're interested, I can open a PR assuming you will assist in developing tests.

@henryiii
Copy link
Collaborator

henryiii commented Oct 3, 2024

IMO, this should be on hold until the dependency group pep gets accepted/rejected, in case that needs to be part of the design.

@henryiii
Copy link
Collaborator

The PEP (735) was just accepted!

@johnthagen
Copy link
Contributor

Supporting PEP 735 dependency groups would be great. Some motivation on a similar issue for nox-poetry is listed here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

3 participants