Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ repos:
- id: check-merge-conflict
- id: check-vcs-permalinks
- id: check-yaml
exclude: (^mkdocs\.yml$)
- id: debug-statements
exclude: (debugging\.py)
- id: end-of-file-fixer
Expand Down
1 change: 1 addition & 0 deletions docs/source/_static/md/commands/command-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
| [`clean`](clean.md) | Clean the provided paths by removing files unknown to pytask. |
| [`collect`](collect.md) | Collect tasks and report information about them. |
| [`dag`](dag.md) | Create a visualization of the directed acyclic graph. |
| [`lock`](lock.md) | Inspect and update recorded task state in the lockfile. |
| [`markers`](markers.md) | Show all registered markers. |
| [`profile`](profile.md) | Show information about resource consumption. |
10 changes: 10 additions & 0 deletions docs/source/_static/md/commands/lock-accept-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
| Option | Default | Description |
| ------------------------------ | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `-k, --expression TEXT` | - | Select tasks by expression. |
| `-m, --marker-expression TEXT` | - | Select tasks by marker expression. |
| `--with-ancestors` | `false` | Also include preceding tasks of the selected tasks. |
| `--with-descendants` | `false` | Also include descending tasks of the selected tasks. |
| `--run-on TEXT` | `task_change,dependency_change,product_change` | Choose which kinds of changes are considered when determining whether a task would normally require execution. |
| `--dry-run` | `false` | Show which recorded states would be updated without writing changes. |
| `-y, --yes` | `false` | Apply the changes without prompting for confirmation. |
| `-h, --help` | - | Show this message and exit. |
5 changes: 5 additions & 0 deletions docs/source/_static/md/commands/lock-clean-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
| Option | Default | Description |
| ------------ | ------- | ------------------------------------------------------------------ |
| `--dry-run` | `false` | Show which stale entries would be removed without writing changes. |
| `-y, --yes` | `false` | Apply the changes without prompting for confirmation. |
| `-h, --help` | - | Show this message and exit. |
9 changes: 9 additions & 0 deletions docs/source/_static/md/commands/lock-reset-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
| Option | Default | Description |
| ------------------------------ | ------- | -------------------------------------------------------------------- |
| `-k, --expression TEXT` | - | Select tasks by expression. |
| `-m, --marker-expression TEXT` | - | Select tasks by marker expression. |
| `--with-ancestors` | `false` | Also include preceding tasks of the selected tasks. |
| `--with-descendants` | `false` | Also include descending tasks of the selected tasks. |
| `--dry-run` | `false` | Show which recorded states would be removed without writing changes. |
| `-y, --yes` | `false` | Apply the changes without prompting for confirmation. |
| `-h, --help` | - | Show this message and exit. |
23 changes: 23 additions & 0 deletions docs/source/_static/md/lock-accept-dry-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<div class="termy">

```console

$ pytask lock accept -k train --dry-run
────────────────────────── Start pytask session ─────────────────────────
Platform: win32 -- Python 3.12.0, pytask 0.5.3, pluggy 1.3.0
Root: C:\Users\pytask-dev\git\my_project
Collected 2 tasks.

The following recorded states would be updated:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┓
┃ Task ┃ Reason ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━┩
│ <span class="termynal-dim">task_train.py::</span>task_train_model │ task_change │
│ <span class="termynal-dim">task_train.py::</span>task_evaluate_model │ dependency_change │
└────────────────────────────────────────────────┴──────────────────────┘

<span class="termynal-warning">No changes were written because --dry-run was used.</span>
```

</div>
31 changes: 31 additions & 0 deletions docs/source/_static/md/lock-accept-interactive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<div class="termy">

