Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
install_groups takes args instead of list of groups
  • Loading branch information
Niicck committed Feb 7, 2023
commit a68884d62e326e46d27af05dcd0182ebdbf606ce
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Here is a comparison of the different installation methods:
- Use `session.install(...)` to install specific development dependencies, e.g. `session.install("pytest")`.
- Use `session.install(".")` (or `session.poetry.installroot()`) to install your own package.
- Use `session.run_always("poetry", "install", external=True)` to install your package with _all_ development dependencies.
- Use `session.install_groups(...)` to install all depedencies in given dependency groups (only available for poetry >= 1.2.0).

Please read the next section for the tradeoffs of each method.

Expand Down
8 changes: 4 additions & 4 deletions src/nox_poetry/poetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,12 @@ def config(self) -> Config:

def export(
self,
only_groups: Optional[List[str]] = None,
groups: Optional[List[str]] = None,
) -> str:
"""Export the lock file to requirements format.

Args:
only_groups: optional list of poetry depedency groups to --only install.
groups: optional list of poetry depedency groups to --only install.

Returns:
The generated requirements as text.
Expand All @@ -111,8 +111,8 @@ def export(
"--without-hashes",
]

if only_groups:
args.extend(f"--only={group}" for group in only_groups)
if groups:
args.extend(f"--only={group}" for group in groups)
elif self.config.is_compatible_with_group_deps():
args.append("--with=dev")
else:
Expand Down
80 changes: 37 additions & 43 deletions src/nox_poetry/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,77 +198,93 @@ def installroot(

self.session.install(f"--constraint={requirements}", package)

def install_groups(self, groups: List[str], *args, **kwargs) -> None:
def install_groups(self, *args: str, **kwargs: Any) -> None:
"""Install all packages in the given Poetry dependency groups.

Args:
groups: The poetry dependency groups to install.
args: Command-line arguments for ``pip install``.
args: The poetry dependency groups to install.
kwargs: Keyword-arguments for ``session.install``. These are the same
as those for :meth:`nox.sessions.Session.run`.

Raises:
ValueError: if no groups are provided to install.
"""
groups = [*args]
if not groups:
raise ValueError("At least one argument required to install_groups().")

try:
requirements = self.export_requirements(only_groups=groups)
requirements = self.export_requirements(groups=groups)
except CommandSkippedError:
return

self.session.install("-r", str(requirements), *args, **kwargs)
self.session.install("-r", str(requirements), **kwargs)

def export_requirements(
self,
only_groups: Optional[List[str]] = None,
groups: Optional[List[str]] = None,
) -> Path:
"""Export a requirements file from Poetry.

This function uses `poetry export <https://python-poetry.org/docs/cli/#export>`_
to generate a :ref:`requirements file <Requirements Files>` containing the
project dependencies at the versions specified in ``poetry.lock``.

If a list of only_groups is not provided, then a constraints.txt file will be
If a list of groups is not provided, then a constraints.txt file will be
generated that includes both main and dev group dependencies.

If a list of only_groups is provided, then a requirements.txt file will be
If a list of groups is provided, then a requirements.txt file will be
generated that includes only the specified group dependencies.

Each constraints/requirements file is stored in a per-session temporary
directory, together with a hash digest over ``poetry.lock`` to avoid generating
the file when the dependencies or only_groups have not changed since the last
the file when the dependencies or groups have not changed since the last
run.

Args:
only_groups: optional list of poetry depedency groups to --only install.
Passing only_groups will generate a requirements.txt file to install
groups: optional list of poetry depedency groups to --only install.
Passing groups will generate a requirements.txt file to install
all packages in those groups, rather than generating a constraints.txt
file for installing individual packages.

Raises:
IncompatiblePoetryVersionError: The version of poetry installed is less than
v1.2.0, which is not compatible with installing dependency groups.

Returns:
The path to the requirements file.
"""
# Avoid ``session.virtualenv.location`` because PassthroughEnv does not
# have it. We'll just create a fake virtualenv directory in this case.

"""
If no only_groups are provided, then export requirements as a constraints.txt
If no groups are provided, then export requirements as a constraints.txt
file. Otherwise, export requirements as a requirements.txt file.
"""
export_as_constraints = not only_groups
if groups and not self.poetry.config.is_compatible_with_group_deps():
raise IncompatiblePoetryVersionError(
f"Installed version of poetry must be >="
f" {self.poetry.config.MINIMUM_VERSION_SUPPORTING_GROUP_DEPS} in"
" order to install dependency groups. Current version installed:"
f" {self.poetry.config.version()}"
)

tmpdir = Path(self.session._runner.envdir) / "tmp"
tmpdir.mkdir(exist_ok=True, parents=True)

if export_as_constraints:
filename = "constraints.txt"
if groups:
filename = ",".join(groups) + "-" + "requirements.txt"
else:
filename = ",".join(only_groups) + "-" + "requirements.txt"
filename = "constraints.txt"
path = tmpdir / filename
hashfile = tmpdir / f"{path.name}.hash"

lockdata = Path("poetry.lock").read_bytes()
digest = hashlib.blake2b(lockdata).hexdigest()

if not hashfile.is_file() or hashfile.read_text() != digest:
contents = self.poetry.export(only_groups=only_groups)
if export_as_constraints:
contents = self.poetry.export(groups=groups)
if not groups:
contents = to_constraints(contents)
path.write_text(contents)
hashfile.write_text(digest)
Expand Down Expand Up @@ -336,28 +352,6 @@ def install(self, *args: str, **kwargs: Any) -> None:
"""Install packages into a Nox session using Poetry."""
return self.poetry.install(*args, **kwargs)

def install_groups(self, groups: List[str], *args, **kwargs) -> None:
"""Install all packages in the given Poetry dependency groups.

