|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +This file provides guidance to agents when working with code in this repository. |
| 4 | + |
| 5 | +## Commands |
| 6 | + |
| 7 | +**Run all tests:** |
| 8 | +```bash |
| 9 | +pytest jug/tests/ |
| 10 | +``` |
| 11 | + |
| 12 | +**Run a single test file:** |
| 13 | +```bash |
| 14 | +pytest jug/tests/test_tasks.py |
| 15 | +``` |
| 16 | + |
| 17 | +**Run a single test:** |
| 18 | +```bash |
| 19 | +pytest jug/tests/test_tasks.py::test_function_name |
| 20 | +``` |
| 21 | + |
| 22 | +**Install in development mode (with dev dependencies):** |
| 23 | +```bash |
| 24 | +pip install -e ".[dev]" |
| 25 | +``` |
| 26 | + |
| 27 | +**Build the package:** |
| 28 | +```bash |
| 29 | +python -m build |
| 30 | +``` |
| 31 | + |
| 32 | +## Architecture |
| 33 | + |
| 34 | +Jug is a task-based parallelization framework where a **jugfile** (a Python script) defines tasks, and multiple `jug execute` processes run those tasks concurrently using a shared backend store for coordination and result caching. |
| 35 | + |
| 36 | +### Core concepts |
| 37 | + |
| 38 | +**Task** (`jug/task.py`): The central abstraction. A `Task` wraps a function call with its arguments. Tasks are identified by a content hash of their function + arguments. `task.alltasks` is a module-level list that accumulates all tasks as the jugfile is imported. Tasks are typically created via the `@TaskGenerator` decorator. |
| 39 | + |
| 40 | +**Backends** (`jug/backends/`): Storage and locking layer. `base_store` (abstract) defines the interface: `dump`, `load`, `can_load`, `list`, `lock`, etc. Implementations: |
| 41 | +- `file_store` — filesystem-based (default, NFS-safe). Results stored as files named by task hash. |
| 42 | +- `dict_store` — in-memory (useful for testing). |
| 43 | +- `redis_store` — Redis-based. |
| 44 | + |
| 45 | +Backends also implement **locking** to prevent duplicate execution across workers. `file_store` uses lock files; `redis_store` uses Redis locks. |
| 46 | + |
| 47 | +**Hashing** (`jug/hash.py`): Task identity is determined by hashing the function (by name/module) and all arguments recursively. Objects can implement `__jug_hash__()` for custom hashing. numpy arrays and polars DataFrames have special handling in `file_store` (stored in native format for efficiency). |
| 48 | + |
| 49 | +**Subcommands** (`jug/subcommands/`): Each `jug <cmd>` maps to a subcommand module: `execute`, `status`, `invalidate`, `cleanup`, `count`, `check`, `graph`, `shell`, `webstatus`, `pack`, `demo`. The `execute` subcommand runs the core execution loop. |
| 50 | + |
| 51 | +**Barrier** (`jug/barrier.py`): `barrier()` raises `BarrierError` if any previously defined task isn't complete yet. This stops jugfile parsing at that point, allowing dynamic task graphs where the number of tasks depends on prior results. `bvalue(t)` is a scoped version that only checks one task. |
| 52 | + |
| 53 | +**CompoundTask** (`jug/compound.py`): A task whose function itself returns a Task. Used to create dynamic sub-graphs that are only instantiated after the compound task runs. |
| 54 | + |
| 55 | +**Hooks** (`jug/hooks/`): Event system for extending jug behavior (e.g., logging task execution events). Register with `register_hook(event_name, callback)`. |
| 56 | + |
| 57 | +**Options** (`jug/options.py`): A chained options object where attributes fall through to the next layer. Configuration can come from CLI args, local config files, or defaults. |
| 58 | + |
| 59 | +### Execution flow |
| 60 | + |
| 61 | +1. `jug execute jugfile.py --jugdir jugdata/` is called |
| 62 | +2. `jug.init()` sets up the backend store and imports the jugfile, populating `task.alltasks` |
| 63 | +3. If a `barrier()` is hit before all prior tasks are done, a `BarrierError` is raised and jugfile import stops at that point (the execution loop re-imports once more tasks complete) |
| 64 | +4. The execution loop picks tasks that are ready (all dependencies loadable), acquires a lock, executes, stores result, releases lock |
| 65 | +5. Multiple workers run in parallel — coordination is entirely through the backend store |
| 66 | + |
| 67 | +### Test infrastructure |
| 68 | + |
| 69 | +Tests live in `jug/tests/`. The `tmp_file_store` pytest fixture (in `jug/tests/utils.py`) provides a temporary `file_store` and handles cleanup of `task.Task.store`. Test jugfiles (used as fixtures for integration tests) are in `jug/tests/jugfiles/`. |
0 commit comments