Argparse Integration
TIP
This feature was added in version 0.1.1.
Slyme provides a built-in slyme.cli module to automatically map Ref dependencies from the core system into command-line arguments. It is built on top of the Python standard library argparse, allowing you to define your data model once and seamlessly gain command-line parsing and Context injection capabilities.
Core Concepts: Arg and ARG Metadata
In Slyme, any Ref can carry metadata describing how it should behave as an external input. This is achieved using the Arg dataclass and the ARG metadata key, both exported from slyme.context.
Arg contains a rich set of configuration options, designed to be compatible with mainstream CLI and configuration tools:
default/default_factory: The default value of the argument.help: The help description for the argument.type: The argument type. If not provided, Slyme will try to infer it automatically based on the type ofdefault.choices: The allowed range of values for the argument.required: Whether the external input is required.Node.run()accepts it frominputs, an existing Context, or the command line whenuse_argparse=True.nargs: The number of command-line arguments to consume.aliases: Aliases for the argument (e.g.,["-lr"]).metavar: The name displayed in the help message.
Data Type Support and Conversion Rules
A core advantage of slyme.cli is its intelligent type inference and conversion. It automatically registers the corresponding argparse processing logic based on the type specified in Arg (or inferred from the default value). Here is a detailed breakdown of the internal conversion behaviors for various data types:
1. Boolean (bool)
- Input Parsing: The framework automatically converts common strings into boolean values. It supports case-insensitive
yes/no,true/false,t/f,y/n, and1/0. - Argument Behavior: Defaults to
nargs="?"andconst=True. This means you can simply pass--flagto implyTrue, or--flag falseto explicitly state it. - Auto
--no-xxxFlag: If a boolean argument defaults toTrue, Slyme automatically generates a corresponding negative flag. For example, if the path ismodel.use_cacheand it defaults toTrue, a--no-model-use-cacheflag (equivalent toaction="store_false") is added automatically. - Default Fallback: If
required=Trueis not set and no default is provided, it falls back toFalse.
2. List and Tuple (list, tuple)
- Input Parsing: Automatically maps to argparse's
nargs="+"(unlessarg.nargsis explicitly overridden). This allows passing multiple values from the command line. - Generic Unpacking: If a generic type hint is provided (like
list[int]ortuple[float, ...]), the framework extracts the inner type (e.g.,int,float) and applies it to parse every element. - Example:
--server.ports 8080 8081will be correctly parsed into[8080, 8081].
3. Dictionary (dict)
- Input Parsing: Automatically enables the JSON parser. The command line input must be a valid JSON string.
- Example:
--model.config '{"layers": 3, "dim": 512}'. - Note: Because dictionary objects are difficult to validate using simple
choicesin the CLI layer,dicttypes omitchoicesconstraints by default.
4. Enum (Enum)
- Input Parsing: Slyme automatically extracts all
.valueitems from the Enum class and registers them as the CLIchoicesconstraint. - Type Mapping: The actual parsing type is set to the data type of the Enum values (e.g.,
strorint). - Default Value Handling: If an Enum instance is passed as the default (e.g.,
Color.RED), the framework extracts its underlying value to satisfyargparse.
5. Literal (Literal)
- Input Parsing: Similar to Enums, Slyme registers all candidate values within the
Literalaschoices. - Type Mapping: The type of the argument is mapped to the type of the first element in the
Literal. - Example:
type=Literal["small", "base"]restricts user input to these two exact strings.
6. Optional (Optional[T] / Union[T, None])
- Generic Unpacking: When a
UnioncontainingNoneTypeis detected, the framework filters outNoneand extracts the actual typeTto dispatch the parsing logic. This allows developers to safely useOptionalannotations without breaking CLI behavior.
Comprehensive Example
Combining the type rules above, here is a complete demonstration:
from enum import Enum
from typing import Literal
from slyme.context import ARG, Arg, Context, R
from slyme.node import node, Auto
class ModelSize(Enum):
SMALL = "small"
BASE = "base"
# 1. Define Refs with Arg metadata (demonstrating various data types)
use_cache_ref = R.model.use_cache(metadata={ARG: Arg(default=True, help="Whether to use cache")})
ports_ref = R.server.ports(metadata={ARG: Arg(type=list[int], default=[8080], help="List of ports")})
config_ref = R.model.config(metadata={ARG: Arg(type=dict, required=True, help="Model configuration (JSON string)")})
size_ref = R.model.size(metadata={ARG: Arg(type=ModelSize, default=ModelSize.SMALL)})
mode_ref = R.run.mode(metadata={ARG: Arg(type=Literal["train", "test"], default="train")})
# 2. Define Node
@node
def start_server(
ctx: Context,
/,
*,
use_cache: Auto[bool],
ports: Auto[list[int]],
config: Auto[dict],
size: Auto[str],
mode: Auto[str]
):
print(f"Cache: {use_cache}, Ports: {ports}, Config: {config}, Size: {size}, Mode: {mode}")
return ctx
if __name__ == "__main__":
# 3. Instantiate Node Def
server_node = start_server(
use_cache=use_cache_ref,
ports=ports_ref,
config=config_ref,
size=size_ref,
mode=mode_ref
)
# Simulating command line execution:
# python main.py --no-model-use-cache --server.ports 80 443 --model.config '{"debug": true}' --model.size base --run.mode test
# Discover Arg metadata, parse the CLI, validate required inputs, prepare,
# and execute through the normal application boundary.
final_context = server_node.run(use_argparse=True)Core API Reference
Node.run
For an executable Node tree, run() is the recommended application boundary. Set use_argparse=True to discover every Arg in the tree and parse command-line values. Values already supplied by context or inputs satisfy required arguments and become parser defaults; explicit CLI values take precedence.
result = node_def.run(
use_argparse=True,
cli_args=["--model.config", '{"debug": true}'],
)Omit cli_args to parse sys.argv[1:]. As with every run() call, you may also pass an existing Context as the first positional argument, provide programmatic inputs, select an outputs Ref PyTree, or request (output, context) with return_context=True.
If cli_args is provided while use_argparse=False, run() raises ValueError instead of silently ignoring it.
parse_and_inject
This is the lower-level API for parsing arguments independently of Node execution and optionally injecting them into a Context.
def parse_and_inject(
context: Optional[Context] = None,
parser: Optional[argparse.ArgumentParser] = None,
cli_args: Optional[List[str]] = None,
node: Optional[Union[Any, Iterable[RefLike]]] = None,
extra_refs: Optional[Iterable[RefLike]] = None,
extra_args: Optional[Dict[str, Arg]] = None,
) -> Union[Dict[str, Any], Context]:- Returns:
- If
contextis provided: Returns a newContextmerged with the parsed command-line arguments. - If
contextisNone: Returns the parsed arguments as a dictionary (Dict[str, Any]).
- If
- Argument Sources: Pass a
nodeto automatically scan all requiredRefsacross the dependency tree, or manually appendextra_refsandextra_args.
populate_parser and prepare_args
Low-level APIs for fine-grained control. When you already have a custom argparse.ArgumentParser instance, you can use these to append Slyme's arguments to your own parser.
prepare_args(node, extra_refs, extra_args): Extracts and merges allRefsandArgs, returning a dictionary{ "path": Arg }and handling naming conflicts automatically.populate_parser(parser, args_map): Mounts the extractedargs_mapinto the targetArgumentParser.
import argparse
from slyme.cli import prepare_args, populate_parser
# Initialize custom Parser
parser = argparse.ArgumentParser(description="Custom CLI")
parser.add_argument("--verbose", action="store_true")
# Extract dependency parameter map from Node
args_map = prepare_args(node=server_node)
# Mount into custom Parser
populate_parser(parser, args_map)
# Execute unified parsing
args = parser.parse_args()
print(args)