├── .gitattributes
├── .github
├── CONTRIBUTING.rst
├── FUNDING.yml
├── pull_request_template.md
└── workflows
│ ├── ci.yml
│ └── publish-docs.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── docs
├── __init__.py
├── _static
│ ├── favicon.ico
│ ├── poe.svg
│ └── poe_logo_x2000.png
├── conf.py
├── contributing.rst
├── env_vars.rst
├── global_options.rst
├── guides
│ ├── args_guide.rst
│ ├── composition_guide.rst
│ ├── global_tasks.rst
│ ├── help_guide.rst
│ ├── include_guide.rst
│ ├── index.rst
│ ├── library_guide.rst
│ ├── packaged_tasks.rst
│ ├── running_guide.rst
│ ├── toml_guide.rst
│ └── without_poetry.rst
├── index.rst
├── installation.rst
├── license.rst
├── poetry_plugin.rst
└── tasks
│ ├── index.rst
│ ├── options.rst
│ └── task_types
│ ├── cmd.rst
│ ├── expr.rst
│ ├── ref.rst
│ ├── script.rst
│ ├── sequence.rst
│ ├── shell.rst
│ └── switch.rst
├── poethepoet
├── __init__.py
├── __main__.py
├── __version__.py
├── app.py
├── completion
│ ├── __init__.py
│ ├── bash.py
│ ├── fish.py
│ └── zsh.py
├── config
│ ├── __init__.py
│ ├── config.py
│ ├── file.py
│ ├── partition.py
│ └── primitives.py
├── context.py
├── env
│ ├── __init__.py
│ ├── cache.py
│ ├── manager.py
│ ├── parse.py
│ └── template.py
├── exceptions.py
├── executor
│ ├── __init__.py
│ ├── base.py
│ ├── poetry.py
│ ├── simple.py
│ ├── uv.py
│ └── virtualenv.py
├── helpers
│ ├── __init__.py
│ ├── command
│ │ ├── __init__.py
│ │ ├── ast.py
│ │ └── ast_core.py
│ ├── git.py
│ ├── python.py
│ └── script.py
├── options
│ ├── __init__.py
│ └── annotations.py
├── plugin.py
├── scripts
│ ├── __init__.py
│ └── _rm.py
├── task
│ ├── __init__.py
│ ├── args.py
│ ├── base.py
│ ├── cmd.py
│ ├── expr.py
│ ├── graph.py
│ ├── ref.py
│ ├── script.py
│ ├── sequence.py
│ ├── shell.py
│ └── switch.py
├── ui.py
└── virtualenv.py
├── poetry.lock
├── pyproject.toml
└── tests
├── conftest.py
├── env
└── test_parse_env_file.py
├── fixtures
├── cmds_project
│ └── pyproject.toml
├── conditionals_project
│ └── pyproject.toml
├── custom_config_project
│ ├── tasks.json
│ └── tasks.toml
├── cwd_project
│ ├── pyproject.toml
│ └── subdir
│ │ ├── bar
│ │ └── baz.txt
│ │ └── foo
│ │ └── bar.txt
├── default_value_project
│ └── pyproject.toml
├── envfile_project
│ ├── credentials.env
│ ├── first.env
│ ├── fourth.env
│ ├── multiple_envfiles.toml
│ ├── prod.env
│ ├── pyproject.toml
│ ├── second.env
│ └── third.env
├── example_project
│ ├── dummy_package
│ │ └── __init__.py
│ ├── poetry.lock
│ ├── poetry.toml
│ └── pyproject.toml
├── expr_project
│ └── pyproject.toml
├── graphs_project
│ └── pyproject.toml
├── high_verbosity
│ └── pyproject.toml
├── include_scripts_project
│ ├── README.md
│ ├── envfile
│ ├── pyproject.toml
│ ├── src
│ │ └── include_scripts
│ │ │ └── __init__.py
│ ├── tasks.py
│ └── uv.lock
├── includes_project
│ ├── cross_ref.toml
│ ├── greet.toml
│ ├── laugh.json
│ ├── multiple_includes.toml
│ ├── only_includes.toml
│ ├── pyproject.toml
│ ├── some_vars.env
│ └── sub_git_repo
│ │ ├── .gitignore
│ │ ├── base_env
│ │ ├── base_tasks.toml
│ │ ├── base_tasks2.toml
│ │ └── sub_project
│ │ └── pyproject.toml
├── low_verbosity
│ └── pyproject.toml
├── monorepo_project
│ ├── env1
│ ├── env1t
│ ├── env2
│ ├── env2t
│ ├── pyproject.toml
│ ├── subproject_1
│ │ └── pyproject.toml
│ ├── subproject_2
│ │ ├── extra_tasks.toml
│ │ └── pyproject.toml
│ ├── subproject_3
│ │ ├── env3
│ │ ├── env3t
│ │ └── pyproject.toml
│ └── subproject_4
│ │ ├── env3
│ │ ├── env3t
│ │ ├── exec_dir
│ │ ├── env0
│ │ └── env0t
│ │ └── pyproject.toml
├── packages
│ ├── cowpy-1.1.5-py3-none-any.whl
│ ├── poe_test_helpers
│ │ ├── poe_test_helpers
│ │ │ └── __init__.py
│ │ └── pyproject.toml
│ ├── poe_test_package
│ │ ├── poe_test_package
│ │ │ └── __init__.py
│ │ └── pyproject.toml
│ ├── poetry-1.8.2-py3-none-any.whl
│ └── poetry-2.0.0-py3-none-any.whl
├── poe_tasks_file_project
│ ├── poe_tasks.toml
│ ├── pyproject.toml
│ ├── sub1
│ │ ├── poe_tasks.json
│ │ ├── poe_tasks.toml
│ │ ├── poe_tasks.yaml
│ │ └── pyproject.toml
│ ├── sub2
│ │ ├── poe_tasks.json
│ │ ├── poe_tasks.yaml
│ │ └── pyproject.toml
│ └── sub3
│ │ └── poe_tasks.json
├── poetry_plugin_project
│ ├── empty_prefix
│ │ ├── poetry.lock
│ │ ├── poetry.toml
│ │ ├── pyproject.toml
│ │ └── tasks.toml
│ ├── poetry.lock
│ ├── poetry.toml
│ ├── pyproject.toml
│ └── with_prefix
│ │ ├── poetry.lock
│ │ ├── poetry.toml
│ │ └── pyproject.toml
├── refs_project
│ └── pyproject.toml
├── scripts_project
│ ├── bad_content.toml
│ ├── bad_type.toml
│ ├── pkg
│ │ └── __init__.py
│ ├── poetry.lock
│ └── pyproject.toml
├── sequences_project
│ ├── my_package
│ │ └── __init__.py
│ └── pyproject.toml
├── shells_project
│ ├── bad_interpreter.toml
│ ├── pyproject.toml
│ └── shell_interpreter_config.toml
├── simple_project
│ ├── pyproject.toml
│ └── scripts_pkg
│ │ └── __init__.py
├── switch_project
│ └── pyproject.toml
├── uv_project
│ ├── .gitignore
│ ├── README.md
│ ├── pyproject.toml
│ ├── src
│ │ └── uv_project
│ │ │ └── __init__.py
│ ├── subproject
│ │ ├── pyproject.toml
│ │ ├── src
│ │ │ └── uv_sub_project
│ │ │ │ └── __init__.py
│ │ └── uv.lock
│ └── uv.lock
└── venv_project
│ ├── .gitignore
│ ├── pyproject.toml
│ └── scripts.py
├── helpers
├── command
│ ├── test_ast.py
│ └── test_command_parsing.py
└── conftest.py
├── test_api.py
├── test_cli.py
├── test_cmd_param_expansion.py
├── test_cmd_tasks.py
├── test_default_value.py
├── test_env_config.py
├── test_envfile.py
├── test_executors.py
├── test_expr_task.py
├── test_graph_execution.py
├── test_ignore_fail.py
├── test_include_scripts.py
├── test_includes.py
├── test_poe_config.py
├── test_poe_tasks_file.py
├── test_poetry_plugin_v1.py
├── test_poetry_plugin_v2.py
├── test_ref_task.py
├── test_script_tasks.py
├── test_scripts.py
├── test_sequence_tasks.py
├── test_shell_completion.py
├── test_shell_task.py
├── test_switch_task.py
├── test_uv_executor.py
└── test_version.py
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
3 | *.py text
4 | *.rst text
5 | *.toml text
6 | *.yml text
7 | *.md text
8 |
9 | *.png binary
10 | *.jpg binary
11 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [nat-n]
2 | custom: https://www.buymeacoffee.com/natn
3 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Description of changes
2 |
3 | Replace this text with a description of the changes proposed in this PR, including the motivation for these changes and a reference to any related GitHub issue or discussion.
4 |
5 | ## Pre-merge Checklist
6 |
7 | - [ ] New features (if any) are covered by new feature tests
8 | - [ ] New features (if any) are documented
9 | - [ ] Bug fixes (if any) are accompanied by a test (if not too complicated to do so)
10 | - [ ] `poe check` executed successfully
11 | - [ ] This PR targets the *development* branch for code changes or *main* if only documentation is updated
12 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 | - development
8 | push:
9 | branches:
10 | - main
11 | - development
12 | tags:
13 | - v*
14 |
15 | jobs:
16 |
17 | code-quality:
18 | name: Check coding standards
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: actions/checkout@v4
22 |
23 | - name: Install poetry
24 | run: pipx install poetry
25 |
26 | - uses: actions/setup-python@v5
27 | with:
28 | python-version: 3.12
29 | cache: poetry
30 |
31 | - name: Install dependencies
32 | run: poetry --ansi install
33 |
34 | - name: Check code formatting
35 | run: poetry --ansi run poe style
36 |
37 | - name: Run linter
38 | run: poetry --ansi run poe lint
39 |
40 | - name: Check types
41 | run: poetry --ansi run poe types
42 |
43 | - name: Check rst syntax
44 | run: poetry --ansi run poe docs-check
45 |
46 | run-tests:
47 | name: Run tests
48 | strategy:
49 | matrix:
50 | os: [Ubuntu, MacOS, Windows]
51 | python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
52 |
53 | runs-on: ${{ matrix.os }}-latest
54 | steps:
55 | - uses: actions/checkout@v4
56 |
57 | - name: Install uv
58 | uses: astral-sh/setup-uv@v5
59 |
60 | - name: Install poetry
61 | run: pipx install poetry
62 |
63 | - uses: actions/setup-python@v5
64 | with:
65 | python-version: ${{ matrix.python-version }}
66 | cache: poetry
67 |
68 | - name: Install dependencies
69 | run: poetry --ansi install --without docs
70 |
71 | - name: Run tests
72 | run: poetry --ansi run pytest -v --color=yes
73 |
74 | build:
75 | name: Build distribution
76 | runs-on: ubuntu-latest
77 | needs: [code-quality, run-tests]
78 | steps:
79 | - uses: actions/checkout@v4
80 |
81 | - name: Install poetry
82 | run: pipx install poetry
83 |
84 | - uses: actions/setup-python@v5
85 | with:
86 | python-version: 3.12
87 |
88 | - name: Build package
89 | run: poetry --ansi build
90 |
91 | - name: Store the distribution packages
92 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
93 | uses: actions/upload-artifact@v4
94 | with:
95 | name: python-package-distributions
96 | path: dist/
97 |
98 | pypi-publish:
99 | name: Upload release to PyPI
100 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
101 | needs: [build]
102 | runs-on: ubuntu-latest
103 | environment:
104 | name: pypi
105 | url: https://pypi.org/p/poethepoet
106 | permissions:
107 | id-token: write
108 | steps:
109 | - name: Download all the dists
110 | uses: actions/download-artifact@v4
111 | with:
112 | name: python-package-distributions
113 | path: dist/
114 |
115 | - name: Publish distribution 📦 to PyPI
116 | uses: pypa/gh-action-pypi-publish@release/v1
117 |
118 | homebrew-publish:
119 | name: Upload homebrew formula
120 | needs: [pypi-publish]
121 | runs-on: ubuntu-latest
122 | steps:
123 | - name: Trigger update of homebrew formula
124 | run: |
125 | sleep 10 # some delay seems to be necessary
126 | curl -L -X POST \
127 | -H "Accept: application/vnd.github+json" \
128 | -H "Authorization: Bearer ${{ secrets.homebrew_pat }}" \
129 | -H "X-GitHub-Api-Version: 2022-11-28" \
130 | https://api.github.com/repos/nat-n/homebrew-poethepoet/actions/workflows/71211730/dispatches \
131 | -d '{"ref":"main", "inputs":{}}'
132 |
133 | github-release:
134 | name: >-
135 | Sign the Python 🐍 distribution 📦 with Sigstore and upload them to GitHub Release
136 | needs: [pypi-publish]
137 | runs-on: ubuntu-latest
138 |
139 | permissions:
140 | contents: write
141 | id-token: write
142 |
143 | steps:
144 | - name: Download all the dists
145 | uses: actions/download-artifact@v4
146 | with:
147 | name: python-package-distributions
148 | path: dist/
149 | - name: Sign the dists with Sigstore
150 | uses: sigstore/gh-action-sigstore-python@v3.0.0
151 | with:
152 | inputs: >-
153 | ./dist/*.tar.gz
154 | ./dist/*.whl
155 | - name: Upload artifact signatures to GitHub Release
156 | env:
157 | GITHUB_TOKEN: ${{ github.token }}
158 | # Upload to GitHub Release using the `gh` CLI.
159 | # `dist/` contains the built packages, and the
160 | # sigstore-produced signatures and certificates.
161 | run: >-
162 | gh release upload
163 | '${{ github.ref_name }}' dist/**
164 | --repo '${{ github.repository }}'
165 |
--------------------------------------------------------------------------------
/.github/workflows/publish-docs.yml:
--------------------------------------------------------------------------------
1 | name: Publish documentation website
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - doc/init-sphinx
8 |
9 | jobs:
10 |
11 | publish-docs:
12 | name: Publish docs
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 |
17 | - name: Install poetry
18 | run: pipx install poetry
19 |
20 | - uses: actions/setup-python@v5
21 | with:
22 | python-version: 3.11
23 | cache: poetry
24 |
25 | - name: Install dependencies
26 | run: poetry install --without ci
27 |
28 | - name: Build sphinx documentation website
29 | run: poetry run poe docs
30 |
31 | - name: Publish documentation website to GitHub Pages
32 | uses: peaceiris/actions-gh-pages@v3
33 | with:
34 | github_token: ${{ secrets.GITHUB_TOKEN }}
35 | publish_dir: ./docs/_build
36 | commit_message: ${{ github.event.head_commit.message }}
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | notes/
2 | tests/temp
3 | out.txt
4 |
5 | # Byte-compiled / optimized / DLL files
6 | __pycache__/
7 | *.py[cod]
8 | *$py.class
9 |
10 | # C extensions
11 | *.so
12 |
13 | # Distribution / packaging
14 | .Python
15 | build/
16 | develop-eggs/
17 | dist/
18 | downloads/
19 | eggs/
20 | .eggs/
21 | lib/
22 | lib64/
23 | parts/
24 | sdist/
25 | var/
26 | wheels/
27 | pip-wheel-metadata/
28 | share/python-wheels/
29 | *.egg-info/
30 | .installed.cfg
31 | *.egg
32 | MANIFEST
33 |
34 | # PyInstaller
35 | # Usually these files are written by a python script from a template
36 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
37 | *.manifest
38 | *.spec
39 |
40 | # Installer logs
41 | pip-log.txt
42 | pip-delete-this-directory.txt
43 |
44 | # Unit test / coverage reports
45 | htmlcov/
46 | .tox/
47 | .nox/
48 | .coverage
49 | .coverage.*
50 | .cache
51 | nosetests.xml
52 | coverage.xml
53 | *.cover
54 | *.py,cover
55 | .hypothesis/
56 | .pytest_cache/
57 |
58 | # Sphinx documentation
59 | docs/_build/
60 |
61 | # pyenv
62 | .python-version
63 |
64 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
65 | __pypackages__/
66 |
67 | # mypy
68 | .mypy_cache/
69 | .dmypy.json
70 | dmypy.json
71 |
72 | # Pyre type checker
73 | .pyre/
74 |
75 | # macOS
76 | .DS_Store
77 |
78 | # Misc
79 | /TODO.md
80 | Dockerfile
81 | .vscode/
82 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Nat Noordanus
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Poe the Poet
2 |
3 |
4 |
5 | [](https://pypi.org/project/poethepoet/)
6 | [](https://pypi.org/project/poethepoet/)
7 | [](https://pypistats.org/packages/poethepoet)
8 | [](https://github.com/nat-n/poethepoet/blob/doc/init-sphinx/LICENSE)
9 |
10 | **A batteries included task runner that works well with [poetry](https://python-poetry.org/) or [uv](https://docs.astral.sh/uv/).**
11 |
12 | **[📖 Read the documentation 📖](https://poethepoet.natn.io/)**
13 |
14 |
15 |
16 | ## Features
17 |
18 | - ✅ Straight forward [declaration of project tasks in your pyproject.toml](https://poethepoet.natn.io/tasks/index.html) (or [poe_tasks.toml](https://poethepoet.natn.io/guides/without_poetry.html#usage-without-pyproject-toml))
19 |
20 | - ✅ Tasks are run in poetry or uv's virtualenv ([or another env](https://poethepoet.natn.io/index.html#usage-without-poetry) you specify)
21 |
22 | - ✅ [Shell completion of task names](https://poethepoet.natn.io/installation.html#shell-completion) (and global options too for zsh)
23 |
24 | - ✅ The poe CLI can be used standalone, or as a [plugin for poetry](https://poethepoet.natn.io/poetry_plugin.html)
25 |
26 | - ✅ Tasks can be [commands](https://poethepoet.natn.io/tasks/task_types/cmd.html), [shell scripts](https://poethepoet.natn.io/tasks/task_types/shell.html), [python expressions](https://poethepoet.natn.io/tasks/task_types/expr.html), or references to [python functions](https://poethepoet.natn.io/tasks/task_types/script.html)
27 |
28 | - ✅ Concise commands with extra arguments passed to the task `poe [options] task [task_args]`
29 |
30 | - ✅ Easily [declare named CLI arguments](https://poethepoet.natn.io/guides/args_guide.html) for your tasks
31 |
32 | - ✅ Tasks can specify and [reference environment variables](https://poethepoet.natn.io/tasks/task_types/cmd.html#ref-env-vars), even without a shell
33 |
34 | - ✅ Tasks are [self documenting](https://poethepoet.natn.io/guides/help_guide.html), with optional help messages (just run `poe` with no arguments)
35 |
36 | - ✅ Tasks can be composed into [sequences](https://poethepoet.natn.io/guides/composition_guide.html#composing-tasks-into-sequences) or [DAGs](https://poethepoet.natn.io/guides/composition_guide.html#composing-tasks-into-graphs)
37 |
38 | - ✅ Works with [`.env` files](https://poethepoet.natn.io/tasks/options.html#loading-environment-variables-from-an-env-file)
39 |
40 | - ✅ Can be [used as a library](https://poethepoet.natn.io/guides/library_guide.html) to embed in other tools
41 |
42 | - ✅ Tasks can be [defined in python packages](https://poethepoet.natn.io/guides/packaged_tasks.html) for ease of reuse across projects
43 |
44 | - ✅ Also works fine as a [general purpose task runner](https://poethepoet.natn.io/guides/without_poetry.html)
45 |
46 |
47 | ## Quick start
48 |
49 | 1. Install the Poe the Poet via [pipx](https://pypa.github.io/pipx/) or [another method](https://poethepoet.natn.io/installation.html).
50 |
51 | ```sh
52 | pipx install poethepoet
53 | ```
54 |
55 | 2. Define some tasks in your **pyproject.toml**
56 |
57 | ```toml
58 | [tool.poe.tasks]
59 | test = "pytest --cov=my_app" # a simple command task
60 | serve.script = "my_app.service:run(debug=True)" # python script based task
61 | tunnel.shell = "ssh -N -L 0.0.0.0:8080:$PROD:8080 $PROD &" # (posix) shell based task
62 | ```
63 |
64 | 3. Run your tasks via the CLI
65 |
66 | ```sh
67 | $ poe test -v tests/unit # extra CLI arguments are appended to the underlying command
68 | Poe => pytest --cov=my_app
69 | ...
70 | ```
71 |
72 | If you're using poetry or uv, then poe will automatically use CLI tools and libraries from your project's virtualenv without you having to run `poetry run` / `uv run`
73 |
74 | Poe can also be [used as a general purpose task runner](https://poethepoet.natn.io/guides/without_poetry.html).
75 |
76 | ## Contributing
77 |
78 | There's plenty to do, come say hi in the [discussions](https://github.com/nat-n/poethepoet/discussions) or [open an issue](https://github.com/nat-n/poethepoet/issues)! 👋
79 |
80 | Also check out the [CONTRIBUTING guide](https://github.com/nat-n/poethepoet/blob/main/.github/CONTRIBUTING.rst) 🤓
81 |
82 |
83 | ## License
84 |
85 | [MIT](https://github.com/nat-n/poethepoet/blob/main/LICENSE)
86 |
--------------------------------------------------------------------------------
/docs/__init__.py:
--------------------------------------------------------------------------------
1 | def serve(docs_dir: str = ".", delay: int = 1):
2 | from livereload import Server, shell
3 |
4 | build_dir = f"{docs_dir}/_build"
5 | build_cmd = f"sphinx-build {docs_dir} {build_dir}"
6 | server = Server()
7 | server.watch("**/*.rst", shell(build_cmd), delay=delay)
8 | server.watch("**/*.md", shell(build_cmd), delay=delay)
9 | server.watch("**/*.py", shell(build_cmd), delay=delay)
10 | server.watch("_static/*", shell(build_cmd), delay=delay)
11 | server.watch("_templates/*", shell(build_cmd), delay=delay)
12 | server.watch("../poethepoet/**/*", shell(build_cmd), delay=delay)
13 | server.serve(root=f"{build_dir}")
14 |
--------------------------------------------------------------------------------
/docs/_static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nat-n/poethepoet/4f10e2588c4dd8820befc0c3655036ac1e230a2e/docs/_static/favicon.ico
--------------------------------------------------------------------------------
/docs/_static/poe_logo_x2000.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nat-n/poethepoet/4f10e2588c4dd8820befc0c3655036ac1e230a2e/docs/_static/poe_logo_x2000.png
--------------------------------------------------------------------------------
/docs/contributing.rst:
--------------------------------------------------------------------------------
1 | Contributing
2 | ============
3 |
4 | .. include:: ../.github/CONTRIBUTING.rst
5 |
--------------------------------------------------------------------------------
/docs/env_vars.rst:
--------------------------------------------------------------------------------
1 | Environment variables
2 | =====================
3 |
4 | Internal Environment variables
5 | ------------------------------
6 |
7 | The following environment variables are used by Poe the Poet internally, and can be accessed from within configuration and tasks.
8 |
9 | - ``POE_ROOT``: path to the parent directory of the main tasks file (e.g. pyproject.toml).
10 | - ``POE_PWD``: the current working directory of the poe process (unless overridden programmatically).
11 | - ``POE_CONF_DIR``: the path to the parent directory of the config file that defines the running task or the :ref:`cwd option` set when including that config.
12 | - ``POE_ACTIVE``: identifies the active PoeExecutor, so that Poe the Poet can tell when it is running recursively.
13 | - ``POE_VERBOSITY``: reflects the current verbosity level. Normally 0 is the default, 1 means more verbose and -1 means less.
14 |
15 | Special variables
16 | -----------------
17 |
18 | The following variables are not set on the environment by default but can be referenced from task configuration as if they were.
19 |
20 | - ``POE_GIT_DIR``: path of the git repo that the project is part of. This allows a project in a subdirectory of a monorepo to reference :ref:`includes` or :ref:`envfiles` relative to the root of the git repo. Note that referencing this variable causes poe to attempt to call the ``git`` executable which must be available on the path.
21 |
22 | - ``POE_GIT_ROOT``: just like ``POE_GIT_DIR`` except that if the project is in a git submodule, then the path will point to the working directory of the main repo above it.
23 |
24 | External Environment variables
25 | ------------------------------
26 |
27 | The following environment variables can be set to modify Poe the Poet's behavior.
28 |
29 | - ``POE_PROJECT_DIR``: used as the default value for the ``--directory`` global argument.
30 | - ``NO_COLOR``: disables ansi colors in output (unless the --ansi argument is provided).
31 | - ``POE_DEBUG``: can be set to ``1`` to enable printing debug messages to stdout.
32 |
--------------------------------------------------------------------------------
/docs/guides/composition_guide.rst:
--------------------------------------------------------------------------------
1 | Composing tasks
2 | ===============
3 |
4 | .. _sequence_composition:
5 |
6 | Composing tasks into sequences
7 | ------------------------------
8 |
9 | You can compose tasks into a new task that runs them in order by declaring a :doc:`sequence<../tasks/task_types/sequence>` task.
10 |
11 | For example:
12 |
13 | .. code-block:: toml
14 |
15 | [tool.poe.tasks]
16 |
17 | _publish = "poetry publish"
18 | release = [
19 | { cmd = "pytest --cov=src" },
20 | { script = "devtasks:build" },
21 | { ref = "_publish" },
22 | ]
23 |
24 | And here's an example that uses the *sequence* task type explicitly in a task definition:
25 |
26 | .. code-block:: toml
27 |
28 | [tool.poe.tasks._publish]
29 | cmd = "poetry publish"
30 |
31 | [tool.poe.tasks.release]
32 | sequence = [
33 | { cmd = "pytest --cov=src" },
34 | { script = "devtasks:build" },
35 | { ref = "_publish" }
36 | ]
37 |
38 | .. seealso::
39 |
40 | See :doc:`sequence tasks<../tasks/task_types/sequence>` specifics for more information on the :code:`sequence` task type.
41 |
42 | .. _graph_composition:
43 |
44 | Composing tasks into graphs
45 | ---------------------------
46 |
47 | You can define tasks that depend on other tasks, and optionally capture and reuse the output of those tasks, thus defining an execution graph of tasks. This is done by using the ``deps`` task option, or if you want to capture the output of the upstream task to pass it to the present task then specify the ``uses`` option, as demonstrated below.
48 |
49 | .. code-block:: toml
50 |
51 | [tool.poe.tasks]
52 | _website_bucket_name.shell = """
53 | aws cloudformation describe-stacks \
54 | --stack-name $AWS_SAM_STACK_NAME \
55 | --query "Stacks[0].Outputs[?(@.OutputKey == 'FrontendS3Bucket')].OutputValue" \
56 | | jq -cr 'select(0)[0]'
57 | """
58 |
59 | [tool.poe.tasks.build-backend]
60 | help = "Build the backend"
61 | sequence = [
62 | {cmd = "poetry export -f requirements.txt --output src/requirements.txt"},
63 | {cmd = "sam build"},
64 | ]
65 |
66 | [tool.poe.tasks.build-frontend]
67 | help = "Build the frontend"
68 | cmd = "npm --prefix client run build"
69 |
70 | [tool.poe.tasks.shipit]
71 | help = "Build and deploy the app"
72 | sequence = [
73 | "sam deploy --config-env $SAM_ENV_NAME",
74 | "aws s3 sync --delete ./client/build s3://${BUCKET_NAME}"
75 | ]
76 | default_item_type = "cmd"
77 | deps = ["build-frontend", "build-backend"]
78 | uses = { BUCKET_NAME = "_website_bucket_name" }
79 |
80 |
81 | In this example the :code:`shipit` task depends on the :code:`build-frontend` :code:`build-backend`, which means that these tasks get executed before the :code:`shipit` task. It also declares that it uses the output of the hidden :code:`_website_bucket_name` task, which means that this also gets executed, but its output it captured and then made available to the :code:`shipit` task as the environment variable :code:`BUCKET_NAME`.
82 |
83 | .. important::
84 |
85 | Note that captured output that is exposed as an environment variable via the ``uses`` is compacted to have new lines removed. This is similar to how interpolated command output is treated by bash.
86 |
--------------------------------------------------------------------------------
/docs/guides/global_tasks.rst:
--------------------------------------------------------------------------------
1 | Global tasks
2 | ============
3 |
4 | This guide covers how to use poethepoet as a global task runner, for private user level tasks instead of shared project level tasks. Global tasks are available anywhere, and serve a similar purpose to shell aliases or scripts on the ``PATH`` — but as poe tasks.
5 |
6 | There are two steps required to make this work:
7 |
8 | 1. Create a project somewhere central such as ``~/.poethepoet`` where you define tasks that you want to have globally accessible
9 | 2. Configure an alias in your shell's startup script such as ``alias edgar="poe -C ~/.poethepoet"``.
10 |
11 | .. tip::
12 |
13 | This document suggests calling your alias `edgar` — because it's a pun... but you can use any alternative name you fancy.
14 |
15 | The project at ``~/.poethepoet`` can be a regular poetry project including dependencies or just a file with tasks.
16 |
17 | You can choose any location to define the tasks, and whatever name you like for the global poe alias.
18 |
19 | .. warning::
20 |
21 | For this to work Poe the Poet must be installed globally such as via pipx or homebrew.
22 |
23 |
24 | Shell completions for global tasks
25 | ----------------------------------
26 |
27 | If you uze zsh or fish then the usual completion script should just work with your alias (as long as it was created with poethepoet >=0.28.0).
28 |
29 | However for bash you'll need to generate a new completion script for the alias specifying the alias and the path to you global tasks like so:
30 |
31 | .. code-block:: bash
32 |
33 | # System bash
34 | poe _bash_completion edgar ~/.poethepoet > /etc/bash_completion.d/edgar.bash-completion
35 |
36 | # Homebrew bash
37 | poe _bash_completion edgar ~/.poethepoet > $(brew --prefix)/etc/bash_completion.d/edgar.bash-completion
38 |
39 | .. note::
40 |
41 | These examples assume your global poe alias is ``edgar``, and your global tasks live at ``~/.poethepoet``.
42 |
43 | How to ensure installed bash completions are enabled may vary depending on your system.
44 |
45 |
46 |
--------------------------------------------------------------------------------
/docs/guides/help_guide.rst:
--------------------------------------------------------------------------------
1 | Documenting tasks
2 | =================
3 |
4 | You can add help text to your tasks by adding the ``help`` option to the task definition, like so:
5 |
6 | .. code-block:: toml
7 |
8 | [tool.poe.tasks.test]
9 | help = "Run the test suite"
10 | cmd = "pytest --cov=poethepoet"
11 |
12 | [tool.poe.tasks.serve]
13 | help = "Run the app in debug mode"
14 | script = "my_app.service:run(debug=True)"
15 |
16 | [tool.poe.tasks.tunnel]
17 | help = "Create an SSH tunnel to the production server"
18 | shell = "ssh -N -L 0.0.0.0:8080:$prod_host:8080 $prod_host &"
19 | args = [
20 | {name = "prod_host", help = "Hostname of the production server", default = "myapp.com"}
21 | ]
22 |
23 | This help text will be displayed alongside the task name in the list of configured tasks when ``poe`` is run without specifying a task.
24 |
25 | .. code-block:: docs
26 |
27 | $ poe --help
28 | Poe the Poet - A task runner that works well with poetry.
29 | version 0.25.1
30 |
31 | Usage:
32 | poe [global options] task [task arguments]
33 |
34 | Global options:
35 | -h [TASK], --help [TASK]
36 | Show this help page and exit, optionally supply a task.
37 | --version Print the version and exit
38 | -v, --verbose Increase command output (repeatable)
39 | -q, --quiet Decrease command output (repeatable)
40 | -d, --dry-run Print the task contents but don't actually run it
41 | -C PATH, --directory PATH
42 | Specify where to find the pyproject.toml
43 | -e EXECUTOR, --executor EXECUTOR
44 | Override the default task executor
45 | --ansi Force enable ANSI output
46 | --no-ansi Force disable ANSI output
47 |
48 | Configured tasks:
49 | test Run the test suite
50 | serve Run the app in debug mode
51 | tunnel Create an SSH tunnel to the production server
52 | --prod_host Hostname of the production server [default: myapp.com]
53 |
54 | Display help for a single task
55 | ------------------------------
56 |
57 | Passing the ``--help`` option normally has the same effect as running poe with no arguments. However you can also supply the name of a task to display documentation for just that task.
58 |
59 | .. code-block:: docs
60 |
61 | $ poe --help tunnel
62 |
63 | Description:
64 | Create an SSH tunnel to the production server
65 |
66 | Usage:
67 | poe [global options] tunnel [named arguments] -- [free arguments]
68 |
69 | Named arguments:
70 | --prod_host Hostname of the production server [default: myapp.com]
71 |
--------------------------------------------------------------------------------
/docs/guides/include_guide.rst:
--------------------------------------------------------------------------------
1 | Loading tasks from another file
2 | ===============================
3 |
4 | There are some scenarios where one might wish to define tasks outside of pyproject.toml, or to collect tasks from multiple projects into one. For example, if you want to share tasks between projects via git modules, generate tasks definitions dynamically, organise your code in a monorepo, or simply have a lot of tasks and don't want the pyproject.toml to get too large. This can be achieved by creating a toml, yaml, or json file including the same structure for tasks as used in pyproject.toml
5 |
6 | .. tip::
7 |
8 | Imported toml, yaml, or json files are not required to namespace config under ``tool.poe``. However if config exists under this structure then it will be used.
9 |
10 | For example:
11 |
12 | .. code-block:: toml
13 | :caption: ./pyproject.toml
14 |
15 | [tool.poe]
16 | include = "modules/acme_common/shared_tasks.toml" # include tasks from a git submodule
17 |
18 | .. code-block:: toml
19 | :caption: ./modules/acme_common/shared_tasks.toml
20 |
21 | [tool.poe.tasks.build-image]
22 | cmd = "docker build"
23 |
24 | Imported files may also specify environment variables via
25 | ``tool.poe.envfile`` or entries for ``tool.poe.env``.
26 |
27 | .. warning::
28 |
29 | If a referenced file is missing then poe ignores it without error, though failure to read the contents will result in failure.
30 |
31 |
32 | Including tasks from a python package
33 | -------------------------------------
34 |
35 | You can also include tasks from a python function originating either within the current project or from a dependency. This makes it much easier to share tasks across projects by distributing them as a python package, or to dynamically generate tasks depending on the context.
36 |
37 | For more details see the :doc:`include_scripts<../guides/packaged_tasks>` global option.
38 |
39 |
40 | Including multiple files
41 | ------------------------
42 |
43 | It's also possible to include tasks from multiple files by providing a list like so:
44 |
45 | .. code-block:: toml
46 |
47 | [tool.poe]
48 | include = ["modules/acme_common/shared_tasks.toml", "generated_tasks.json"]
49 |
50 | Files are loaded in the order specified. If an item already exists then the included value is ignored.
51 |
52 | If an included task file itself includes other files, these second order includes are **not inherited**, so circular includes are not a concern.
53 |
54 |
55 | Setting a working directory for included tasks
56 | ----------------------------------------------
57 |
58 | When including files from another location, you can also specify that tasks from that other file should be run from within a specific directory. For example with the following configuration, when tasks imported from *my_subproject* are run from the root, the task will actually execute as if it had been run from the *my_subproject* subdirectory.
59 |
60 | .. code-block:: toml
61 |
62 | [[tool.poe.include]]
63 | path = "my_subproject/pyproject.toml"
64 | cwd = "my_subproject"
65 |
66 | The directory indicated by the ``cwd`` option will also be used as the base directory for global or task level ``envfile`` imports for tasks defined within an included file.
67 |
68 | Tasks and config in an included file can access the ``cwd`` value via the ``POE_CONF_DIR`` environment variable. When no ``cwd`` is set on the include then ``POE_CONF_DIR`` refers the to the parent directory of the config file where a task is defined.
69 |
70 | You can still specify that an envfile referenced within an included file should be imported relative to the main project root, using the ``POE_ROOT`` environment variable like so:
71 |
72 | .. code-block:: toml
73 |
74 | [tool.poe]
75 | envfile = "${POE_ROOT}/.env"
76 |
77 |
78 | Including files relative to the git repo
79 | ----------------------------------------
80 |
81 | Normally include paths are resolved relative to the project root (that is the parent directory of the pyproject.toml). However when working with a monorepo it can also be useful to specify the file to include relative to the root of the git repository, which can be done by referenceing the ``POE_GIT_DIR`` or ``POE_GIT_ROOT`` variables like so:
82 |
83 | .. code-block:: toml
84 |
85 | [tool.poe]
86 | include = "${POE_GIT_DIR}/tasks.toml"
87 |
88 | See the documentation on :ref:`Special variables` for a full explanation of how these variables work.
89 |
--------------------------------------------------------------------------------
/docs/guides/index.rst:
--------------------------------------------------------------------------------
1 | Guides
2 | ======
3 |
4 | This section contains guides for using the various features of Poe the Poet.
5 |
6 | .. toctree::
7 | :maxdepth: 1
8 |
9 | running_guide
10 | help_guide
11 | args_guide
12 | composition_guide
13 | include_guide
14 | packaged_tasks
15 | global_tasks
16 | without_poetry
17 | library_guide
18 |
--------------------------------------------------------------------------------
/docs/guides/library_guide.rst:
--------------------------------------------------------------------------------
1 | Using poethepoet as a library
2 | =============================
3 |
4 | Normally poethepoet would be installed as a tool or poetry plugin, but it can also be used as a library to embed task runner capabilities into another tool.
5 |
6 |
7 | The following script replicates the main functionality of the `poe` standalone cli.
8 |
9 | .. code-block:: python
10 |
11 | import sys
12 |
13 | from poethepoet.app import PoeThePoet
14 |
15 |
16 | if __name__ == "__main__":
17 | app = PoeThePoet()
18 | result = app(cli_args=sys.argv[1:])
19 | if result:
20 | sys.exit(result)
21 |
22 | The `PoeThePoet `_ class accepts various optional arguments to customize its behavior as described below.
23 |
24 | .. autoclass:: poethepoet.app.PoeThePoet
25 |
--------------------------------------------------------------------------------
/docs/guides/running_guide.rst:
--------------------------------------------------------------------------------
1 | Running tasks
2 | =============
3 |
4 | An example task
5 | ---------------
6 |
7 | Poe tasks are defined in your **pyproject.toml** file under ``tool.poe.tasks``
8 |
9 | The following toml example defines a task called `test` that consists of the associated command.
10 |
11 | .. code-block:: toml
12 |
13 | [tool.poe.tasks]
14 | test = "pytest --cov=poethepoet"
15 |
16 | This task can the be run via the poe cli as ``poe test``.
17 |
18 | .. hint::
19 |
20 | If your pyproject defines pytest as a dependency with poetry, then poe will run the task with pytest from the poetry managed virtualenv, so you don't need to explicitly activate the virtualenv via ``poetry shell`` or ``poetry run``.
21 |
22 |
23 | `Click here for a real example `_.
24 |
25 |
26 | Run a task with the poe CLI
27 | ---------------------------
28 |
29 | The preferred way to run poe is via the standalone CLI.
30 |
31 | .. code-block:: sh
32 |
33 | poe test
34 |
35 | The above command can only be ran if you've :doc:`installed Poe globally<../installation>`, or if you've sourced the venv that Poe the Poet is installed in (e.g. using ``poetry shell``).
36 |
37 |
38 | Running Poe as a Python module
39 | ---------------------------------------
40 |
41 | You can also run poe as a python module
42 |
43 | .. code-block:: sh
44 |
45 | python -m poethepoet [options] test [task_args]
46 |
47 |
48 | Running Poe as a Poetry plugin
49 | ------------------------------
50 | If you've installed it as a :doc:`poetry plugin<../poetry_plugin>` (for poetry >= 1.2), you can run it like so
51 |
52 | .. code-block:: sh
53 |
54 | poetry self add poethepoet[poetry_plugin]
55 | poetry poe [options] test [task_args]
56 |
57 |
58 | Running Poe as a Poetry dependency
59 | ----------------------------------
60 | If you've installed it as a dev dependency with poetry, you can run it like so
61 |
62 | .. code-block:: sh
63 |
64 | poetry add --group dev poethepoet
65 | poetry run poe [options] test [task_args]
66 |
67 |
68 | .. hint::
69 | In this case you might want create an alias like :sh:`alias poe='poetry run poe'`.
70 |
71 |
72 | Passing arguments
73 | -----------------
74 |
75 | By default additional arguments are passed to the task so
76 |
77 | .. code-block:: sh
78 |
79 | poe test -v tests/favorite_test.py
80 |
81 | will result in the following being run inside poetry's virtualenv
82 |
83 | .. code-block:: sh
84 |
85 | pytest --cov=poethepoet -v tests/favorite_test.py
86 |
87 |
--------------------------------------------------------------------------------
/docs/guides/toml_guide.rst:
--------------------------------------------------------------------------------
1 | Using toml syntax
2 | =================
3 |
4 | TODO: Explain different ways of leveraging toml syntax to define tasks.
5 |
--------------------------------------------------------------------------------
/docs/guides/without_poetry.rst:
--------------------------------------------------------------------------------
1 | Usage without poetry
2 | ====================
3 |
4 | Poe the Poet was originally intended as the missing task runner for |poetry_link|. But it also works great with |uv_link|, or any other kind of virtualenv, or as a general purpose task runner! This behaviour is configurable via the :ref:`tool.poe.executor global option`.
5 |
6 | By default poe will run tasks in your project's virtual environment. Poe will deduce what kind of project is present by detecting tool specific tables in ``pyproject.toml`` such as :toml:`tool.poetry`, lock files such as ``uv.lock``, or a virtualenv at ``./.venv`` or ``./venv`` relative to the ``pyproject.toml``. If no virtualenv is found then poe will run tasks without any special environment management.
7 |
8 | Poe the Poet can also be used as a :doc:`global task runner<./global_tasks>`, scoped to your system rather than any particular project, like shell aliases by sweeter.
9 |
10 |
11 | Usage with uv
12 | -------------
13 |
14 | |uv_link| is another popular tool for managing project dependencies with a ``pyproject.toml`` file. If the ``pyproject.toml`` contains a ``[tool.uv]`` table, or the project directory contains a ``uv.lock`` files then Poe the Poet will recognise this and activate the UvExecutor to execute tasks using ``uv run``.
15 |
16 | In case your uv project doesn't have a lockfile or any uv specific config, you can still force poe to use the uv executor like so:
17 |
18 | .. code-block:: toml
19 |
20 | [tool.poe]
21 | executor.type = "uv"
22 |
23 | So Poe the Poet also works well with uv.
24 |
25 |
26 | Usage without pyproject.toml
27 | ----------------------------
28 |
29 | When using Poe the Poet outside of a poetry (or other |pep518_link|) project, you can avoid the potential confusion of creating a ``pyproject.toml`` file and instead name the file ``poe_tasks.toml``.
30 |
31 |
32 | Usage with with json or yaml instead of toml
33 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
34 |
35 | As an alternative to toml, poethepoet configuration can also be provided via yaml or json files. When searching for a tasks file to load within a certain directory poe will try the following file names in order:
36 |
37 | - pyproject.toml
38 | - poe_tasks.toml
39 | - poe_tasks.yaml
40 | - poe_tasks.json
41 |
42 | If ``pyproject.toml`` exists but does not contain the key prefix ``tool.poe`` then the search continues with `poe_tasks.toml`. If one of the listed ``poe_tasks.*`` files exist then the search is terminated, even if the file is empty.
43 |
44 | When config is loaded from a file other than ``pyproject.toml`` the ``tool.poe`` namespace for poe config is optional. So for example the following two ``poe_tasks.yaml`` files are equivalent and both valid:
45 |
46 | .. code-block:: yaml
47 | :caption: poe_tasks.yaml
48 |
49 | env:
50 | VAR0: FOO
51 |
52 | tasks:
53 | show-vars:
54 | cmd: "echo $VAR0 $VAR1 $VAR2"
55 | env:
56 | VAR1: BAR
57 | args:
58 | - name: VAR2
59 | options: ["--var"]
60 | default: BAZ
61 |
62 | .. code-block:: yaml
63 | :caption: poe_tasks.yaml
64 |
65 | tool:
66 | poe:
67 | env:
68 | VAR0: FOO
69 |
70 | tasks:
71 | show-vars:
72 | cmd: "echo $VAR0 $VAR1 $VAR2"
73 | env:
74 | VAR1: BAR
75 | args:
76 | - name: VAR2
77 | options: ["--var"]
78 | default: BAZ
79 |
80 | .. |uv_link| raw:: html
81 |
82 | uv
83 |
84 | .. |poetry_link| raw:: html
85 |
86 | poetry
87 |
88 | .. |pep518_link| raw:: html
89 |
90 | PEP 518
91 |
--------------------------------------------------------------------------------
/docs/installation.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 | Installing Poe the Poet
5 | -----------------------
6 |
7 | There are a few ways to install Poe the Poet depending on your preferences.
8 |
9 | 1. Install the CLI globally *(recommended)*
10 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11 |
12 | The following methods will make the ``poe`` executable available anywhere on your system.
13 |
14 | With |pipx_link|
15 | """"""""""""""""
16 |
17 | .. code-block:: sh
18 |
19 | pipx install poethepoet
20 |
21 |
22 | With |uv_link|
23 | """"""""""""""
24 |
25 | .. code-block:: sh
26 |
27 | uv tool install poethepoet
28 |
29 | With |brew_link|
30 | """"""""""""""""
31 |
32 | .. code-block:: sh
33 |
34 | brew tap nat-n/poethepoet
35 | brew install nat-n/poethepoet/poethepoet
36 |
37 | See the |formula_link|.
38 |
39 | With pip
40 | """"""""
41 |
42 | Of course you can also install it with pip – assuming your current python environment is global.
43 |
44 | .. code-block:: sh
45 |
46 | pip install poethepoet
47 |
48 | 2. Install poethepoet as a :doc:`poetry plugin`
49 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
50 |
51 | You can install the poethepoet poetry plugin globally like so:
52 |
53 | .. code-block:: sh
54 |
55 | poetry self add 'poethepoet[poetry_plugin]'
56 |
57 | Or add it to poetry on a per project basis by adding the following to your *pyproject.toml*:
58 |
59 | .. code-block:: sh
60 |
61 | [tool.poetry.requires-plugins]
62 | poethepoet = { version = "~0.34.0", extras = ["poetry_plugin"]}
63 |
64 | See the |poetry_plugin_link| for more installation options, or see the :doc:`poetry plugin docs ` for more details about this option.
65 |
66 |
67 | 3. Install poethepoet into your project
68 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
69 |
70 | With poetry
71 | """""""""""
72 |
73 | .. code-block:: sh
74 | :caption: Add poethepoet as a dev dependency
75 |
76 | poetry add --group dev poethepoet
77 |
78 | .. code-block:: sh
79 | :caption: And run it
80 |
81 | poetry run poe
82 |
83 | With uv
84 | """""""
85 |
86 | .. code-block:: sh
87 | :caption: Add poethepoet as a dev dependency
88 |
89 | uv add --dev poethepoet
90 |
91 | .. code-block:: sh
92 | :caption: And run it
93 |
94 | uv run poe
95 |
96 | .. _shell_completion:
97 |
98 | .. tip::
99 |
100 | If you prefer *not* to install the poe CLI globally, then you can still benefit from shell completions if you create an appropriate alias in your shell, such as one of:
101 |
102 | - :sh:`alias poe="poetry run poe"`
103 | - :sh:`alias poe="poetry poe"`
104 | - :sh:`alias poe="uv run poe"`
105 |
106 | Enable tab completion for your shell
107 | ------------------------------------
108 |
109 | Poe comes with tab completion scripts for bash, zsh, and fish to save you keystrokes.
110 | How to install them will depend on your shell setup.
111 |
112 |
113 | Zsh
114 | ~~~
115 |
116 | .. code-block:: zsh
117 |
118 | # oh-my-zsh
119 | mkdir -p ~/.oh-my-zsh/completions
120 | poe _zsh_completion > ~/.oh-my-zsh/completions/_poe
121 |
122 | # without oh-my-zsh
123 | mkdir -p ~/.zfunc/
124 | poe _zsh_completion > ~/.zfunc/_poe
125 |
126 | .. tip::
127 |
128 | You'll need to start a new shell for the new completion script to be loaded. If it still doesn't work try adding a call to :sh:`compinit` to the end of your zshrc file.
129 |
130 |
131 | Bash
132 | ~~~~
133 |
134 | .. code-block:: bash
135 |
136 | # System bash
137 | poe _bash_completion > /etc/bash_completion.d/poe.bash-completion
138 |
139 | # Homebrew bash
140 | poe _bash_completion > $(brew --prefix)/etc/bash_completion.d/poe.bash-completion
141 |
142 |
143 | How to ensure installed bash completions are enabled may vary depending on your system.
144 |
145 |
146 | Fish
147 | ~~~~
148 |
149 | .. code-block:: fish
150 |
151 | # Fish
152 | poe _fish_completion > ~/.config/fish/completions/poe.fish
153 |
154 | # Homebrew fish
155 | poe _fish_completion > (brew --prefix)/share/fish/vendor_completions.d/poe.fish
156 |
157 |
158 | Supported python versions
159 | -------------------------
160 |
161 | Poe the Poet officially supports python >=3.9, and is tested with python 3.9 to 3.13 on
162 | macOS, linux and windows.
163 |
164 |
165 | .. |pipx_link| raw:: html
166 |
167 | pipx
168 |
169 | .. |uv_link| raw:: html
170 |
171 | uv
172 |
173 | .. |brew_link| raw:: html
174 |
175 | homebrew
176 |
177 | .. |formula_link| raw:: html
178 |
179 | homebrew formula
180 |
181 | .. |poetry_plugin_link| raw:: html
182 |
183 | poetry docs
184 |
185 |
--------------------------------------------------------------------------------
/docs/license.rst:
--------------------------------------------------------------------------------
1 | Licence
2 | =======
3 |
4 | .. include:: ../LICENSE
5 |
--------------------------------------------------------------------------------
/docs/tasks/index.rst:
--------------------------------------------------------------------------------
1 | Defining tasks
2 | ==============
3 |
4 | Poe the Poet supports several ways of defining tasks under :toml:`[tool.poe.tasks]`, trading off simplicity against configurability. Furthermore toml syntax supports both more terse or more expressive alternative ways of writing the same thing, see the guide on :doc:`../guides/toml_guide` for details.
5 |
6 | A task defined as a string will by default be interpreted as a single command to be executed without a shell (aka a :doc:`cmd task `).
7 |
8 | .. code-block:: toml
9 |
10 | [tool.poe.tasks]
11 | test = "pytest"
12 |
13 | A task defined as an array will by default be interpreted as a :doc:`sequence ` of :doc:`references` to other tasks.
14 |
15 | .. code-block:: toml
16 |
17 | [tool.poe.tasks]
18 | test = "pytest"
19 | _build = "poetry build"
20 | build = ["test", "_build"] # this task runs the two referenced tasks in sequence
21 |
22 | .. important::
23 |
24 | Task with names starting with an underscore ``_`` are excluded from documentation and cannot be run directly from the command line. They can only be run when referenced by another task.
25 |
26 | Tasks can also be defined as sub-tables, which allows for specifying the task type and various configuration options on the task. The type of a task defined as a table is determined by the presence of a particular key that is unique to a certain task type and corresponds to the name of the task type.
27 |
28 | .. code-block:: toml
29 |
30 | [tool.poe.tasks.test-quick]
31 | help = "Run tests excluding those makes as slow."
32 | cmd = "pytest -m \"not slow\"" # here the cmd key identifies the task type and content
33 |
34 | This implies that you can also define tasks of other types on a single line, like so:
35 |
36 | .. code-block:: toml
37 |
38 | [tool.poe.tasks]
39 | tunnel.shell = "ssh -N -L 0.0.0.0:8080:$PROD:8080 $PROD &" } # (posix) shell based task
40 |
41 | :doc:`Some options ` are applicable to all tasks, whereas other are only applicable to :ref:`specific types of tasks`.
42 |
43 | .. seealso::
44 |
45 | Top level tasks are defined as members of :toml:`[tool.poe.tasks]`, but sometimes tasks can be defined as children of other tasks, for example as items within a :doc:`sequence ` task, or as the ``control`` or ``case`` roles with a :doc:`switch ` task.
46 |
47 | Types of task
48 | -------------
49 |
50 | You can define seven different types of task:
51 |
52 | - :doc:`Command tasks ` :code:`cmd` : for simple commands that are executed as a subprocess without a shell
53 |
54 | - :doc:`Script tasks` :code:`script` : for python function calls
55 |
56 | - :doc:`Shell tasks` :code:`shell` : for scripts to be executed with via an external interpreter (such as sh).
57 |
58 | - :doc:`Sequence tasks` :code:`sequence` : for composing multiple tasks into a sequence
59 |
60 | - :doc:`Expression tasks` :code:`expr` : which consist of a python expression to evaluate
61 |
62 | - :doc:`Switch tasks` :code:`switch` : for running different tasks depending on a control value (such as the platform)
63 |
64 | - :doc:`Reference tasks` :code:`ref` : for defining a task as an alias of another task, such as in a sequence task.
65 |
66 |
67 | .. toctree::
68 | :hidden:
69 |
70 | Standard task options
71 | Command tasks
72 | Script tasks
73 | Shell tasks
74 | Sequence tasks
75 | Expression tasks
76 | Switch tasks
77 | Reference tasks
78 |
--------------------------------------------------------------------------------
/docs/tasks/task_types/cmd.rst:
--------------------------------------------------------------------------------
1 | ``cmd`` tasks
2 | =============
3 |
4 | **Command tasks** contain a single command that will be executed as a sub-process without a shell. This covers most basic use cases such as the following examples.
5 |
6 | .. code-block:: toml
7 |
8 | [tool.poe.tasks.test]
9 | cmd = "pytest -v tests"
10 |
11 | .. note::
12 |
13 | Tasks defined as just a string value, are interpreted as ``cmd`` tasks by default.
14 |
15 |
16 | Available task options
17 | ----------------------
18 |
19 | ``cmd`` tasks support all of the :doc:`standard task options <../options>`.
20 |
21 |
22 | Shell like features
23 | -------------------
24 |
25 | It is important to understand that ``cmd`` tasks are executed without a shell (to maximise portability). However some shell like features are still available including basic parameter expansion and pattern matching. Quotes and escapes are also generally interpreted as one would expect in a shell.
26 |
27 | .. _ref_env_vars:
28 |
29 |
30 | Referencing environment variables
31 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 |
33 | Environment variables can be templated into the command. Just like in bash: whitespace inside a variable results in a word break, and glob patterns are evaluated after parameter expansion, unless the parameter expansion is inside double quotes. Single quotes disable parameter expansion. Curly braces are recommended but optional.
34 |
35 | .. code-block:: toml
36 |
37 | [tool.poe.tasks]
38 | greet = "echo Hello ${USER}"
39 |
40 | .. code-block:: sh
41 |
42 | $ poe greet
43 | Poe => echo Hello nat
44 | Hello nat
45 |
46 | Parameter expansion can also can be disabled by escaping the $ with a backslash like so:
47 |
48 | .. code-block:: toml
49 |
50 | [tool.poe.tasks]
51 | greet = "echo Hello \\$USER" # the backslash itself needs escaping for the toml parser
52 |
53 |
54 | Parameter expansion operators
55 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56 |
57 | When referencing an environment variable in a cmd task you can use the ``:-`` operator from bash to specify a *default value*, to be used in case the variable is unset. Similarly the ``:+`` operator can be used to specify an *alternate value* to use in place of the environment variable if it *is* set.
58 |
59 | In the following example, if ``AWS_REGION`` has a value then it will be used, otherwise ``us-east-1`` will be used as a fallback.
60 |
61 | .. code-block:: toml
62 |
63 | [tool.poe.tasks]
64 | tables = "aws dynamodb list-tables --region ${AWS_REGION:-us-east-1}"
65 |
66 | The ``:+`` or *alternate value* operator is especially useful in cases such as the following where you might want to control whether some CLI options are passed to the command.
67 |
68 | .. code-block:: toml
69 |
70 | [tool.poe.tasks.aws-identity]
71 | cmd = "aws sts get-caller-identity ${ARN_ONLY:+ --no-cli-pager --output text --query 'Arn'}"
72 | args = [{ name = "ARN_ONLY", options = ["--arn-only"], type = "boolean" }]
73 |
74 | In this example we declare a boolean argument with no default, so if the ``--arn-only`` flag is provided to the task then three additional CLI options will be included in the task content.
75 |
76 |
77 | Glob expansion
78 | ~~~~~~~~~~~~~~
79 |
80 | Glob patterns in cmd tasks are expanded and replaced with the list of matching files and directories. The supported glob syntax is that of the |glob_link|, which differs from bash in that square bracket patterns don't support character classes, don't break on whitespace, and don't allow escaping of contained characters.
81 |
82 | Glob patterns are evaluated relative to the working directory of the task, and if there are no matches then the pattern is expanded to nothing.
83 |
84 | Here's an example of task using a recursive glob pattern:
85 |
86 | .. code-block:: toml
87 |
88 | [tool.poe.tasks]
89 | clean = """
90 | rm -rf ./**/*.pyc
91 | ./**/__pycache__ # this will match all __pycache__ dirs in the project
92 | """
93 |
94 | .. code-block:: sh
95 |
96 | $ poe clean
97 | Poe => rm -rf ./tests/__pycache__ ./docs/__pycache__ ...
98 |
99 | .. seealso::
100 |
101 | Notice that this example also demonstrates that comments and excess whitespace (including new lines) are ignored, without needing to escape new lines.
102 |
103 | .. seealso::
104 |
105 | Just like in bash, the glob pattern can be escaped by wrapping it in quotes, or preceding it with a backslash.
106 |
107 |
108 | .. |glob_link| raw:: html
109 |
110 | python standard library glob module
111 |
--------------------------------------------------------------------------------
/docs/tasks/task_types/expr.rst:
--------------------------------------------------------------------------------
1 | ``expr`` tasks
2 | ==============
3 |
4 | **Expr tasks** consist of a single `python expression `_. Running the task evaluates the expression and outputs the resulting value. Here's a trivial example of an expr task that will print `2` when run:
5 |
6 | .. code-block:: toml
7 |
8 | [tool.poe.tasks.trivial-example]
9 | expr = "1 + 1"
10 |
11 | .. code-block:: bash
12 |
13 | $ poe trivial-example
14 | Poe => 1 + 1
15 | 2
16 |
17 | **Expressions can:**
18 |
19 | - use most python expression constructs with the exception of yield, await, or named
20 | expressions
21 | - use most builtin functions including all members of
22 | `this collection `_
23 | - reference the |sys_module_link| module without having to specify it as an import
24 | - directly access whatever arguments were passed to the task from `sys.argv`
25 | - reference values of named args as python variables
26 | - include environment variables as string values that are injected into the expression
27 | using the usual templating syntax ``${...}``
28 |
29 | Available task options
30 | ----------------------
31 |
32 | ``expr`` tasks support all of the :doc:`standard task options <../options>`.
33 |
34 | The following options are also accepted:
35 |
36 | **imports** : ``list[str]`` :ref:`📖`
37 | A list of modules to import for use in the expression.
38 |
39 | **assert** : ``bool`` :ref:`📖`
40 | If set to true and the result of the expression is found to be a falsey value then the task will fail with a non-zero return value.
41 |
42 |
43 | Referencing arguments and environment variables
44 | -----------------------------------------------
45 |
46 | The expression can reference environment variables using templating syntax as in :doc:`cmd` tasks, and named arguments as python variables in scope as in :doc:`script