feat: impl docs (#4)

The entire public api is covered with documentation in two languages - Russian and English.

the library now supports the latest three versions of python - 3.12, 3.13 and 3.14

minor design changes: now, when a Boolean flag is entered, its value is an empty string, not None.

tests have been adapted to the supported versions of python, readmi has been redesigned in two languages, German is no longer available.
This commit is contained in:
kolo
2025-12-04 21:55:19 +03:00
committed by GitHub
parent a2ac6a608f
commit ce7e24b924
210 changed files with 13770 additions and 1183 deletions
+13
View File
@@ -0,0 +1,13 @@
from argenta import App, Orchestrator
from argenta.orchestrator.argparser import ArgParser, BooleanArgument, ValueArgument
arg_parser = ArgParser(processed_args=[BooleanArgument("dev"), ValueArgument('some', possible_values=['fuck', 'cruck'])])
orchestrator = Orchestrator(
arg_parser=arg_parser,
)
if __name__ == "__main__":
if arg_parser.parsed_argspace.get_by_name('dev'):
orchestrator.start_polling(App(initial_message='ArgentaDev'))
else:
orchestrator.start_polling(App())
+33
View File
@@ -0,0 +1,33 @@
from argenta import App, Orchestrator
from argenta.orchestrator.argparser import ArgParser, ValueArgument
arguments = [
ValueArgument("host", help="Server host", is_required=True),
ValueArgument("port", help="Server port", is_required=True),
]
argparser = ArgParser(
processed_args=arguments,
name="WebServer",
description="Simple web server"
)
app = App()
orchestrator = Orchestrator(argparser)
def main():
argspace = argparser.parsed_argspace
host = argspace.get_by_name("host")
port = argspace.get_by_name("port")
print("Server configuration:")
print(f" Host: {host.value}")
print(f" Port: {port.value}")
orchestrator.start_polling(app)
if __name__ == "__main__":
main()
+10
View File
@@ -0,0 +1,10 @@
from argenta import Response, Router
from argenta.di import FromDishka
from argenta.orchestrator.argparser import ArgSpace
router = Router()
@router.command("get_args")
async def get_args(response: Response, argspace: FromDishka[ArgSpace]):
print(argspace.all_arguments)
+21
View File
@@ -0,0 +1,21 @@
from argenta import Response, Router
from argenta.di import FromDishka
from argenta.orchestrator.argparser import ArgSpace, BooleanArgument, ValueArgument
router = Router()
@router.command("get_args")
def get_args(response: Response, argspace: FromDishka[ArgSpace]):
# Get all boolean flags
boolean_flags = argspace.get_by_type(BooleanArgument)
print(f"Active flags: {[arg.name for arg in boolean_flags if arg.value]}")
# Get all value arguments
value_args = argspace.get_by_type(ValueArgument)
for arg in value_args:
print(f"{arg.name} = {arg.value}")
# Count arguments of each type
print(f"Boolean arguments: {len(argspace.get_by_type(BooleanArgument))}")
print(f"Value arguments: {len(argspace.get_by_type(ValueArgument))}")
+11
View File
@@ -0,0 +1,11 @@
config_arg = argspace.get_by_name("config")
if config_arg:
print(f"Config path: {config_arg.value}")
verbose_arg = argspace.get_by_name("verbose")
if verbose_arg and verbose_arg.value:
print("Verbose mode enabled")
unknown_arg = argspace.get_by_name("nonexistent")
if unknown_arg is None:
print("Argument not found")
+28
View File
@@ -0,0 +1,28 @@
from argenta.orchestrator.argparser import ArgParser, ValueArgument
# Create arguments
config_arg = ValueArgument(
"config",
help="Path to configuration file",
default="config.yaml"
)
log_level_arg = ValueArgument(
"log-level",
help="Logging level",
possible_values=["DEBUG", "INFO", "WARNING", "ERROR"],
default="INFO"
)
host_arg = ValueArgument(
"host",
help="Server host address",
is_required=True
)
# Register in ArgParser
parser = ArgParser(
processed_args=[config_arg, log_level_arg, host_arg],
name="MyApp",
description="My application with CLI arguments"
)
+23
View File
@@ -0,0 +1,23 @@
from argenta.orchestrator.argparser import ArgParser, BooleanArgument
# Create boolean arguments
verbose_arg = BooleanArgument(
"verbose",
help="Enable verbose output"
)
debug_arg = BooleanArgument(
"debug",
help="Enable debug mode"
)
no_cache_arg = BooleanArgument(
"no-cache",
help="Disable caching"
)
# Register in ArgParser
parser = ArgParser(
processed_args=[verbose_arg, debug_arg, no_cache_arg],
name="MyApp"
)
@@ -0,0 +1,10 @@
from argenta import App
from argenta.app import AutoCompleter
# Setting up autocompletion with saving history to a file
my_autocompleter = AutoCompleter(history_filename="argenta_history.txt")
# Passing the configured autocompleter to the application
app = App(autocompleter=my_autocompleter)
# ... the rest of the application logic
+20
View File
@@ -0,0 +1,20 @@
from argenta.command import Flag, Flags, Command
# Simple command without flags
hello_cmd = Command("hello", description="Greet the user")
# Command with description and aliases
quit_cmd = Command("quit", description="Exit the application", aliases=["exit", "q"])
# Command with flags
deploy_cmd = Command(
"deploy",
description="Deploy application to server",
flags=Flags(
[
Flag("env", possible_values=["dev", "prod"]),
Flag("force"),
]
),
aliases=["dep"],
)
+19
View File
@@ -0,0 +1,19 @@
from argenta import Command, Response, Router
router = Router(title="User Management")
@router.command(Command("create-user", description="Create a new user account"))
def handle_create_user(response: Response):
print("Creating new user...")
@router.command(
Command(
"delete-user",
description="Delete existing user account",
aliases=["remove-user", "rm-user"],
)
)
def handle_delete_user(response: Response):
print("Deleting user...")
+33
View File
@@ -0,0 +1,33 @@
from argenta import Command, Response, Router
from argenta.command import Flag, Flags
router = Router(title="Server Management")
@router.command(
Command(
"start",
description="Start the server",
flags=Flags(
[
Flag("port"),
Flag("host"),
Flag("debug"),
]
),
aliases=["run"],
)
)
def handle_start(response: Response):
input_flags = response.input_flags
port_flag = input_flags.get_flag_by_name("port")
host_flag = input_flags.get_flag_by_name("host")
debug_flag = input_flags.get_flag_by_name("debug")
host = host_flag.input_value if host_flag else "localhost"
port = port_flag.input_value if port_flag else "8080"
debug = debug_flag and debug_flag.input_value
print(f"Starting server on {host}:{port}")
if debug:
print("Debug mode: ON")
+11
View File
@@ -0,0 +1,11 @@
from argenta.command import InputCommand
# Parse command without flags
cmd1 = InputCommand.parse("hello")
print(cmd1.trigger) # "hello"
print(len(cmd1.input_flags)) # 0
# Parse command with flags
cmd2 = InputCommand.parse("deploy --env prod --force")
print(cmd2.trigger) # "deploy"
print(len(cmd2.input_flags)) # 2
+11
View File
@@ -0,0 +1,11 @@
from argenta import Router, Command, Response
router = Router(title="System")
@router.command(Command(
"shutdown",
description="Shutdown the system",
aliases=["poweroff", "halt", "stop"]
))
def handle_shutdown(response: Response):
print("Shutting down the system...")
@@ -0,0 +1,9 @@
from sqlite3 import Connection
from argenta import Response, Router
from argenta.di import FromDishka
router = Router()
@router.command("connect")
def connect_handler(response: Response, connection: FromDishka[Connection]):
connection.execute("...")
@@ -0,0 +1,13 @@
import sqlite3
from sqlite3 import Connection
from typing import Iterable
from dishka import Provider, Scope, provide
class ConnectionProvider(Provider):
@provide(scope=Scope.REQUEST)
def new_connection(self) -> Iterable[Connection]:
conn = sqlite3.connect(":memory:")
yield conn
conn.close()
@@ -0,0 +1,3 @@
from argenta import Orchestrator
orchestrator = Orchestrator(custom_providers=[ConnectionProvider()])
@@ -0,0 +1,9 @@
from argenta import Response, Router
from argenta.di import FromDishka
from argenta.orchestrator.argparser import ArgSpace
router = Router()
@router.command("info")
def connect_handler(response: Response, argspace: FromDishka[ArgSpace]):
print(argspace.get_by_name("type"))
@@ -0,0 +1,7 @@
from argenta import App
def empty_command_handler():
print("Empty command handler called")
app: App = App()
app.set_empty_command_handler(empty_command_handler)
@@ -0,0 +1,7 @@
from argenta import App
def incorrect_input_syntax_handler(raw_command: str):
print(f"Incorrect input syntax for command: {raw_command}")
app: App = App()
app.set_incorrect_input_syntax_handler(incorrect_input_syntax_handler)
@@ -0,0 +1,7 @@
from argenta import App
def repeated_input_flags_handler(raw_command: str):
print(f"Repeated input flags: {raw_command}")
app: App = App()
app.set_repeated_input_flags_handler(repeated_input_flags_handler)
@@ -0,0 +1,7 @@
from argenta import App
def empty_command_handler():
print("Empty input command")
app: App = App()
app.set_empty_command_handler(empty_command_handler)
@@ -0,0 +1,8 @@
from argenta import App
from argenta.command import InputCommand
def unknown_command_handler(command: InputCommand):
print(f"Unknown input command with trigger: {command.trigger}")
app: App = App()
app.set_unknown_command_handler(unknown_command_handler)
@@ -0,0 +1,7 @@
from argenta import App, Response
def exit_command_handler(response: Response):
print("Exit command handler")
app: App = App()
app.set_exit_command_handler(exit_command_handler)
@@ -0,0 +1,36 @@
from argenta.command import Flags, PredefinedFlags
# Using predefined flags when creating a command
command_flags = Flags(
[
PredefinedFlags.HELP,
PredefinedFlags.SHORT_HELP,
PredefinedFlags.INFO,
]
)
# Using Network Flags
network_flags = Flags(
[
PredefinedFlags.HOST,
PredefinedFlags.PORT,
]
)
# Validating the values of predefined flags
print(PredefinedFlags.HOST.validate_input_flag_value("192.168.1.1")) # True
print(PredefinedFlags.HOST.validate_input_flag_value("invalid")) # False
print(PredefinedFlags.PORT.validate_input_flag_value("8080")) # True
print(PredefinedFlags.PORT.validate_input_flag_value("99999")) # True
print(PredefinedFlags.PORT.validate_input_flag_value("abc")) # False
# Flags without values
print(PredefinedFlags.HELP.validate_input_flag_value(None)) # True
print(PredefinedFlags.HELP.validate_input_flag_value("something")) # False
# Checking string representations
print(PredefinedFlags.HELP.string_entity) # --help
print(PredefinedFlags.SHORT_HELP.string_entity) # -H
print(PredefinedFlags.HOST.string_entity) # --host
print(PredefinedFlags.SHORT_PORT.string_entity) # -P
+20
View File
@@ -0,0 +1,20 @@
import re
from argenta.command import Flag, PossibleValues
# Simple flag with any values
verbose_flag = Flag(name="verbose")
# Flag with short prefix
short_flag = Flag(name="v", prefix="-")
# Flag that does not take a value
help_flag = Flag(name="help", possible_values=PossibleValues.NEITHER)
# Flag with list of possible values
format_flag = Flag(name="format", possible_values=["json", "xml", "csv"])
# Flag with regexp for validation input value
email_flag = Flag(
name="email",
possible_values=re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"),
)
+20
View File
@@ -0,0 +1,20 @@
import re
from argenta.command.flag.models import Flag, PossibleValues
# Flag with list of allowed values
format_flag = Flag(name="format", possible_values=["json", "xml", "csv"])
# Value validation
print(format_flag.validate_input_flag_value("json")) # True
print(format_flag.validate_input_flag_value("pdf")) # False
# Flag without value
help_flag = Flag(name="help", possible_values=PossibleValues.NEITHER)
print(help_flag.validate_input_flag_value(None)) # True
print(help_flag.validate_input_flag_value("value")) # False
# Flag with regular expression
port_flag = Flag(name="port", possible_values=re.compile(r"^\d{1,5}$"))
print(port_flag.validate_input_flag_value("8080")) # True
print(port_flag.validate_input_flag_value("abc")) # False
+9
View File
@@ -0,0 +1,9 @@
from argenta.command import Flag
help_flag = Flag(name="help")
version_flag = Flag(name="V", prefix="-")
print(help_flag) # --help
message = f"Use {help_flag} to see help"
print(message) # Use --help to see help
+12
View File
@@ -0,0 +1,12 @@
from argenta.command import Flag
verbose_flag = Flag(name="verbose", prefix="--")
short_flag = Flag(name="v", prefix="-")
# Debug view
print(repr(verbose_flag)) # Flag<prefix=--, name=verbose>
print(repr(short_flag)) # Flag<prefix=-, name=v>
# In an interactive console or debugger
# >>> verbose_flag
# Flag<prefix=--, name=verbose>
+21
View File
@@ -0,0 +1,21 @@
from argenta.command import Flag, PossibleValues
# Creating two flags with the same name and prefix
flag1 = Flag(name="verbose", prefix="--")
flag2 = Flag(name="verbose", prefix="--")
# Flag comparison
print(flag1 == flag2) # True
# Flags with different prefixes are not equal
flag3 = Flag(name="verbose", prefix="-")
print(flag1 == flag3) # False
# Flags with different names are not equal
flag4 = Flag(name="help", prefix="--")
print(flag1 == flag4) # False
# Different possible_values do not affect equality
flag5 = Flag(name="verbose", prefix="--", possible_values=PossibleValues.NEITHER)
flag6 = Flag(name="verbose", prefix="--", possible_values=["value1", "value2"])
print(flag5 == flag6)
@@ -0,0 +1,24 @@
from argenta import Router, Response
from argenta.command import Command, Flag, PossibleValues
from argenta.command.flag import ValidationStatus
router = Router()
@router.command(Command(
"deploy",
flags=Flag("verbose", possible_values=PossibleValues.NEITHER)
))
def deploy_handler(response: Response):
# Check for toggle flag presence
verbose_flag = response.input_flags.get_flag_by_name("verbose")
if verbose_flag and verbose_flag.status == ValidationStatus.VALID:
print("Deploying with verbose output...")
# Detailed logic
elif verbose_flag and verbose_flag.status == ValidationStatus.INVALID:
print("Incorrect flag value")
return
else:
print("Deploying...")
# Normal logic
+16
View File
@@ -0,0 +1,16 @@
from argenta import Router, Response
from argenta.command import Command, Flag
router = Router()
@router.command(Command("greet", flags=Flag("name")))
def greet_handler(response: Response):
# Get flag by name
name_flag = response.input_flags.get_flag_by_name("name")
# Check if flag was passed
if name_flag:
print(f"Hello, {name_flag.input_value}!")
else:
print("Hello, stranger!")
+11
View File
@@ -0,0 +1,11 @@
import re
from argenta.command import Command, Flag, Flags
flags = Flags(
[
Flag("host", possible_values=re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")),
Flag("port", possible_values=re.compile(r"^\d{1,5}$")),
]
)
cmd = Command("start", description="Start the server", flags=flags)
+9
View File
@@ -0,0 +1,9 @@
from argenta.command import Flag, Flags
flags: Flags = Flags()
flags.add_flag(Flag("config"))
flags.add_flag(Flag("debug"))
flags.add_flag(Flag("log-level", possible_values=["INFO", "DEBUG", "ERROR"]))
print(len(flags)) # 3
+15
View File
@@ -0,0 +1,15 @@
from argenta.command import Flag, Flags
from argenta.command.flag.defaults import PredefinedFlags
flags = Flags([PredefinedFlags.HOST])
additional_flags = [
PredefinedFlags.PORT,
Flag("database"),
Flag("ssl"),
Flag("verbose"),
]
flags.add_flags(additional_flags)
print(len(flags)) # 5
+13
View File
@@ -0,0 +1,13 @@
from argenta.command import Flag, Flags
from argenta.command.flag.defaults import PredefinedFlags
flags = Flags([PredefinedFlags.HOST, PredefinedFlags.PORT, Flag("verbose")])
host_flag = flags.get_flag_by_name("host")
if host_flag:
print(f"Found flag: {host_flag.name}")
unknown_flag = flags.get_flag_by_name("nonexistent")
if unknown_flag is None:
print("Flag not found")
+13
View File
@@ -0,0 +1,13 @@
from argenta.command.flag import InputFlag, ValidationStatus
flag_with_value = InputFlag(
name="output", prefix="--", input_value="result.txt", status=ValidationStatus.VALID
)
flag_without_value = InputFlag(
name="help", prefix="-", input_value='', status=ValidationStatus.VALID
)
# String representation includes value
print(str(flag_with_value)) # --output result.txt
print(str(flag_without_value)) # -help
+12
View File
@@ -0,0 +1,12 @@
from argenta.command.flag import InputFlag, ValidationStatus
flag = InputFlag(
name="config",
prefix="--",
input_value="settings.json",
status=ValidationStatus.VALID,
)
# Debug representation of the object
print(repr(flag))
# InputFlag<prefix='--', name='config', value='settings.json', status=ValidationStatus.VALID>
@@ -0,0 +1,23 @@
from argenta import Command, Response, Router
from argenta.command import Flag, Flags
router = Router(title="Example")
@router.command(
Command(
"example",
description="Example command with flags",
flags=Flags([
Flag("name"),
Flag("age")
]),
)
)
def example_handler(response: Response):
input_flags = response.input_flags
if input_flags:
print(f"Received {len(input_flags)} flag(s)")
else:
print("No flags provided")
@@ -0,0 +1,49 @@
from argenta import Command, Response, Router
from argenta.command import Flag, Flags, InputFlag
from argenta.command.flag import ValidationStatus
router = Router(title="Comprehensive Example")
@router.command(
Command(
"validate",
description="Validate all flags",
flags=Flags(
[
Flag("format", possible_values=["json", "xml"]),
Flag("output"),
Flag("force"),
]
),
)
)
def validate_handler(response: Response):
input_flags = response.input_flags
print("Flag validation results:")
valid_flags: list[InputFlag] = []
invalid_flags: list[InputFlag] = []
undefined_flags: list[InputFlag] = []
for flag in input_flags:
if flag.status == ValidationStatus.VALID:
valid_flags.append(flag)
print(f"{flag.string_entity}: {flag.input_value} (VALID)")
elif flag.status == ValidationStatus.INVALID:
invalid_flags.append(flag)
print(f"{flag.string_entity}: {flag.input_value} (INVALID)")
elif flag.status == ValidationStatus.UNDEFINED:
undefined_flags.append(flag)
print(f" ? {flag.string_entity}: {flag.input_value} (UNDEFINED)")
print("\nSummary:")
print(f" Valid flags: {len(valid_flags)}")
print(f" Invalid flags: {len(invalid_flags)}")
print(f" Undefined flags: {len(undefined_flags)}")
if valid_flags:
print("\nProcessing valid flags:")
for flag in valid_flags:
print(f" Processing {flag.name} = {flag.input_value}")
@@ -0,0 +1,36 @@
from argenta import Command, Response, Router
from argenta.command import Flag, Flags
router = Router(title="Get Flag Example")
@router.command(
Command(
"config",
description="Configure settings",
flags=Flags([
Flag("host"),
Flag("port"),
Flag("debug")
]),
)
)
def config_handler(response: Response):
input_flags = response.input_flags
host_flag = input_flags.get_flag_by_name("host")
port_flag = input_flags.get_flag_by_name("port")
debug_flag = input_flags.get_flag_by_name("debug")
if host_flag:
print(f"Host: {host_flag.input_value}")
if port_flag:
print(f"Port: {port_flag.input_value}")
if debug_flag:
print("Debug mode enabled")
missing_flag = input_flags.get_flag_by_name("nonexistent")
if missing_flag is None:
print("Flag 'nonexistent' not found")
@@ -0,0 +1,19 @@
from argenta import Command, Response, Router
from argenta.command.flag import InputFlag, InputFlags, ValidationStatus
router = Router(title="Add Flag Example")
@router.command(Command("test", description="Test command"))
def test_handler(response: Response):
# Create new InputFlags collection
new_flags = InputFlags()
# Add one flag
test_flag = InputFlag(
name="test", prefix="--", input_value="value", status=ValidationStatus.VALID
)
new_flags.add_flag(test_flag)
print(f"Flags count: {len(new_flags.flags)}")
print(f"First flag: {new_flags.flags[0].name}")
@@ -0,0 +1,24 @@
from argenta.command.flag import InputFlag, InputFlags, ValidationStatus
# Create InputFlags collection
flags = InputFlags()
# Create several flags
flag1 = InputFlag(
name="option1", prefix="--", input_value="value1", status=ValidationStatus.VALID
)
flag2 = InputFlag(
name="option2", prefix="--", input_value="value2", status=ValidationStatus.VALID
)
flag3 = InputFlag(
name="option3", prefix="---", input_value="value3", status=ValidationStatus.VALID
)
# Add all flags in one call
flags.add_flags([flag1, flag2, flag3])
print(f"Total flags: {len(flags.flags)}")
for flag in flags:
print(f" - {flag.string_entity}: {flag.input_value}")
@@ -0,0 +1,27 @@
from argenta import Command, Response, Router
from argenta.command import Flag, Flags
router = Router(title="Bool Check Example")
@router.command(
Command(
"action",
description="Action with optional flags",
flags=Flags([Flag("option1"), Flag("option2")]),
)
)
def action_handler(response: Response):
input_flags = response.input_flags
# Check for flags presence
if input_flags:
print("Flags were provided:")
for flag in input_flags:
print(f" - {flag.name}: {flag.input_value}")
else:
print("No flags provided, using defaults")
# Alternative way to check
has_flags = bool(input_flags)
print(f"\nHas flags: {has_flags}")
@@ -0,0 +1,37 @@
from argenta.command.flag import InputFlag, ValidationStatus
from argenta.command.flag.flags.models import InputFlags
# Create first collection
flags1 = InputFlags(
[
InputFlag(name="flag1", input_value="value1", status=ValidationStatus.VALID),
InputFlag(name="flag2", input_value="value2", status=ValidationStatus.VALID),
]
)
# Create second collection with same flags
flags2 = InputFlags(
[
InputFlag(name="flag1", input_value="value1", status=ValidationStatus.VALID),
InputFlag(name="flag2", input_value="value2", status=ValidationStatus.VALID),
]
)
# Create third collection with different values
flags3 = InputFlags(
[
InputFlag(name="flag1", input_value="different", status=ValidationStatus.VALID),
InputFlag(name="flag2", input_value="value2", status=ValidationStatus.VALID),
]
)
print(f"flags1 == flags2: {flags1 == flags2}") # True (same names)
print(
f"flags1 == flags3: {flags1 == flags3}"
) # True (same names, values are not considered)
# Different collections
flags4 = InputFlags(
[InputFlag(name="flag3", input_value="value3", status=ValidationStatus.VALID)]
)
print(f"flags1 == flags4: {flags1 == flags4}") # False (different flags)
@@ -0,0 +1,33 @@
from argenta import Command, Response, Router
from argenta.command import Flag, Flags
from argenta.command.flag import InputFlag
router = Router(title="Contains Example")
@router.command(
Command(
"check",
description="Check flags",
flags=Flags([Flag("verbose"), Flag("debug"), Flag("quiet")]),
)
)
def check_handler(response: Response):
input_flags = response.input_flags
# Check for specific flag presence
verbose_flag = input_flags.get_flag_by_name("verbose")
debug_flag = input_flags.get_flag_by_name("debug")
# Use 'in' operator for checking
if verbose_flag and verbose_flag in input_flags:
print("Verbose flag is present")
if debug_flag and debug_flag in input_flags:
print("Debug flag is present")
# You can create a flag for checking (comparison is by name)
test_flag = InputFlag(name="verbose", prefix="--", input_value="any", status=None)
if test_flag in input_flags:
print("Verbose flag found using 'in' operator")
@@ -0,0 +1,27 @@
import sqlite3
from sqlite3 import Connection
from typing import Iterable
from dishka import Provider, Scope, provide
from argenta import App, Orchestrator
class ConnectionProvider(Provider):
@provide(scope=Scope.REQUEST)
def new_connection(self) -> Iterable[Connection]:
conn = sqlite3.connect(":memory:")
yield conn
conn.close()
# 2. Create and configure App
app = App()
# ... you can add routers here ...
# 3. Create Orchestrator, passing our provider
orchestrator = Orchestrator(custom_providers=[ConnectionProvider()])
# 4. Start the application
if __name__ == "__main__":
orchestrator.start_polling(app)
@@ -0,0 +1,5 @@
from argenta.command import Flag, PossibleValues
# Creating flags with any values
message_flag = Flag(name="message", possible_values=PossibleValues.ALL)
name_flag = Flag(name="name", possible_values=PossibleValues.ALL)
@@ -0,0 +1,14 @@
import re
from argenta.command import Flag, PossibleValues
# Flag without value
verbose_flag = Flag(name="verbose", possible_values=PossibleValues.NEITHER)
# Flag with any value
output_flag = Flag(name="output", possible_values=PossibleValues.ALL)
# Flag with a list of valid values
format_flag = Flag(name="format", possible_values=["json", "xml", "csv", "yaml"])
# Flag with regular expression
email_flag = Flag(name="email", possible_values=re.compile(r"^[\w\.-]+@[\w\.-]+\.\w+$"))
@@ -0,0 +1,6 @@
from argenta.command import Flag, PossibleValues
# Creating flags without values
help_flag = Flag(name="help", possible_values=PossibleValues.NEITHER)
verbose_flag = Flag(name="verbose", possible_values=PossibleValues.NEITHER)
force_flag = Flag(name="force", possible_values=PossibleValues.NEITHER)
@@ -0,0 +1,68 @@
import operator
import re
from argenta import App, Orchestrator, Response, Router
from argenta.app import DynamicDividingLine
from argenta.command import Command, Flag, Flags
from argenta.response.status import ResponseStatus
router = Router("Calculator")
operations = {
'mul': operator.mul,
'sub': operator.sub,
'add': operator.add
}
@router.command(
Command(
"calc",
description="Calculator with two numbers",
flags=Flags(
[
Flag("a", possible_values=re.compile(r"^\d{,5}$")), # First number
Flag("b", possible_values=re.compile(r"^\d{,5}$")), # Second number
Flag("operation", possible_values=["add", "sub", "mul"]), # Operation: add, sub, mul
]
),
)
)
def calc_handler(response: Response):
# Get flag values
a_flag = response.input_flags.get_flag_by_name("a")
b_flag = response.input_flags.get_flag_by_name("b")
op_flag = response.input_flags.get_flag_by_name("op")
# Check that all flags are provided
if response.status != ResponseStatus.ALL_FLAGS_VALID or not all([a_flag, b_flag, op_flag]):
print("Error: must specify --a, --b and --op")
return
a = float(a_flag.input_value)
b = float(b_flag.input_value)
operation = op_flag.input_value
try:
result = operations[operation](a, b)
except ZeroDivisionError:
print("Can't divide by zero")
else:
print(f"Result: {result}")
app = App(
initial_message="Calculator",
repeat_command_groups_printing=False,
prompt=">> ",
dividing_line=DynamicDividingLine("~"),
)
orchestrator = Orchestrator()
def main():
app.include_router(router)
orchestrator.start_polling(app)
if __name__ == "__main__":
main()
+16
View File
@@ -0,0 +1,16 @@
# main.py
from .routers import router
from argenta import App, Orchestrator
app: App = App()
orchestrator: Orchestrator = Orchestrator()
def main() -> None:
app.include_router(router)
orchestrator.start_polling(app)
if __name__ == "__main__":
main()
+9
View File
@@ -0,0 +1,9 @@
# routers.py
from argenta import Command, Response, Router
router = Router(title="Quickstart Example")
@router.command(Command("hello", description="Say hello"))
def handler(response: Response):
print("Hello, world!")
@@ -0,0 +1,37 @@
from argenta import App, Command, Orchestrator, Router, Response
from argenta.command import Flag
# 1. Create app and orchestrator instances
app = App(
prompt=">> ",
initial_message="Simple App",
farewell_message="Goodbye!",
repeat_command_groups_printing=False
)
orchestrator = Orchestrator()
# 2. Create router for grouping commands
main_router = Router(title="Main commands")
# 3. Define command and its handler
@main_router.command(Command(
"hello",
description="Prints greeting message",
flags=Flag("name")
))
def hello_handler(response: Response):
"""This handler will be called for 'hello' command."""
name = response.input_flags.get_flag_by_name("name")
if name:
print(f"Hello, {name.input_value}!")
else:
print("Hello, world!")
# 4. Include router to application
app.include_router(main_router)
# 5. Start application
if __name__ == "__main__":
orchestrator.start_polling(app)
@@ -0,0 +1,54 @@
from typing import cast
from argenta import Command, Response, Router
from argenta.command.flag import Flag, Flags, ValidationStatus
from argenta.di import FromDishka
from .repository import Priority, Task, TaskRepository
router = Router(title="Task Manager")
@router.command(Command(
"add-task",
description="Add a new task",
flags=Flags([
Flag("description"),
Flag("priority", possible_values=["low", "medium", "high"]),
]),
))
def add_task(response: Response, repo: FromDishka[TaskRepository]):
description_flag = response.input_flags.get_flag_by_name("description")
if not description_flag or not description_flag.status == ValidationStatus.VALID:
print("Error: --description flag is required.")
return
task_description = description_flag.input_value or ""
priority_flag = response.input_flags.get_flag_by_name("priority")
if priority_flag and priority_flag.status == ValidationStatus.VALID:
priority_value = priority_flag.input_value
else:
priority_value = "medium"
priority = cast(Priority, priority_value)
task = Task(description=task_description, priority=priority)
repo.add_task(task)
print(f"Added task: '{task.description}' with priority '{task.priority}'")
@router.command(Command("list-tasks", description="List all tasks"))
def list_tasks(response: Response, repo: FromDishka[TaskRepository]):
tasks = repo.get_all_tasks()
if not tasks:
print("No tasks found.")
return
print("Tasks:")
for i, task in enumerate(tasks, 1):
print(f" {i}. {task.description} (Priority: {task.priority})")
@@ -0,0 +1,18 @@
from argenta import App, Orchestrator
from .handlers import router
from .provider import TaskProvider
# 1. Create app and orchestrator instances
app = App(
initial_message="Task Manager",
prompt="Enter a command: ",
)
orchestrator = Orchestrator(custom_providers=[TaskProvider()])
# 2. Include router with our commands
app.include_router(router)
# 3. Start polling via orchestrator
if __name__ == "__main__":
orchestrator.start_polling(app)
@@ -0,0 +1,9 @@
from dishka import Provider, Scope, provide
from .repository import TaskRepository
class TaskProvider(Provider):
@provide(scope=Scope.APP)
def get_repository(self) -> TaskRepository:
return TaskRepository()
@@ -0,0 +1,21 @@
from dataclasses import dataclass
from typing import Literal
Priority = Literal["low", "medium", "high"]
@dataclass
class Task:
description: str
priority: Priority = "medium"
class TaskRepository:
def __init__(self):
self._tasks: list[Task] = []
def add_task(self, task: Task):
self._tasks.append(task)
def get_all_tasks(self) -> list[Task]:
return self._tasks
@@ -0,0 +1,10 @@
from argenta import Response, Router
# For this router stdout redirect will be disabled
interactive_router = Router(disable_redirect_stdout=True)
@interactive_router.command("ask")
def ask_name(response: Response):
name = input("What is your name? ")
print(f"Nice to meet you, {name}!")
@@ -0,0 +1,5 @@
from argenta import App
from argenta.app import StaticDividingLine
# All routers will use static line with length 50 by default
app = App(dividing_line=StaticDividingLine(length=50))
@@ -0,0 +1,42 @@
from argenta import Router, Response, Command, DataBridge
from argenta.command import Flag
from argenta.di import FromDishka
router = Router(title="Authentication")
def authenticate_user(username: str) -> str:
return f"token_for_{username}"
@router.command(Command("login", flags=Flag("username")))
def login_handler(response: Response, data_bridge: FromDishka[DataBridge]):
username_flag = response.input_flags.get_flag_by_name("username")
if not username_flag or not username_flag.input_value:
print("Error: username must be specified using the --username flag.")
return
username = username_flag.input_value
token = authenticate_user(username)
data_bridge.update({"auth_token": token})
print(f"Login successful! User '{username}' authenticated.")
@router.command("get-profile")
def get_profile_handler(response: Response, data_bridge: FromDishka[DataBridge]):
token = data_bridge.get_by_key("auth_token")
if not token:
print("Error: you are not authenticated. Please run the 'login' command first.")
return
print(f"Loading profile using token: [yellow]{token}[/yellow]")
@router.command("logout")
def logout_handler(response: Response, data_bridge: FromDishka[DataBridge]):
try:
data_bridge.delete_by_key("auth_token")
print("Logout successful. Session data cleared.")
except KeyError:
print("You were not authenticated anyway.")
+12
View File
@@ -0,0 +1,12 @@
from argenta import Command, Response, Router
from argenta.response import ResponseStatus
router = Router(title="Example")
@router.command(Command("greet", description="Greet the user"))
def greet_handler(response: Response):
if response.status == ResponseStatus.ALL_FLAGS_VALID:
print("Hello! All flags are valid.")
else:
print("Warning: Some flags have issues.")
+25
View File
@@ -0,0 +1,25 @@
from argenta import Command, Response, Router
router = Router(title="Data Example")
@router.command(Command("set", description="Set data"))
def set_handler(response: Response):
# Update global data storage
response.update_data(
{
"user_name": "John",
"timestamp": "2024-01-01",
"settings": {"theme": "dark", "language": "ru"},
}
)
print("Data updated successfully")
@router.command(Command("show", description="Show data"))
def show_handler(response: Response):
# Get data from global storage
data = response.get_data()
if "user_name" in data:
print(f"User: {data['user_name']}")
print(f"Settings: {data.get('settings', {})}")
+16
View File
@@ -0,0 +1,16 @@
from argenta import Command, Response, Router
router = Router(title="Get Data Example")
@router.command(Command("info", description="Show all stored data"))
def info_handler(response: Response):
# Get all data from global storage
all_data = response.get_data()
if all_data:
print("Stored data:")
for key, value in all_data.items():
print(f" {key}: {value}")
else:
print("No data stored")
+19
View File
@@ -0,0 +1,19 @@
from argenta import Command, Response, Router
router = Router(title="Clear Data Example")
@router.command(Command("clear", description="Clear all stored data"))
def clear_handler(response: Response):
# Clear all data storage
response.clear_data()
print("All data cleared")
@router.command(Command("check", description="Check if data exists"))
def check_handler(response: Response):
data = response.get_data()
if data:
print(f"Storage contains {len(data)} item(s)")
else:
print("Storage is empty")
+29
View File
@@ -0,0 +1,29 @@
from argenta import Command, Response, Router
router = Router(title="Delete Data Example")
@router.command(Command("store", description="Store data"))
def store_handler(response: Response):
response.update_data(
{
"temp_key": "temporary value",
"important_key": "important value",
"another_key": "another value",
}
)
print("Data stored")
@router.command(Command("remove", description="Remove specific key"))
def remove_handler(response: Response):
# Delete specific key from storage
try:
response.delete_from_data("temp_key")
print("Key 'temp_key' deleted")
# Check what remains
remaining = response.get_data()
print(f"Remaining keys: {list(remaining.keys())}")
except KeyError:
print("Key not found")
+38
View File
@@ -0,0 +1,38 @@
from argenta import Command, Response, Router
from argenta.command import Flag, Flags
from argenta.command.flag import ValidationStatus
from argenta.response import ResponseStatus
router = Router(title="Flags Example")
@router.command(
Command(
"process",
description="Process with flags",
flags=Flags([
Flag("format", possible_values=["json", "xml"]),
Flag("verbose")
]),
)
)
def process_handler(response: Response):
print(f"Status: {response.status}")
format_flag = response.input_flags.get_flag_by_name("format")
verbose_flag = response.input_flags.get_flag_by_name("verbose")
if format_flag:
format_value = format_flag.input_value
print(f"Format: {format_value}")
if verbose_flag:
print("Verbose mode enabled")
if response.status == ResponseStatus.ALL_FLAGS_VALID:
print("All flags are valid, proceeding...")
elif response.status == ResponseStatus.INVALID_VALUE_FLAGS:
print("Warning: Some flags have invalid values")
for flag in response.input_flags:
if flag.status == ValidationStatus.INVALID:
print(f" Invalid flag: {flag.string_entity} = {flag.input_value}")
+7
View File
@@ -0,0 +1,7 @@
from argenta import Command, Response, Router
user_router = Router(title="User Management")
@user_router.command(Command("add-user", description="Adds a new user"))
def add_user_handler(response: Response):
print("User added successfully!")
@@ -0,0 +1,32 @@
import sys
from unittest.mock import patch
import pytest
from pytest import CaptureFixture
from argenta import App, Orchestrator, Router, Command, Response
@pytest.fixture(autouse=True)
def patched_argv():
with patch.object(sys, 'argv', ['program.py']):
yield
def test_input_incorrect_command(capsys: CaptureFixture[str]):
router = Router()
orchestrator = Orchestrator()
@router.command(Command('test'))
def test(response: Response) -> None:
print('test command')
app = App(override_system_messages=True, print_func=print)
app.include_router(router)
app.set_unknown_command_handler(
lambda command: print(f'Unknown command: {command.trigger}')
)
with patch("builtins.input", side_effect=["help", "q"]):
orchestrator.start_polling(app)
output = capsys.readouterr().out
assert "\nUnknown command: help\n" in output
@@ -0,0 +1,21 @@
import io
from contextlib import redirect_stdout
from argenta import App, Router, Command, Response
from argenta.command import InputCommand
def test_simple_app() -> None:
app = App(override_system_messages=True, repeat_command_groups_printing=False)
router = Router(title="App")
@router.command(Command("HELP", description="Show help"))
def help_cmd(response: Response):
print("Available commands: HELP")
app.include_router(router)
with redirect_stdout(io.StringIO()) as stdout:
router.finds_appropriate_handler(InputCommand.parse("HELP"))
assert "Available commands:" in stdout.getvalue()
@@ -0,0 +1,42 @@
import io
from contextlib import redirect_stdout
from argenta.command import InputCommand
from dishka import Provider, make_container, Scope
from argenta import Router, Response
from argenta.di.integration import setup_dishka, FromDishka
class Service:
def hello(self) -> str:
return "world"
def get_service() -> Service:
return Service()
router = Router(title="DI")
@router.command("HELLO")
def hello(response: Response, service: FromDishka[Service]) -> None:
print(f"hello {service.hello()}")
class _FakeApp:
# Minimal stub for setup_dishka; app object is not used in unit tests
registered_routers = [router]
def test_hello_uses_service():
provider = Provider(scope=Scope.APP)
provider.provide(get_service)
container = make_container(provider)
setup_dishka(app=_FakeApp(), container=container, auto_inject=True)
# Call handler
with redirect_stdout(io.StringIO()) as stdout:
router.finds_appropriate_handler(InputCommand.parse('HELLO'))
assert "hello world" in stdout.getvalue()
@@ -0,0 +1,19 @@
import io
from contextlib import redirect_stdout
from argenta import Router, Command, Response
from argenta.command import InputCommand
router = Router(title="Demo")
@router.command(Command("PING", description="Ping command"))
def ping(response: Response):
print("PONG")
def test_ping_prints_pong():
# Call handler
with redirect_stdout(io.StringIO()) as stdout:
router.finds_appropriate_handler(InputCommand.parse("PING"))
assert "PONG" in stdout.getvalue()