```console

$ pytask lock accept -k train
────────────────────────── Start pytask session ─────────────────────────
Platform: win32 -- Python 3.12.0, pytask 0.5.3, pluggy 1.3.0
Root: C:\Users\pytask-dev\git\my_project
Collected 2 tasks.

The following recorded states would be updated:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┓
┃ Task ┃ Reason ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━┩
│ <span class="termynal-dim">task_train.py::</span>task_train_model │ task_change │
│ <span class="termynal-dim">task_train.py::</span>task_evaluate_model │ dependency_change │
└────────────────────────────────────────────────┴──────────────────────┘

Accept recorded state for <span class="termynal-dim">task_train.py::</span>task_train_model? Yes

Accept recorded state for <span class="termynal-dim">task_train.py::</span>task_evaluate_model?
Yes
No
❯ Accept all remaining tasks
Quit

<span class="termynal-success">Accepted recorded state for 2 task(s).</span>
```

</div>
30 changes: 30 additions & 0 deletions docs/source/_static/md/lock-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<div class="termy">

```console

$ pytask lock clean
────────────────────────── Start pytask session ─────────────────────────
Platform: win32 -- Python 3.12.0, pytask 0.5.3, pluggy 1.3.0
Root: C:\Users\pytask-dev\git\my_project

The following stale lockfile entries would be removed:

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Lockfile entry ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ <span class="termynal-dim">task_old_pipeline.py::</span>task_train_model │
│ <span class="termynal-dim">task_old_pipeline.py::</span>task_evaluate_model │
└───────────────────────────────────────────────────────────────────────┘

Remove stale entry <span class="termynal-dim">task_old_pipeline.py::</span>task_train_model? Yes

Remove stale entry <span class="termynal-dim">task_old_pipeline.py::</span>task_evaluate_model?
Yes
No
❯ Remove all remaining entries
Quit

<span class="termynal-success">Removed 2 stale lockfile entries.</span>
```

</div>
94 changes: 94 additions & 0 deletions docs/source/commands/lock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# lock

Inspect and update recorded task state in the lockfile.

## Usage

```bash
pytask lock SUBCOMMAND [OPTIONS] [PATHS]
```

Subcommands:

- `accept`: Accept the current state for selected tasks.
- `reset`: Remove recorded state for selected tasks.
- `clean`: Remove stale lockfile entries which no longer correspond to collected tasks.

If no selectors are provided for `accept` or `reset`, the command applies to all
collected tasks in the provided paths.

## Examples

```bash
# Preview which selected tasks would be accepted.
pytask lock accept -k train --dry-run

# Accept the current state for all collected tasks in the current directory.
pytask lock accept --yes

# Also include descendants of the selected tasks.
pytask lock accept -k train --with-descendants

# Restrict the staleness decision to task and dependency changes.
pytask lock accept -k train --run-on task_change,dependency_change

# Reset recorded state for selected tasks.
pytask lock reset -k train --yes

# Remove stale entries from the lockfile.
pytask lock clean --yes
```

## Subcommands

### accept

Accept the current state for selected tasks without executing them.

```bash
pytask lock accept [OPTIONS] [PATHS]
```

Options:

--8<-- "docs/source/_static/md/commands/lock-accept-options.md"

### reset

Remove recorded state for selected tasks.

```bash
pytask lock reset [OPTIONS] [PATHS]
```

Options:

--8<-- "docs/source/_static/md/commands/lock-reset-options.md"

### clean

Remove stale lockfile entries.

```bash
pytask lock clean [OPTIONS] [PATHS]
```

Options:

--8<-- "docs/source/_static/md/commands/lock-clean-options.md"

## Interaction Modes

- Default: Run interactively, show the planned changes, and step through them one by one
with an interactive selector for each task or lockfile entry.
- `--dry-run`: Show the planned changes without writing them.
- `--yes`: Apply the planned changes without prompting.

The options `--dry-run` and `--yes` are mutually exclusive.

## Related

- [Reconciling Lockfile State](../how_to_guides/reconciling_lockfile_state.md)
- [Selecting tasks](../tutorials/selecting_tasks.md)
- [Making Tasks Persist](../tutorials/making_tasks_persist.md)
- [Lockfile](../reference_guides/lockfile.md)
108 changes: 108 additions & 0 deletions docs/source/explanations/execution_policies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Task Execution Model

