Skip to content

feat: Add category option for task grouping in help output#354

Open
brolewis wants to merge 3 commits intonat-n:mainfrom
brolewis:feature/task-categories
Open

feat: Add category option for task grouping in help output#354
brolewis wants to merge 3 commits intonat-n:mainfrom
brolewis:feature/task-categories

Conversation

@brolewis
Copy link

@brolewis brolewis commented Feb 7, 2026

Description of changes

Adds support for an optional category option for tasks. If the category is included, it is used for grouping help output and the categories are ordered alphabetically. Added tests and docs as well.

A quick example I created from the user guide example:

  $ poe --help
  Poe the Poet (version 0.40.0)

  Usage:
    poe [global options] task [task arguments]

  Global options:
    -h [TASK], --help [TASK]
                          Show this help page and exit, optionally supply a task.
    --version             Print the version and exit
    -v, --verbose         Increase output (repeatable)
    -q, --quiet           Decrease output (repeatable)
    -d, --dry-run         Print the task contents but don't actually run it
    -C PATH, --directory PATH
                          Specify where to find the pyproject.toml
    -e EXECUTOR, --executor EXECUTOR
                          Override the default task executor
    -X KEY[=VALUE], --executor-opt KEY[=VALUE]
                          Set executor configuration for this run.
    --ansi                Force enable ANSI output
    --no-ansi             Force disable ANSI output

  Configured tasks:
    test                  Run the test suite

    dev
      serve                 Run the app in debug mode
      tunnel                Create an SSH tunnel to the production server
        --prod_host         Hostname of the production server [default: myapp.com]

Fixes #352

Pre-merge Checklist

  • New features (if any) are covered by new feature tests
  • New features (if any) are documented
  • Bug fixes (if any) are accompanied by a test (if not too complicated to do so)
  • poe check executed successfully
  • This PR targets the development branch for code changes or main if only documentation is updated

Adds support for an optional category option for tasks. If the category
is included, it is used for grouping help output and the categories are
ordered alphabetically. Added tests and docs as well.

A quick example I created from the user guide example:

```
  $ poe --help
  Poe the Poet (version 0.40.0)

  Usage:
    poe [global options] task [task arguments]

  Global options:
    -h [TASK], --help [TASK]
                          Show this help page and exit, optionally supply a task.
    --version             Print the version and exit
    -v, --verbose         Increase output (repeatable)
    -q, --quiet           Decrease output (repeatable)
    -d, --dry-run         Print the task contents but don't actually run it
    -C PATH, --directory PATH
                          Specify where to find the pyproject.toml
    -e EXECUTOR, --executor EXECUTOR
                          Override the default task executor
    -X KEY[=VALUE], --executor-opt KEY[=VALUE]
                          Set executor configuration for this run.
    --ansi                Force enable ANSI output
    --no-ansi             Force disable ANSI output

  Configured tasks:
    test                  Run the test suite

    dev
      serve                 Run the app in debug mode
      tunnel                Create an SSH tunnel to the production server
        --prod_host         Hostname of the production server [default: myapp.com]
```

Fixes nat-n#352
@brolewis brolewis mentioned this pull request Feb 7, 2026
Copy link
Owner

@nat-n nat-n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work here.

I'm dissatisfied with how the config for tasks is modelled for this purpose (not your fault), which I think requires a slightly broader refactor to get right. I'm going to play around with it a bit, and might push what I come up with to this PR.

Comment on lines +114 to +120
Application Serving
dev Run the app in debug mode
prod Run the app in production mode

Testing & Quality
test Run the test suite
lint Run the linter
Copy link
Owner

@nat-n nat-n Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest keeping the same indentation for grouped tasks, and unindent the group headings, like the section headings are unindented. Maybe also give group headings a distinct color using pastel... yellow?

Suggested change
Application Serving
dev Run the app in debug mode
prod Run the app in production mode
Testing & Quality
test Run the test suite
lint Run the linter
Application Serving
dev Run the app in debug mode
prod Run the app in production mode
Testing & Quality
test Run the test suite
lint Run the linter


def load(self, task_name: str):
task_def, config_partition = self.config.lookup_task(task_name)
task_def, config_partition, group_name = self.config.lookup_task(task_name)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about making self.config.lookup_task return the actual group config instead of just the name?

I'm thinking an object or dict including the name of the group but also the relevant config (as PoeOptions)

shell_interpreter: str | Sequence[str] = "posix"
verbosity: Literal[-2, -1, 0, 1, 2] = 0
tasks: Mapping[str, Any] = EmptyDict
groups: Mapping[str, Any] = EmptyDict
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tasks doesn't have an easy to model schema, but task groups do, so we should model them.

)
for task, (_, args) in tasks.items()
for task, (_, args, _) in tasks.items()
if not task.startswith("_")
Copy link
Owner

@nat-n nat-n Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, though I think this introduces a crash condition if there are only hidden tasks defined!

Comment on lines +292 to +293
if group_count > 0 or None in grouped_tasks:
tasks_section.append("")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be done before the for loop instead of inside it.

group_heading = (
groups.get(group_name, group_name) if groups else group_name
)
tasks_section.append(f" {group_heading}")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should define a new style for this in PoeIO._init_colors

Suggested change
tasks_section.append(f" {group_heading}")
tasks_section.append(f" <group-heading>{group_heading}</group-heading>")

Drop copy-pasta

Co-authored-by: Nat Noordanus <n@noordan.us>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support task groups

2 participants