Skip to content

Installation

← docs index

Requirements

Python 3.10 or newer (CPython; 3.10 – 3.13 are tested in CI)
Runtime dependencies none — standard library only
Operating systems Linux, macOS, Windows

No dependencies is a deliberate feature: quarantine is meant to be safe to add to someone else's crowded environment without a resolver argument.

Install from PyPI

The package lives at pypi.org/project/quarantine-py:

pip install quarantine-py

Releases are built in CI and published to PyPI with trusted publishing (OIDC) - no long-lived API keys and no manual uploads sit between the tagged source and the wheel you install.

With uv:

uv add quarantine-py            # inside a uv project
uv pip install quarantine-py    # standalone

Pin it like any other library:

# requirements.txt
quarantine-py~=0.1
# pyproject.toml
dependencies = ["quarantine-py>=0.1,<0.2"]

Install from conda-forge

The package is available on conda-forge:

conda install -c conda-forge quarantine-py

Or using mamba or pixi:

# mamba
mamba install -c conda-forge quarantine-py

# pixi
pixi add quarantine-py

Install for development, or from source

git clone https://github.com/halcyon-past/quarantine
cd quarantine
python -m venv .venv
. .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -e ".[dev]"       # the package plus pytest, ruff, mypy

Straight from git, without cloning:

pip install "git+https://github.com/halcyon-past/quarantine"

Verify the install

Two halves get installed — the importable library, and a quarantine command-line tool. Check both:

$ python -c "import quarantine; print(quarantine.__version__)"
0.1.2
$ quarantine --version
quarantine 0.1.2

If the command is not on your PATH — usually after a pip install --user, where scripts land somewhere your shell does not look — the module form is identical in every way:

python -m quarantine list

To find where the script went:

python -c "import sysconfig; print(sysconfig.get_path('scripts'))"

First run

Nothing needs configuring. Decorate a function, run it, and the folder appears the first time something fails:

from quarantine import quarantine


@quarantine
def process(item):
    return 100 / item


for item in [1, 2, 0, 4]:
    process(item)
$ python job.py
✓ 3 processed · ✗ 1 quarantined → .quarantine/  (run `quarantine retry` after fixing)
$ quarantine list
   #  when      function  error                              input preview
   1  09:14:02  process   ZeroDivisionError: division by ...  0

The summary line goes to stderr, so it never pollutes a pipeline whose output you are capturing on stdout.

Choose where the folder lives

In order of precedence:

@quarantine(dir="build/bad-rows")     # 1. explicit, per decorator
export QUARANTINE_DIR=/var/tmp/quarantine   # 2. environment, library + CLI

Otherwise ./.quarantine, relative to the process's working directory.

Add it to your ignore file. Quarantined inputs are your real data — rows, payloads, API responses — and they should not end up in a commit:

.quarantine/

Upgrading and uninstalling

pip install --upgrade quarantine-py
pip uninstall quarantine-py

To hear about new versions: on the GitHub repository, choose Watch → Custom → Releases — GitHub then notifies you of each release and its notes, and nothing else. Every release is also listed in the changelog, and tools like Dependabot and Renovate pick new versions up from PyPI automatically.

Uninstalling leaves any .quarantine/ folders alone; they are plain files, so delete them with rm -rf or quarantine clear --yes beforehand.

The record format carries a meta_version field, and records are read tolerantly: unknown keys are ignored and missing optional keys get defaults, so a folder written by an older version stays readable after an upgrade.