- Mark help parser with `_is_help_command=True` so CLI renders as `program help`. - Add TLDR examples to `Exit` and `History` commands. - Normalize help TLDR/tag docs to short forms `-T` (tldr) and `-t [TAG]`. - Also propagate submenu exit help text TLDRs when set. - Disallow defaults for `HELP`, `TLDR`, `COUNT`, and boolean store actions. - Enforce list defaults for `APPEND`/`EXTEND` and any `nargs` in `{int, "*", "+"}`; coerce to list when `nargs == 1`. - Validate default(s) against `choices` (lists must be subset). - Strengthen `choices` checking at parse-time for both scalars and lists; track invalid-choice state for UX. - New `_resolve_posix_bundling()` with context: - Won’t split negative numbers or dash-prefixed positional/path values. - Uses the *last seen flag’s type/action* to decide if a dash token is a value vs. bundle. - Add `_is_valid_dash_token_positional_value()` and `_find_last_flag_argument()` helpers. - Completions overhaul - Track `consumed_position` and `has_invalid_choice` per-arg (via new `ArgumentState.set_consumed()` / `reset()`). - Add `_is_mid_value()` and `_value_suggestions_for_arg()` to produce value suggestions while typing. - Persist value context for multi-value args (`nargs="*"`, `"+"`) for each call to parse_args - Suppress suggestions when a choice is currently invalid, then recover as the prefix becomes valid. - Respect `cursor_at_end_of_token`; do not mutate the user’s prefix; improve path suggestions (`"."` vs prefix). - Better behavior after a space: suggest remaining flags when appropriate. - Consistent `index` naming (vs `i`) and propagate `base_index` into positional consumption to mark positions accurately. - Return value tweaks for `find_argument_by_dest()` and minor readability changes. - Replace the minimal completion test with a comprehensive suite covering: - Basics (defaults, option parsing, lists, booleans). - Validation edges (default/choices, `nargs` list requirements). - POSIX bundling (flags only; negative values; dash-prefixed paths). - Completions for flags/values/mid-value/path/`nargs="*"` persistence. - `store_bool_optional` (feature / no-feature, last one wins). - Invalid choice suppression & recovery. - Repeated keywords (last one wins) and completion context follows the last. - File-system-backed path suggestions. - Bumped version to 0.1.83.
192 lines
4.8 KiB
Python
192 lines
4.8 KiB
Python
import asyncio
|
|
from enum import Enum
|
|
from pathlib import Path
|
|
|
|
from falyx import Falyx
|
|
from falyx.action import Action
|
|
from falyx.parser.command_argument_parser import CommandArgumentParser
|
|
|
|
|
|
class Place(Enum):
|
|
"""Enum for different places."""
|
|
|
|
NEW_YORK = "New York"
|
|
SAN_FRANCISCO = "San Francisco"
|
|
LONDON = "London"
|
|
|
|
def __str__(self):
|
|
return self.value
|
|
|
|
|
|
async def test_args(
|
|
service: str,
|
|
place: Place = Place.NEW_YORK,
|
|
region: str = "us-east-1",
|
|
path: Path | None = None,
|
|
tag: str | None = None,
|
|
verbose: bool | None = None,
|
|
numbers: list[int] | None = None,
|
|
just_a_bool: bool = False,
|
|
) -> str:
|
|
if numbers is None:
|
|
numbers = []
|
|
if verbose:
|
|
print(
|
|
f"Deploying {service}:{tag}:{"|".join(str(number) for number in numbers)} to {region} at {place} from {path}..."
|
|
)
|
|
return f"{service}:{tag}:{"|".join(str(number) for number in numbers)} deployed to {region} at {place} from {path}."
|
|
|
|
|
|
async def test_path_arg(*paths: Path) -> str:
|
|
return f"Path argument received: {'|'.join(str(path) for path in paths)}"
|
|
|
|
|
|
async def test_positional_numbers(*numbers: int) -> str:
|
|
return f"Positional numbers received: {', '.join(str(num) for num in numbers)}"
|
|
|
|
|
|
def default_config(parser: CommandArgumentParser) -> None:
|
|
"""Default argument configuration for the command."""
|
|
parser.add_argument(
|
|
"service",
|
|
type=str,
|
|
choices=["web", "database", "cache"],
|
|
help="Service name to deploy.",
|
|
)
|
|
parser.add_argument(
|
|
"place",
|
|
type=Place,
|
|
choices=list(Place),
|
|
default=Place.NEW_YORK,
|
|
help="Place where the service will be deployed.",
|
|
)
|
|
parser.add_argument(
|
|
"--region",
|
|
type=str,
|
|
default="us-east-1",
|
|
help="Deployment region.",
|
|
choices=["us-east-1", "us-west-2", "eu-west-1"],
|
|
)
|
|
parser.add_argument(
|
|
"-p",
|
|
"--path",
|
|
type=Path,
|
|
help="Path to the configuration file.",
|
|
)
|
|
parser.add_argument(
|
|
"--verbose",
|
|
action="store_bool_optional",
|
|
help="Enable verbose output.",
|
|
)
|
|
parser.add_argument(
|
|
"-t",
|
|
"--tag",
|
|
type=str,
|
|
help="Optional tag for the deployment.",
|
|
suggestions=["latest", "stable", "beta"],
|
|
)
|
|
parser.add_argument(
|
|
"--numbers",
|
|
type=int,
|
|
nargs="*",
|
|
default=[1, 2, 3],
|
|
help="Optional number argument.",
|
|
)
|
|
parser.add_argument(
|
|
"-j",
|
|
"--just-a-bool",
|
|
action="store_true",
|
|
help="Just a boolean flag.",
|
|
)
|
|
parser.add_tldr_examples(
|
|
[
|
|
("web", "Deploy 'web' to the default location (New York)"),
|
|
("cache London --tag beta", "Deploy 'cache' to London with tag"),
|
|
("database --region us-west-2 --verbose", "Verbose deploy to west region"),
|
|
]
|
|
)
|
|
|
|
|
|
def path_config(parser: CommandArgumentParser) -> None:
|
|
"""Argument configuration for path testing command."""
|
|
parser.add_argument(
|
|
"paths",
|
|
type=Path,
|
|
nargs="*",
|
|
help="One or more file or directory paths.",
|
|
)
|
|
parser.add_tldr_examples(
|
|
[
|
|
("/path/to/file.txt", "Single file path"),
|
|
("/path/to/dir1 /path/to/dir2", "Multiple directory paths"),
|
|
("/path/with spaces/file.txt", "Path with spaces"),
|
|
]
|
|
)
|
|
|
|
|
|
def numbers_config(parser: CommandArgumentParser) -> None:
|
|
"""Argument configuration for positional numbers testing command."""
|
|
parser.add_argument(
|
|
"numbers",
|
|
type=int,
|
|
nargs="*",
|
|
help="One or more integers.",
|
|
)
|
|
parser.add_tldr_examples(
|
|
[
|
|
("1 2 3", "Three numbers"),
|
|
("42", "Single number"),
|
|
("", "No numbers"),
|
|
]
|
|
)
|
|
|
|
|
|
flx = Falyx(
|
|
"Argument Examples",
|
|
program="argument_examples.py",
|
|
hide_menu_table=True,
|
|
show_placeholder_menu=True,
|
|
enable_prompt_history=True,
|
|
)
|
|
|
|
flx.add_command(
|
|
key="T",
|
|
aliases=["test"],
|
|
description="Test Command",
|
|
help_text="A command to test argument parsing.",
|
|
action=Action(
|
|
name="test_args",
|
|
action=test_args,
|
|
),
|
|
style="bold #B3EBF2",
|
|
argument_config=default_config,
|
|
)
|
|
|
|
flx.add_command(
|
|
key="P",
|
|
aliases=["path"],
|
|
description="Path Command",
|
|
help_text="A command to test path argument parsing.",
|
|
action=Action(
|
|
name="test_path_arg",
|
|
action=test_path_arg,
|
|
),
|
|
style="bold #F2B3EB",
|
|
argument_config=path_config,
|
|
)
|
|
|
|
flx.add_command(
|
|
key="N",
|
|
aliases=["numbers"],
|
|
description="Numbers Command",
|
|
help_text="A command to test positional numbers argument parsing.",
|
|
action=Action(
|
|
name="test_positional_numbers",
|
|
action=test_positional_numbers,
|
|
),
|
|
style="bold #F2F2B3",
|
|
argument_config=numbers_config,
|
|
)
|
|
|
|
asyncio.run(flx.run())
|