Args:
groups: The poetry dependency groups to install.
args: Command-line arguments for ``pip install``.
kwargs: Keyword-arguments for ``session.install``. These are the same
as those for :meth:`nox.sessions.Session.run`.

Raises:
IncompatiblePoetryVersionError: The version of poetry installed is less than
v1.2.0, which is not compatible with installing dependency groups.

Returns:
None
"""
if not self.poetry.poetry.config.is_compatible_with_group_deps():
raise IncompatiblePoetryVersionError(
f"Installed version of poetry must be >="
f" {self.poetry.poetry.config.MINIMUM_VERSION_SUPPORTING_GROUP_DEPS} in"
" order to install dependency groups. Current version installed:"
f" {self.poetry.poetry.config.version()}"
)

return self.poetry.install_groups(groups, *args, **kwargs)
def install_groups(self, *args: str, **kwargs: Any) -> None:
"""Install all packages from given Poetry dependency groups."""
return self.poetry.install_groups(*args, **kwargs)
6 changes: 3 additions & 3 deletions src/nox_poetry/sessions.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ class _PoetrySession:
def installroot(
self, *, distribution_format: str = ..., extras: Iterable[str] = ...
) -> None: ...
def export_requirements(self, only_groups: Optional[List[str]] = None) -> Path: ...
def export_requirements(self, groups: Optional[List[str]] = None) -> Path: ...
def build_package(self, *, distribution_format: str = ...) -> str: ...
def install_groups(self, groups: List[str], *args: str, **kwargs: Any) -> None: ...
def install_groups(self, *args: str, **kwargs: Any) -> None: ...

class Session(nox.Session):
def __init__(self, session: nox.Session) -> None: ...
def install_groups(self, groups: List[str], *args: str, **kwargs: Any) -> None: ...
def install_groups(self, *args: str, **kwargs: Any) -> None: ...
poetry: _PoetrySession

SessionFunction = Callable[..., None]
Expand Down
6 changes: 3 additions & 3 deletions tests/functional/test_install_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def test_dev_dependencies(project: Project) -> None:
@nox_poetry.session
def test(session: nox_poetry.Session) -> None:
"""Install the local package."""
session.install_groups(["dev"])
session.install_groups("dev")

run_nox_with_noxfile(project, [test], [nox_poetry])

Expand All @@ -40,7 +40,7 @@ def test_group_dev(group_project: Project) -> None:
@nox_poetry.session
def test(session: nox_poetry.Session) -> None:
"""Install the local package."""
session.install_groups(["dev"])
session.install_groups("dev")

run_nox_with_noxfile(group_project, [test], [nox_poetry])

Expand All @@ -60,7 +60,7 @@ def test_two_groups(group_project: Project) -> None:
@nox_poetry.session
def test(session: nox_poetry.Session) -> None:
"""Install the local package."""
session.install_groups(["test", "lint"])
session.install_groups("test", "lint")

run_nox_with_noxfile(group_project, [test], [nox_poetry])

Expand Down
19 changes: 14 additions & 5 deletions tests/unit/test_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def test_install_groups_no_install(
"nox_poetry.poetry.Config.is_compatible_with_group_deps", lambda _: True
)

proxy.poetry.install_groups(["dev"])
proxy.poetry.install_groups("dev")

assert cast(FakeSession, session).install_called is not no_install

Expand All @@ -250,8 +250,17 @@ def test_install_groups_old_poetry(
"nox_poetry.poetry.Config.is_compatible_with_group_deps", lambda _: False
)

proxy.install
proxy.poetry.install_groups

with pytest.raises(nox_poetry.poetry.IncompatiblePoetryVersionError):
proxy.install_groups(["dev"])
proxy.install_groups("dev")


def test_install_groups_no_args(
proxy: nox_poetry.Session, monkeypatch: pytest.MonkeyPatch
) -> None:
"""It raises ValueError if no args are passed to install_groups."""
monkeypatch.setattr(
"nox_poetry.poetry.Config.is_compatible_with_group_deps", lambda _: False
)

with pytest.raises(ValueError):
proxy.install_groups()