├── .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 | Poe the Poet 4 | 5 | [![Python versions](https://img.shields.io/badge/python-3.9%20%E2%80%93%203.13-blue)](https://pypi.org/project/poethepoet/) 6 | [![PyPI version](https://img.shields.io/pypi/v/poethepoet.svg)](https://pypi.org/project/poethepoet/) 7 | [![Download stats](https://img.shields.io/pypi/dm/poethepoet.svg)](https://pypistats.org/packages/poethepoet) 8 | [![License](https://img.shields.io/pypi/l/ansicolortags.svg)](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