The task execution model can be confusing because several questions seem to arrive at
once: is the task still current, should it run, and what happens to the recorded state?

It becomes easier to follow once we stop looking at all of these questions at the same
time and instead watch an ordinary task move through pytask.

## The Normal Case

In the normal case, a task passes through four phases: setup, execution, teardown, and
processing the report.

### Setup

Setup is where pytask decides whether a task needs attention.

It checks a few basic questions first.

- Are all dependencies available and have they changed?
- Do the products already exist and have they changed?
- Has the task itself changed?

Generally, change means whether the current state of a node matches the recorded state
in the lockfile. For most filepath-based nodes, the state is the hash of the file
content.

If everything still matches the previously recorded state, the task is considered
unchanged and pytask will not run it.

If something does not match, the task is stale and pytask prepares to run it.

### Execution

Here pytask calls the task function itself.

### Teardown

Once the task function has returned, pytask checks whether the promised products are
really there.

This matters because a task can finish without raising an exception and still leave the
project in an incomplete state. Teardown is where pytask notices that kind of mismatch.

### Processing The Report

After teardown, pytask knows the outcome of the task. In case the task succeeded, this
is also the moment where pytask updates the recorded state for the task.

## Exceptions to the Rule

Not every task follows this path all the way through.

Markers and runtime options can short-circuit parts of the model. A task can be skipped,
persisted, or forced to run even when it would otherwise be left alone.

### Skipping Tasks

Sometimes a task and its subgraph should not run at all. This might be because the tasks
are too costly, buggy, or irrelevant for the current run.

For temporary exclusions, you would use the [`-m`](<>) or [`-k`](<>) options, maybe in
combination with a `not ...` expression.

For a permanent exclusion, you would use the
[`@pytask.mark.skip`](../api/marks.md#pytask.mark.skip) or
[`@pytask.mark.skipif`](../api/marks.md#pytask.mark.skipif) markers.

Skipping hooks into setup because pytask should stop before execution starts. All
dependent tasks are marked to be skipped as well.

### Persisting Tasks

Persisting is useful when a task would normally be considered stale, but rerunning it is
not desirable. A common example is a change to the task file that does not change the
actual result, such as formatting.

Persisting also has to hook into setup because pytask needs to decide early that the
task should not execute even though something changed. Later, when the report is
processed, pytask accepts the current state as the new recorded state.

Users can opt into this behavior with the
[`persist`](../tutorials/making_tasks_persist.md) marker. The disadvantage of the
decorator is that it is permanent and removing it triggers a rerun of the task.

What is missing is a temporary option of the persisting mechanism that would also allow
to integrate new or renamed tasks into an existing project without triggering a rerun.

### Forcing Tasks to Run

Sometimes the opposite is needed: a task should run even though pytask would otherwise
consider it unchanged.

This has to affect setup as well because setup is where pytask normally decides that an
unchanged task can be left alone. Forcing a task to run overrides that decision and lets
the task continue to execution.

Users can request this behavior with the `--force` option.

### Fine-grained Staleness Decision

The current model mostly answers staleness as a yes-or-no question based on recorded
state and the current state of the task, its dependencies, and its products.

Issue [#403](https://github.com/pytask-dev/pytask/issues/403) explores whether this
decision could become more fine-grained. For example, users might want to treat changes
to the task source differently from changes to dependencies or products, or they might
want to override the default decision for one run without changing the task itself.
1 change: 1 addition & 0 deletions docs/source/explanations/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ systems in general as well as its design.

- [Why Pytask](why_pytask.md)
- [Comparison To Other Tools](comparison_to_other_tools.md)
- [Execution Policies](execution_policies.md)
- [Pluggy](pluggy.md)
Loading
Loading