Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,20 @@ The ctl extra provides the `infrahubctl` command, which allows you to intera
pip install 'infrahub-sdk[ctl]'
```

#### tests
#### testing

The tests extra provides all the components for the testing framework of Transforms, Queries and Checks.
The testing extra provides the components for the testing framework of Transforms, Queries and Checks.

```bash
pip install 'infrahub-sdk[tests]'
pip install 'infrahub-sdk[testing]'
```

#### testcontainers

The testcontainers extra provides infrahub_sdk.testing.docker, which starts a real Infrahub in containers so tests can run against a live instance.

```bash
pip install 'infrahub-sdk[testcontainers]'
```

#### all
Expand Down
2 changes: 1 addition & 1 deletion changelog/+dependency-lower-bounds.changed.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ If you pin `pydantic` to 2.0, 2.0.1 or 2.0.2, installing the SDK now fails while

`anyio` and `typing-extensions` are now installed as direct requirements of the SDK itself. It has always imported them but relied on other packages to pull them in, so a minimal or heavily constrained environment could end up with the SDK installed and unusable. No action is needed, installs simply become reliable.

`packaging` is now a direct requirement of the `tests` extra, which is where the `infrahub_sdk.testing` helpers that import it live. A plain `pip install infrahub-sdk` does not install it.
`packaging` is now a direct requirement of the `testcontainers` extra, which is where the `infrahub_sdk.testing.docker` helper that imports it lives. A plain `pip install infrahub-sdk` does not install it.
7 changes: 7 additions & 0 deletions changelog/+testing-extras.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Two extras now cover testing, replacing the `tests` extra.

`pip install 'infrahub-sdk[testing]'` installs pytest and enables the bundled `pytest-infrahub` plugin, which is what you want to test your own Transforms, Queries and Checks. `pip install 'infrahub-sdk[testcontainers]'` additionally provides `infrahub_sdk.testing.docker`, which starts a real Infrahub in containers so your tests can run against a live instance instead of mocked responses.

They are kept apart because the container tooling is a much heavier install. `[testing]` adds four packages on top of a plain install; `[testcontainers]` adds around sixty, among them Docker, FastAPI and Prefect client libraries. Previously you would have had to take all of it to get either.

The `tests` extra is gone. It stopped installing anything in 1.16.0 while the documentation carried on advertising it, so since then `pip install 'infrahub-sdk[tests]'` has warned about an unknown extra and installed only the base package. If you are coming from 1.15.2 or earlier, `[testing]` is the direct replacement for what `[tests]` gave you.
5 changes: 0 additions & 5 deletions changelog/+tests-extra.added.md

This file was deleted.

20 changes: 16 additions & 4 deletions docs/docs/python-sdk/guides/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,31 @@ pip install 'infrahub-sdk[ctl]'

<!-- vale off -->

### tests
### testing

<!-- vale on -->

The `tests` extra provides all the components for the testing framework of Transforms, Queries and Checks.
The `testing` extra provides the components for the testing framework of Transforms, Queries and Checks. It installs `pytest` and enables the bundled `pytest-infrahub` plugin, which discovers the `infrahub_tests` blocks in your repository's `.infrahub.yml` and turns them into test cases.

```shell
pip install 'infrahub-sdk[tests]'
pip install 'infrahub-sdk[testing]'
```

<!-- vale off -->

### testcontainers

<!-- vale on -->

The `testcontainers` extra provides `infrahub_sdk.testing.docker`, which starts a real Infrahub in containers so your tests can run against a live instance rather than mocked responses. Inherit `TestInfrahubDockerClient` to get a client connected to that instance.

```shell
pip install 'infrahub-sdk[testcontainers]'
```

:::note

This extra is much larger than the others. The `infrahub_sdk.testing` helpers run Infrahub in containers, which adds roughly 61 packages on top of the base SDK. Install the `ctl` extra on its own if you only need the `infrahubctl` command.
This extra is considerably larger than the others, since running Infrahub in containers pulls in Docker, FastAPI and Prefect client libraries. Install `testing` on its own if you only need to test Transforms, Queries and Checks.

:::

Expand Down
4 changes: 2 additions & 2 deletions docs/docs/python-sdk/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Extras are available for additional functionality:

```bash
uv add 'infrahub-sdk[ctl]' # Adds the infrahubctl CLI
uv add 'infrahub-sdk[tests]' # Adds the testing framework for transforms and checks
uv add 'infrahub-sdk[testing]' # Adds the testing framework for transforms and checks
uv add 'infrahub-sdk[all]' # Everything
```

Expand All @@ -135,7 +135,7 @@ Extras are available for additional functionality:

```bash
pip install 'infrahub-sdk[ctl]' # Adds the infrahubctl CLI
pip install 'infrahub-sdk[tests]' # Adds the testing framework for transforms and checks
pip install 'infrahub-sdk[testing]' # Adds the testing framework for transforms and checks
pip install 'infrahub-sdk[all]' # Everything
```

Expand Down
17 changes: 11 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,23 @@ ctl = [
"ariadne-codegen==0.18.0",
]

# The user-facing testing surface: `infrahub_sdk.testing` and the bundled `pytest-infrahub`
# plugin. Both ship in the wheel but import packages that no other extra installs. Distinct from
# the `tests` dependency group below, which is the tooling used to test the SDK itself.
tests = [
# The bundled `pytest-infrahub` plugin and the schemas under `infrahub_sdk.testing`, for writing
# tests against your own Transforms, Queries and Checks.
testing = [
"pytest>=7.0",
]

# `infrahub_sdk.testing.docker`, which starts a real Infrahub in containers. Kept apart from
# `testing` because it is the only module that needs it and it is a far heavier install.
testcontainers = [
"infrahub-sdk[testing]",
"infrahub-testcontainers>=1.7.3",
"packaging>=21.0",
"pytest>=7.0",
]

# Self-referential so it cannot drift out of sync with the extras it aggregates.
all = [
"infrahub-sdk[ctl,tests]",
"infrahub-sdk[ctl,testing,testcontainers]",
]

[dependency-groups]
Expand Down
39 changes: 29 additions & 10 deletions tests/unit/test_packaging_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import ast
import re
import sys
from collections.abc import Container, Iterator
from collections.abc import Iterable, Iterator
from dataclasses import dataclass
from pathlib import Path
from typing import Any, TypeGuard
Expand All @@ -34,10 +34,13 @@

# Modules that ship in the wheel but are allowed to need an extra, mapped to the extra that
# supplies them. `ctl/` is the CLI; `async_typer` and `graphql/plugin.py` sit outside it but are
# only reachable from it. Everything else must import on a plain install.
# only reachable from it. Everything else must import on a plain install, which is why the entries
# are per-file where a package is split: `testing/repository.py` needs nothing beyond the core
# dependencies, so it is deliberately absent and held to the base install.
EXTRA_ONLY_MODULES = {
"ctl": ("ctl/", "async_typer.py", "graphql/plugin.py"),
"tests": ("pytest_plugin/", "testing/"),
"testing": ("pytest_plugin/", "testing/schemas/"),
"testcontainers": ("testing/docker.py",),
}

# Operators that establish a floor. A requirement without one of these lets a resolver reach back
Expand Down Expand Up @@ -137,13 +140,29 @@ def _requirement_cases() -> list[RequirementCase]:
]


def _declared_in(sections: Container[str]) -> set[str]:
"""The distributions installed by the given sections of `pyproject.toml`."""
return {
_normalize(requirement.name)
for requirement in (Requirement(case.requirement) for case in _requirement_cases() if case.section in sections)
if not _is_self_reference(requirement)
}
def _declared_in(sections: Iterable[str]) -> set[str]:
"""The distributions installed by the given sections of `pyproject.toml`.

Self-referential extras are followed, so asking for `testcontainers` also returns whatever
the `testing` extra it references installs. Without that, an extra defined by self-reference
would look as though it installed nothing.
"""
cases = _requirement_cases()
pending, seen, declared = [_normalize(section) for section in sections], set(), set()
while pending:
section = pending.pop()
if section in seen:
continue
seen.add(section)
for case in cases:
if _normalize(case.section) != section:
continue
requirement = Requirement(case.requirement)
if _is_self_reference(requirement):
pending.extend(_normalize(extra) for extra in requirement.extras)
else:
declared.add(_normalize(requirement.name))
return declared


def _surface_of(relative_path: Path) -> str | None:
Expand Down
14 changes: 9 additions & 5 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.