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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ dist
build/
_build/
docs/html/
eggs
eggs
.github/
.vscode/
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Features

- Asynchronous and parallel tasks using asyncio
- Easy function to command interface
- Nested command groups for shorter, semantic command paths
- Out of box functionality
- Intuitive usability

Expand Down
6 changes: 6 additions & 0 deletions docs/pages/documentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,10 @@ Command
-------

.. automodule:: cmdcraft.command
:members:

CommandGroup
------------

.. automodule:: cmdcraft.group
:members:
76 changes: 74 additions & 2 deletions docs/pages/howto/crafting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,77 @@ It is highly recommended to use annotations in the callable parameters, so the p
arguments can be cast into said types. This will help you control and validate the input
your user inputs.

Custom types
------------
Command Grouping
================

Commands do not need to stay in a flat namespace. If your application has several
commands that would otherwise need long aliases such as ``project_status`` and
``project_config_set``, you may register them under a semantic path instead.

Both ``register_group()`` and ``register_command()`` treat whitespace as a path
separator:

- ``register_group("project config")`` means "register the group ``config`` under
the group ``project``".
- ``register_command(set_value, alias="project config set")`` means "register the
command ``set`` under the group path ``project -> config``".
- Registering the same group path more than once reuses the existing group,
which lets sibling groups share the same parent naturally.
- Registering a command or group on a path that is already occupied raises
``ValueError``.

.. code:: python

import asyncio
from cmdcraft import Prompter

async def start() -> None:
print("project started")

async def status() -> None:
print("project status shown")

async def show() -> None:
print("project configuration shown")

async def set_value(key: str, value: str) -> None:
print(f"project configuration updated: {key}={value}")

async def list_profiles() -> None:
print("profiles: dev, test, prod")

async def main() -> None:
prompt = Prompter()

project = prompt.register_group("project", doc="Project commands.")
project.register_command(start)
project.register_command(status)

config = prompt.register_group(
"project config",
doc="Project configuration commands.",
)
config.register_command(show)
prompt.register_command(set_value, alias="project config set")

profiles = prompt.register_group(
"project profile",
doc="Project profile commands.",
)
profiles.register_command(list_profiles, alias="list")
Comment on lines +43 to +75

await prompt.run()

asyncio.run(main())

This produces grouped commands such as ``project start``, ``project status``,
``project config show``, ``project config set theme dark`` and
``project profile list``. Re-registering grouped paths such as
``project config`` and ``project profile`` reuses the same ``project`` parent
group automatically.

The group path can be written either explicitly by chaining
``register_group()`` calls or with a spaced alias such as
``prompt.register_group("project config")``.

The runnable example in ``examples/group_commands.py`` demonstrates both styles.
64 changes: 64 additions & 0 deletions examples/group_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# *****************************************************************************
# Copyright (c) 2024-2026, Antonio Mario Weinsen Junior
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
# *****************************************************************************
"""cmdcraft example using grouped commands."""

import asyncio

from cmdcraft import Prompter


async def start() -> None:
"""Start the motor."""
print("motor started")


async def stop() -> None:
"""Stop the motor."""
print("motor stopped")


async def home() -> None:
"""Home the motor axis."""
print("motor axis homed")


async def move(position: float) -> None:
"""Move the motor axis to a position.

Args:
position (float): Target axis position.

Returns:
None: This coroutine does not return a value.

"""
print(f"moving motor axis to {position}")


async def main() -> None:
"""Run the grouped command example.

Returns:
None: This coroutine does not return a value.

"""
prompt = Prompter()

motor = prompt.register_group("motor", doc="Motor related commands.")
motor.register_command(start)
motor.register_command(stop)

axis = prompt.register_group("motor axis", doc="Motor axis commands.")
axis.register_command(home)
prompt.register_command(move, alias="motor axis move")

await prompt.run()


if __name__ == "__main__":
asyncio.run(main())
32 changes: 0 additions & 32 deletions examples/tmp.py

This file was deleted.

14 changes: 11 additions & 3 deletions src/cmdcraft/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
#!/usr/bin/env python3
"""cmdcraft module."""
# *****************************************************************************
# Copyright (c) 2024-2026, Antonio Mario Weinsen Junior
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
# *****************************************************************************
"""cmdcraft package exports."""

from __future__ import annotations

from .base import BasePrompter
from .group import CommandGroup
from .prompter import Prompter

__version__ = "0.0.7"
__version__ = "0.0.8"

__all__ = [
"BasePrompter",
"CommandGroup",
"Prompter",
"__version__",
]
Loading
Loading