Vulcano
Project description
Vulcano is a Python framework for building interactive command-line applications with minimal boilerplate.
Support the project on Patreon
What is Vulcano?
Built on top of prompt_toolkit, Vulcano turns plain Python functions into fully featured CLI commands โ complete with autocompletion, inline help, syntax highlighting, and command history โ with no extra configuration required.
Its simplicity makes it suitable for a wide range of scenarios where you need to expose existing functions through a REPL or a one-shot argument interface.
$ python your_app.py help
๐ Available Commands
โญโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ Command โ Description โ
โโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ reverse_word โ Return the word reversed. โ
โ random_upper_word โ Return the word with randomly capitalised letters. โ
โ multiply โ Multiply two numbers. โ
โ bye โ Say goodbye to someone. โ
โ help โ Print global help or details for a specific command. โ
โฐโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
Key Features
Autocomplete โ Vulcano inspects every registered function and automatically builds a completion list that includes command names and their arguments.
Inline help โ Help text is derived from docstrings or from the description provided at registration time.
History โ Use the up and down arrow keys to navigate through previous commands.
Module registration โ Register all public functions from an existing module without modifying its source.
Syntax highlighting โ Powered by Pygments and prompt_toolkit for a polished REPL experience.
Argument value options โ Attach a list of predefined choices to any argument with arg_opts; the autocompleter offers them and quotes values that contain spaces automatically:
@app.command("greet", "Greet by role", arg_opts={"role": ["admin", "user", "guest"]}) def greet(name, role="user"): return "Hello, {} {}!".format(role.capitalize(), name)Concatenated commands โ Chain multiple commands with and, both in argument mode and in the interactive REPL:
python your_script.py my_func arg="something" and my_func_2 arg="another thing"Context a plain dictionary available to all registered functions.
Command templating โ Use any value stored in the context to parameterise commands at runtime.
Autosuggestion โ When an unknown command is entered, Vulcano suggests the closest match:
๐ niu ๐ค Command 'niu' not found ๐ก Did you mean: "new"? ๐Command groups โ Organise commands into named sub-contexts with app.group(). Typing the group name in the REPL enters an isolated sub-session; exit returns to the parent. Commands in any group can also be run directly with dot-path syntax โ both in REPL and argument modes โ without entering the sub-session at all:
๐ text.hi name=Alice Hi! Mr. Alice :) Glad to see you. ๐Groups can be nested to any depth. The prompt chains all ancestor names so the current nesting level is always visible:
๐ text ๐ text > formal ๐ text > formal > dear name=Alice Dear Dr. Alice, I trust this finds you well. ๐ text > formal > exit ๐ text > exit ๐Source inspection โ Append ? to any command name to view its source code with syntax highlighting. Dot-path commands are supported too:
๐ bye? @app.command def bye(name="User"): """ Say goodbye to someone """ return "Bye {}!".format(name) ๐
Installation
Install the latest release from PyPI:
pip install vulcano
To install a development version directly from the repository:
git clone https://github.com/dgarana/vulcano.git
cd vulcano
pip install -e .
Getting Started
The repository includes a complete example in examples/simple_example.py. If you just want to see Vulcano working quickly, start there.
Quick start ~~~~~~~~~~~n 1. Install the package:
pip install vulcano
Save the example app below as simple_example.py (or use the version in examples/simple_example.py).
Run it in interactive mode:
python simple_example.py
Try a few realistic commands:
๐ hi name=Alice title=Dr.
Hi! Dr. Alice โ glad to see you.
๐ i_am name=Alice
๐ whoami
Alice
๐ greet name=Alice role=admin
Hello, Admin Alice!
๐ multiply number1=6 number2=7
42
๐ text.formal.dear name=Alice title="Prof."
Dear Prof. Alice, I trust this finds you well.
Or run one-shot commands directly from the shell:
python simple_example.py multiply number1=6 number2=7
python simple_example.py text.formal.dear name=Alice title="Prof."
python simple_example.py multiply number1=6 number2=7 and reverse_word word=vulcano
The longer snippet below covers the most common features:
import random
from vulcano.app import VulcanoApp
from vulcano.themes import MonokaiTheme
app = VulcanoApp()
@app.command("hi", "Greet someone by name")
def salute_method_here(name, title="Mr."):
"""Greet a person.
Args:
name (str): Name of the person to greet.
title (str): Honorific title.
"""
print("Hi! {} {} โ glad to see you.".format(title, name))
def has_context_name():
"""Return True only when a name has been set in the context."""
return "name" in app.context
@app.command
def i_am(name):
"""Store your name in the context.
Args:
name (str): Your name.
"""
app.context["name"] = name
@app.command(show_if=has_context_name)
def whoami():
"""Return your name from the context.
Only shown after ``i_am`` has been called.
"""
return app.context["name"]
@app.command
def bye(name="User"):
"""Say goodbye to someone."""
return "Bye {}!".format(name)
@app.command
def sum_numbers(*args):
"""Return the sum of all provided numbers."""
return sum(args)
@app.command
def multiply(number1, number2):
"""Multiply two numbers."""
return number1 * number2
@app.command
def reverse_word(word):
"""Return the word reversed."""
return word[::-1]
@app.command
def random_upper_word(word):
"""Return the word with randomly capitalised letters."""
return "".join(random.choice([letter.upper(), letter]) for letter in word)
@app.command("greet", "Greet someone by role", arg_opts={"role": ["admin", "user", "guest"]})
def greet_by_role(name, role="user"):
"""Greet someone and mention their role.
Args:
name (str): Name of the person to greet.
role (str): Role of the person.
"""
return "Hello, {} {}!".format(role.capitalize(), name)
if __name__ == "__main__":
app.run(theme=MonokaiTheme)
Command Groups
Groups let you organise commands into named sub-contexts, each with its own isolated sub-REPL. Create a group with app.group() and register commands on it the same way you would on the main app:
from vulcano.app import VulcanoApp
app = VulcanoApp()
# Create a group โ its name is what the user types to enter it.
text = app.group("text", "Text-related commands")
@text.command("hi", "Greet someone")
def say_hi(name, title="Mr."):
print("Hi! {} {}!".format(title, name))
@text.command("greet", "Greet by role", arg_opts={"role": ["admin", "user"]})
def greet_by_role(name, role="user"):
return "Hello, {} {}!".format(role.capitalize(), name)
if __name__ == "__main__":
app.run()
Typing the group name in the REPL enters the sub-session, where only that groupโs commands โ plus a local help and exit โ are available. The prompt reflects the current depth:
๐ text
๐ text > hi name=Alice
Hi! Mr. Alice!
๐ text > exit
๐
Nested groups โ Groups can contain other groups to any depth:
formal = text.group("formal", "Formal greetings")
@formal.command("dear", "Send a formal greeting")
def formal_dear(name, title="Dr."):
return "Dear {} {}, I trust this finds you well.".format(title, name)
The prompt chains all ancestor names:
๐ text
๐ text > formal
๐ text > formal > dear name=Alice title=Prof.
Dear Prof. Alice, I trust this finds you well.
๐ text > formal > exit
๐ text > exit
๐
Dot-path syntax โ Run any group command directly without entering the sub-session, using group.command notation. This works in both REPL and argument modes and can cross multiple nesting levels:
๐ text.hi name=Alice
Hi! Mr. Alice!
๐ text.formal.dear name=Alice title="Prof."
Dear Prof. Alice, I trust this finds you well.
$ python your_app.py text.hi name=Alice
Hi! Mr. Alice!
$ python your_app.py text.formal.dear name=Alice and bye
Dear Dr. Alice, I trust this finds you well.
Bye User!
Autocomplete is dot-path aware: typing text. offers text.hi, text.greet, text.formal; typing text.formal. narrows to text.formal.dear. Argument and arg_opts completions work the same as for top-level commands once the full path is typed.
Themes
Vulcano ships five built-in themes, all importable from vulcano.themes:
Theme |
Description |
|---|---|
MonokaiTheme |
Classic Monokai (default). |
DraculaTheme |
Dracula โ pink keywords, purple numbers, yellow strings. |
NordTheme |
Nord โ muted blues and greens on a dark background. |
|
|
OneDarkTheme |
Atom One Dark โ purple keywords, green strings, orange numbers. |
Pass any theme to app.run():
from vulcano.app import VulcanoApp
from vulcano.themes import DraculaTheme
app.run(theme=DraculaTheme)
To create a custom theme, subclass VulcanoStyle and define a styles dict using standard Pygments token types:
from pygments.token import Keyword, Name, Number, String, Text
from vulcano.themes import VulcanoStyle
class MyTheme(VulcanoStyle):
styles = {
Text: "#ffffff",
Keyword: "bold #ff0000",
Name: "#00ff00",
Number: "#0000ff",
String: "#ffff00",
}
app.run(theme=MyTheme)
The snippet above registers the following commands: i_am, whoami, bye, sum_numbers, multiply, reverse_word, random_upper_word, and greet. See Command Groups below to learn how to organise commands into named sub-contexts.
Commands that return a value have their result printed automatically and stored in context["last_result"], making it available for subsequent commands via templating.
REPL mode โ launch with no arguments to start the interactive shell:
$ python simple_example.py
๐ i_am name=Alice
๐ whoami
Alice
๐ reverse_word word=vulcano
onacluv
๐ multiply number1=6 number2=7
42
๐ bye
Bye User!
๐ help command=reverse_word
โญโโโโโโโโโโโโโโโโโโโโโโโโโโ โ๏ธ reverse_word โโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ Return the word reversed. โ
โ โ
โ Argument Type Default Description โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โก word str The word to reverse. โ
โ โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
๐ greet name=Alice role=admin
Hello, Admin Alice!
๐ multiply number1=6 number2=7 and reverse_word word=vulcano
42
onacluv
๐ exit
๐ See you soon!
Argument mode โ pass commands directly; chain with and:
$ python simple_example.py multiply number1=6 number2=7 and reverse_word word=vulcano
42
onacluv
$ python simple_example.py reverse_word "Hello Baby! This is awesome" and random_upper_word "{last_result}"
emosewa si sihT !ybaB olleH
eMOsEwa SI SIHT !YbaB OLlEH
Practical usage notes
A few behaviors are especially useful when you start building non-trivial apps:
Commands that return a value have that value printed automatically.
Returned values are also stored in context["last_result"] and can be reused in later commands.
print(...) is useful for side-effect-style commands, while return works better for commands whose output should be reused.
Group commands can be executed either by entering the group in the REPL or by using dot-path syntax directly.
arg_opts can be static lists or dynamic callables, which makes it possible to offer context-aware suggestions.
Context templating
Vulcano stores command results in context["last_result"]. By default, inline commands can reference context values using Python-style placeholders such as {last_result}.
For example:
python simple_example.py reverse_word word=vulcano and random_upper_word word="{last_result}"
This works because inline command parsing accepts placeholder braces and Vulcano formats arguments with the current context before execution.
If you want to disable this behavior and treat placeholders literally, you can set it either at construction time or afterwards:
app = VulcanoApp(enable_context_formatting=False)
# or later:
app.enable_context_formatting = False
When disabled, values such as {last_result} are passed through as plain text instead of being substituted from the context.
For a more realistic reference app, prefer examples/simple_example.py over minimal one-function snippets.
Development
The project ships a Makefile that wraps all common development tasks. Run make help (or just make) to see the full list of targets:
make fmt # Format code with black and isort
make black # Format with black only
make isort # Sort imports with isort only
make lint # Check style with flake8
make flake8 # Run flake8 only
make security # Scan with bandit
make bandit # Run bandit only
make test # Run the test suite
make check # All checks without modifying files (CI-friendly)
make all # Format, check, and test in one step
Project workflow
Vulcano is maintained as a community-driven project, but its day-to-day implementation work can be significantly accelerated by AI-assisted automation. In practice, community members propose ideas, fixes, and improvements through GitHub issues, and an automated AI workflow can review those requests, prepare changes, and open pull requests for human review.
This does not change the core purpose of the repository: Vulcano remains a framework for building Python command-line utilities by reusing existing Python functions and exposing them through a simpler command interface. AI-assisted contributions should remain aligned with that goal and operate within the repository rules documented in AGENTS.md when present.
Contributing
Contributions of all kinds are welcome โ bug reports, feature requests, documentation improvements, and pull requests.
Before submitting a pull request, please ensure that:
All existing tests pass (make test).
New functionality is covered by tests.
Code is formatted and all checks pass (make check).
Open an issue first if you are planning a significant change, so the approach can be discussed before implementation.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vulcano-2.1.2.tar.gz.
File metadata
- Download URL: vulcano-2.1.2.tar.gz
- Upload date:
- Size: 42.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d1528b30e2837f46920d8607e79374415a4b1dd9b6e7e9774b42e095b1f0f06
|
|
| MD5 |
91adc39d813d2648d7e3ae5153b5e367
|
|
| BLAKE2b-256 |
7945d2dc8ca24fd7cc1da21d4acaadfc452f35071ab52b11ff88578a28bfd2e3
|
Provenance
The following attestation bundles were made for vulcano-2.1.2.tar.gz:
Publisher:
publish.yml on dgarana/vulcano
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vulcano-2.1.2.tar.gz -
Subject digest:
2d1528b30e2837f46920d8607e79374415a4b1dd9b6e7e9774b42e095b1f0f06 - Sigstore transparency entry: 1203568507
- Sigstore integration time:
-
Permalink:
dgarana/vulcano@762be04930f2ae0db5082073bb669b48705e7b0f -
Branch / Tag:
refs/tags/v2.1.2 - Owner: https://github.com/dgarana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@762be04930f2ae0db5082073bb669b48705e7b0f -
Trigger Event:
push
-
Statement type:
File details
Details for the file vulcano-2.1.2-py3-none-any.whl.
File metadata
- Download URL: vulcano-2.1.2-py3-none-any.whl
- Upload date:
- Size: 41.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3bef61dfa12455b28308354c3802ec7f8012b1d5fb06092af70016170d8dd9f
|
|
| MD5 |
c7ec4708430ae700f166a30ac94d207f
|
|
| BLAKE2b-256 |
edec622d4a9e2efe884fbc5f80656c9b5cd8d9d5c9835353003ce4beca3b4c8d
|
Provenance
The following attestation bundles were made for vulcano-2.1.2-py3-none-any.whl:
Publisher:
publish.yml on dgarana/vulcano
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vulcano-2.1.2-py3-none-any.whl -
Subject digest:
b3bef61dfa12455b28308354c3802ec7f8012b1d5fb06092af70016170d8dd9f - Sigstore transparency entry: 1203568510
- Sigstore integration time:
-
Permalink:
dgarana/vulcano@762be04930f2ae0db5082073bb669b48705e7b0f -
Branch / Tag:
refs/tags/v2.1.2 - Owner: https://github.com/dgarana
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@762be04930f2ae0db5082073bb669b48705e7b0f -
Trigger Event:
push
-
Statement type: