Metadata-Version: 2.4
Name: pytest-dag
Version: 3.1.3
Summary: A pytest plugin that enforces test execution order via a dependency DAG
Author-email: "SLR Software Solutions Inc." <support@slrsoft.ca>
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://github.com/SLR-Software-Solutions-Inc/pytest-dag
Project-URL: Bug Tracker, https://github.com/SLR-Software-Solutions-Inc/pytest-dag/issues
Keywords: pytest,testing,dag,dependencies,ordering,plugin
Classifier: Framework :: Pytest
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Testing
Classifier: Intended Audience :: Developers
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pytest>=7.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: cryptography>=42.0.0
Dynamic: license-file

# pytest-dag

<p align="center">
  <img src="https://raw.githubusercontent.com/SLR-Software-Solutions-Inc/pytest-dag-documentation/main/docs/_static/branding/pytest-dag-logo-512.png" alt="pytest-dag logo" width="88">
</p>

![pytest-dag banner](https://raw.githubusercontent.com/SLR-Software-Solutions-Inc/pytest-dag-documentation/main/docs/_static/branding/pytest-dag-banner-1280x640.png)

[![Docs status](https://readthedocs.org/projects/pytest-dag/badge/?version=latest)](https://pytest-dag.readthedocs.io/en/latest/)

Documentation: https://pytest-dag.readthedocs.io/en/latest/

`pytest-dag` is a pytest plugin for dependency-aware test execution.

It lets tests declare dependencies, builds a DAG, runs tests in topological
order, and skips downstream tests when required dependencies fail.

## Installation

```bash
pip install pytest-dag
```

## Quick Start

```python
import pytest

def test_a():
    assert True

@pytest.mark.dag(depends=["test_a"])
def test_b():
    assert True

@pytest.mark.dag(depends=["test_b"])
def test_c():
    assert True

@pytest.mark.dag(depends=["test_b"])
def test_d():
    assert True
```

Expected run order:

`test_a -> test_b -> (test_c, test_d)`

If `test_a` fails, dependent tests are skipped with a clear reason:

```text
SKIPPED pytest-dag: blocked by test_file.py::test_a (FAILED)
```

## Marker Syntax

```python
# Single dependency (string)
@pytest.mark.dag(depends="test_login")
def test_profile():
    ...

# Multiple dependencies (list)
@pytest.mark.dag(depends=["test_login", "test_db_connect"])
def test_dashboard():
    ...

# Cross-file dependency (full nodeid)
@pytest.mark.dag(depends=["tests/auth/test_auth.py::test_login"])
def test_profile():
    ...
```

## YAML DAG

For larger suites, you can define dependencies in YAML instead of (or in
addition to) markers.

`pyproject.toml`:

```toml
[tool.pytest.ini_options]
dag_file = "tests/dag.yaml"
```

`tests/dag.yaml`:

```yaml
nodes:
  - id: tests/test_flow.py::test_a
  - id: tests/test_flow.py::test_b
    depends: [tests/test_flow.py::test_a]
  - id: tests/test_flow.py::test_c
    depends: [tests/test_flow.py::test_b]
```

Marker dependencies and YAML dependencies are unioned.

## CLI Options

| Option                             | Default | Description                                                       |
| ---------------------------------- | ------- | ----------------------------------------------------------------- |
| `--dag-block-on-outcomes OUTCOMES` | `fail` | Outcomes that block dependents: `fail`, `skip`, `xfail`, `error` |
| `--dag-print-graph` | off | Print DAG order and edges after collection |
| `--pytest-dag-license-key`         | unset   | Provide license key directly |
| `--pytest-dag-license-key-file`    | unset   | Read license key from file |

Examples:

```bash
# Cascade skipping (skip dependents when a dependency fails or is skipped)
pytest --dag-block-on-outcomes fail,skip

# Print computed graph for debugging
pytest --dag-print-graph
```

## License Setup

`pytest-dag` may require a license key depending on current license policy.

Provide a license key using one of:

- Environment variable: `PYTEST_DAG_LICENSE_KEY`
- CLI value: `--pytest-dag-license-key`
- Key file: `--pytest-dag-license-key-file`

Examples:

```bash
export PYTEST_DAG_LICENSE_KEY=pd-XXXX-XXXX-XXXX-XXXX
python -m pytest -v -rs
```

```bash
pytest --pytest-dag-license-key-file /path/to/key.txt -v -rs
```

Purchase or renew:

- `https://slrsoft.ca/app/license/purchase?product=pytest-dag`

Support:

- `support@slrsoft.ca`

## Viewing Skip Reasons

Use `-v -rs` to see exact skip reasons:

```bash
pytest -v -rs
```

Example summary:

```text
SKIPPED [2] pytest-dag: blocked by test_demo.py::test_login (FAILED)
SKIPPED [1] test_demo.py:104: feature not yet implemented
```

## pytest-xdist Compatibility

`pytest-dag` is not compatible with `pytest-xdist` parallel execution (`-n`).
To preserve DAG correctness, the plugin automatically disables xdist when it is
active and prints a warning.

If xdist is installed but `-n` is not passed, nothing is changed.

## Troubleshooting

### `plugins: ... dag-0.1.0` (not `pytest-dag-0.1.0`)

This is normal. Pytest shortens plugin names in output by dropping the
`pytest-` prefix.

### Plugin not loading in a virtualenv

If `which pytest` points to a global executable after activating a venv, use:

```bash
python -m pytest ...
```

This guarantees the venv interpreter and installed plugin are used.

## License

Proprietary. Copyright (c) 2026 SLR Software Solutions Inc.

See the license terms in the repository `LICENSE` file.

Licensing inquiries: `support@slrsoft.ca`
