Metadata-Version: 2.3
Name: cronipy
Version: 0.1.11
Summary: Add your description here
Author: lantonic
Author-email: lantonic <luka.olegsson@gmail.com>
Requires-Dist: cyclopts>=5.0
Requires-Python: >=3.12
Project-URL: Homepage, https://codeberg.org/lantonic/cronipy
Project-URL: Repository, https://codeberg.org/lantonic/cronipy
Description-Content-Type: text/markdown

# cronipy

`cronipy` turns Python functions into cron jobs. You decorate a function with a schedule, run one command, and `cronipy` writes the crontab entries for you. 

Rather than reinvent `cron` in Python, `cronipy` uses your system crontab.

The package is in early development, and the interface can change.


## Installation

Requires `cron` installed on your system.

```bash
pip install cronipy
```


## Usage

Define your jobs in a module of your package, e.g. `mypackage.tasks_module`:

```python
from cronipy import Cron

cron = Cron()

@cron.task("*/5 * * * *")
def do_stuff():
    ...
```

Then, from the directory that contains your module, install jobs by running:

```bash
cronipy install
```

And to clean up your `crontab` from jobs added by `cronipy`:

```bash
cronipy uninstall
```

Jobs can also be installed/uninstalled from Python with `Cron().install()`/`Cron().uninstall()`.

For a more complete example contained inside a Python script (e.g. `/some/dir/tasks_module.py`):

```python
import asyncio
import subprocess
from cronipy import Cron

cron = Cron(env={"DISPLAY": ":0.0", "XDG_RUNTIME_DIR": "/run/user/1000"})

@cron.task("*/2 * * * *")
def do_stuff():
    subprocess.run(["notify-send", "cronipied!"])

# cronipy also supports tasks
# defined as async functions
# and the Cron().task decorator can
# override or add specific env vars
@cron.task("*/5 * * * *", env={"DISPLAY": ":0.1"})
async def do_other_stuff():
    await asyncio.create_subprocess_exec("notify-send", "cronipied!")

if __name__ == "__main__":
    cron.install()
```

`cronipy` adds one line per decorated function into the current user's `crontab`. Function output is logged in `logs/<function name>.log`. The example above would generate cron lines as follows:

```cron
*/2 * * * * cd /some/dir && DISPLAY=:0.0 XDG_RUNTIME_DIR=/run/user/1000 PYTHONPATH=/some/dir /some/dir/.venv/bin/python -c "from tasks_module import cron; cron()" do_stuff >> /some/dir/logs/do_stuff.log 2>&1
*/5 * * * * cd /some/dir && DISPLAY=:0.1 XDG_RUNTIME_DIR=/run/user/1000 PYTHONPATH=/some/dir /some/dir/.venv/bin/python -c "from tasks_module import cron; cron()" do_other_stuff >> /some/dir/logs/do_other_stuff.log 2>&1
```

where `/some/dir` contains `tasks_module.py` (and is where the file was executed from). The job working directory and log directory can be modified via arguments to `Cron()`, see [Configuration](#configuration).

## CLI

```bash
$ cronipy install --help
Usage: cronipy install [ARGS]

╭─ Parameters ──────────────────────────────╮
│ PATH --path              [default: ./]    │
│ REPLACE-EXISTING         [default: False] │
│   --replace-existing                      │
│   --no-replace-existing                   │
╰───────────────────────────────────────────╯

$ uv run cronipy uninstall --help
Usage: cronipy uninstall [ARGS]

╭─ Parameters ──────────────────────────────╮
│ PATH --path  [default: ./]                │
╰───────────────────────────────────────────╯
```

The `--path` parameter can also be used to install jobs with more specific targeting:

```bash
# install jobs from all Cron objects in a module
cronipy install mypackage.tasks_module

# install jobs from specific Cron object
cronipy install mypackage.tasks_module:cron

# install jobs from single-file module specified as file path
cronipy install ./tasks_module.py
```

The same argument handling is done with the `cronipy uninstall` command.

To replace your existing crontab with jobs defined through `cronipy`:

> [!WARNING]
> This will remove all jobs currently in your `crontab`

```bash
cronipy install --replace-existing
```


## Configuration

You can pass the following arguments to `Cron`:

- `dry_run`: when true, a job prints its name and does nothing.
- `env`: a dictionary of environment variables added to every crontab line (can also be passed to the `Cron().task` decorator with higher priority).
- `log_dir`: where the log files go, `./logs` by default.
- `work_dir`: the directory each job runs in, `./` by default.
- `python`: the interpreter the jobs use, the one that runs cronipy by default.


## Development

This repository uses [uv](https://docs.astral.sh/uv/). Set up a development environment with `uv sync`, and run the checks with `uv run pytest`.


## License
[BSD 3-Clause](./LICENSE.txt) 
