Metadata-Version: 2.5
Name: pytest-fsplit
Version: 0.1.0
Summary: File-level pytest sharding based on historical test durations.
License: MIT License
        
        Copyright (c) 2026 BeakrHub
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Classifier: Framework :: Pytest
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
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: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.10
Requires-Dist: pytest>=7
Provides-Extra: dev
Requires-Dist: nbval; extra == 'dev'
Requires-Dist: pytest-split; extra == 'dev'
Requires-Dist: pytest-xdist>=3; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# pytest-fsplit

`pytest-fsplit` is a pytest plugin for splitting a test suite into deterministic
file-level shards before pytest imports and collects unselected files. This is in contrast with plugins like `pytest-split` which collect fully and then deselect in each shard. In large test code bases this collection step can take up multiple minutes and keeps taking longer as you add more tests. In `pytest-fsplit` the collection is a lot faster and stays proportional to shard size. In our own codebase with 18k tests and 1.5k test files, `pytest-fsplit` takes 40s vs `pytest-split` which takes 3 mins to collect tests.

`pytest-fsplit` reads a pytest-split-compatible JSON duration file, aggregates node timings by
test file, assigns files to shards with a longest-processing-time-first plan, and
uses `pytest_ignore_collect` to prune files and directories outside the selected
shard.

## Installation

```bash
pip install pytest-fsplit
```

## Usage

First record durations from a complete, unsharded run:

```bash
pytest --fsplit-store-durations
```

Then run each file shard separately:

```bash
pytest --fsplits 4 --fgroup 1
pytest --fsplits 4 --fgroup 2
pytest --fsplits 4 --fgroup 3
pytest --fsplits 4 --fgroup 4
```

The duration file defaults to `.test_durations` in the invocation directory and
can be changed with `--fsplit-durations-path`.

`pytest-fsplit` intentionally uses distinct option names. If `pytest-split` is
installed too, `--splits` and `--group` continue to belong to pytest-split; use
`--fsplits` and `--fgroup` for file-level pre-collection sharding.

The default file splitting algorithm is `least_duration`, which greedily assigns
the next heaviest file to the lightest shard. To preserve contiguous lexical file
order instead, use `duration_based_chunks`:

```bash
pytest --fsplits 4 --fgroup 1 --fsplit-algorithm duration_based_chunks
```

When nbval is active through `--nbval` or `--nbval-lax`, pytest-fsplit treats
`.ipynb` notebooks as shardable files automatically. For other non-Python
collectors, provide the file patterns pytest-fsplit should treat as shardable
files:

```bash
pytest --fsplit-file-pattern "*.case" --fsplits 4 --fgroup 1
```

Stored duration files can be inspected from the command line:

```bash
fsplit-plan --fsplits 4
fsplit-slowest-tests --count 10
slowest-tests --count 10
fsplit-slowest-files --count 10
```

Use `fsplit-plan --fsplits 4 --show-files` to see file assignments, or
`fsplit-plan --fsplits 4 --json` for machine-readable output.

## Behavior

- Shard indices are one-based.
- Both `--fsplits` and `--fgroup` must be supplied together.
- If there are more shards than candidate files, planned empty shards exit
  successfully.
- `--fsplit-algorithm` supports `least_duration` and `duration_based_chunks`.
- Files without historical timings use the median known file duration.
- Stale timing entries for deleted files are ignored.
- Missing, malformed, or unusable duration files fail immediately when sharding.
- `--fsplit-store-durations` writes the same node-duration JSON shape used by
  pytest-split.
- Older pytest-split list-of-pairs duration files are accepted when reading.
- `--fsplit-store-durations` cannot be combined with sharding because it would
  record only the selected shard.
- `--fsplit-clean-durations` is valid only with `--fsplit-store-durations`.

## Compatibility

`pytest-fsplit` runs before collection, so it avoids the full-suite collection
cost paid by post-collection splitters. It honors pytest collection roots,
`python_files`, `--ignore`, `--ignore-glob`, `norecursedirs`, explicit file
arguments, marker deselection, and xdist worker startup.

If pytest-split is installed too, do not combine `--fsplits`/`--fgroup` with
pytest-split's `--splits`/`--group` or `--store-durations`; pytest-fsplit
rejects those combinations to avoid applying two independent partitions or
recording timings for only one file shard.

Unlike pytest-split's post-collection grouping, pytest-fsplit groups whole files
before collection. That means test-order randomization plugins can still reorder
items inside the selected files without changing which files belong to a shard.

When nbval is active, `.ipynb` files are assigned as whole-file shards
automatically. For other file-backed collectors, include those file names with
`--fsplit-file-pattern`. Pytest-fsplit assigns whole files to shards, so it will
not split one notebook or custom collected file across several groups.

## Development

```bash
uv sync --extra dev
uv run ruff check .
uv run pytest -q
uv build
```
