Metadata-Version: 2.4
Name: modify-csv
Version: 2026.10.4
Summary: Apply a user-supplied Python function to a CSV row, column, or cell.
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# modify-csv

Apply a user-provided Python function to every cell in one CSV data row, every
cell in one column, or one cell. The CSV's first record is treated as its header
and is never modified. Row and column indexes are zero-based; columns can also
be addressed by their exact header text.

## Install

Install a published release from PyPI:

```sh
python -m pip install modify-csv
```

Or install a tagged version directly from the private GitHub repository using
SSH authentication:

```sh
python -m pip install "git+ssh://git@github.com/martinjose/modify-csv.git@v0.1.0"
```

Git and access to the private repository are required for the second form. Each
GitHub Release also provides a universal wheel and source archive.

For local development, install an editable copy:

```sh
python -m pip install -e .
```

This installs the `modify-csv` command. You can also run the package without an
installation using `PYTHONPATH=src python -m modify_csv`.

## Script contract

The supplied script must define exactly this callable shape:

```python
from typing import Any


def eval(content: Any, row: int, col: int) -> Any:
    return content
```

`Any` is provided by Python's standard-library `typing` module. The function
name, parameter names, and three-argument shape are enforced.

`content` is the current CSV field (initially a string), while `row` and `col`
are zero-based numeric indexes into the data records and columns. The returned
value is passed to Python's CSV writer. The script is ordinary trusted Python
and executes with the same permissions as the command.

## Examples

Apply a script file to a column selected by header:

```sh
modify-csv apply input.csv output.csv --script-file normalize.py --column email
```

Apply inline code to data row 2:

```sh
modify-csv apply input.csv output.csv \
  --script 'def eval(content, row, col): return content.strip()' \
  --row 2
```

Apply inline code to the cell at data row 0 and column 1:

```sh
modify-csv apply input.csv output.csv \
  --script 'def eval(content, row, col): return int(content) * 2' \
  --cell 0 1
```

Exactly one of `--script` or `--script-file`, and exactly one of `--row`,
`--column`, or `--cell`, is required. Existing output files are protected;
pass `--force` to replace one.

Generate a ready-to-edit transformation script:

```sh
modify-csv template transform.py
```

Use `--force` to replace an existing template. Run `modify-csv --help` or the
help for either subcommand to see the required function signature. The original
form without the `apply` subcommand remains supported for compatibility.

## Releasing

Versions follow `YYYY.M.RELEASE`, where `RELEASE` starts at `1` each month. For
example, the first release in October 2026 is `2026.10.1`.

Preview the next version without changing the repository:

```sh
python scripts/release.py next
```

To update `pyproject.toml`, create the release commit, and create an annotated
tag locally for inspection:

```sh
python scripts/release.py prepare
```

The command prints the exact push command to use after reviewing the commit and
tag. To prepare and push both atomically in one step instead, run:

```sh
python scripts/release.py release
```

Release preparation requires a clean `main` branch. The package version in
`pyproject.toml` remains the release source of truth. Pushing the generated tag
starts the GitHub Actions release process:

```sh
git push --atomic origin main v2026.10.1
```

The tag-triggered release workflow tests Python 3.10 and 3.13 on Linux, macOS,
and Windows. It then builds and validates a platform-independent wheel and
source archive, publishes them to TestPyPI through trusted publishing, verifies
that the package installs and runs from TestPyPI, and stages the exact artifacts
in a draft GitHub Release.

After reviewing the successful run, open **Actions**, select **Release**, choose
**Run workflow**, and enter the tag. This explicit manual action verifies and
publishes the staged artifacts to PyPI, then publishes the draft GitHub Release.
A mismatched version and tag, missing draft release, or changed artifact causes
the production run to fail.

Trusted publishers must be configured independently on TestPyPI and PyPI. Both
use owner `martinjose`, repository `modify-csv`, and workflow `release.yml`.
The TestPyPI publisher uses environment `testpypi`; the production publisher
uses environment `pypi`. The manually dispatched production run serves as the
approval gate for private repositories without environment reviewer support.
