mirror of
https://git.datalinker.icu/vllm-project/vllm.git
synced 2026-05-24 09:24:27 +08:00
Improve --help for enhanced user experience (#24903)
Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com>
This commit is contained in:
parent
1f29141258
commit
e7f27ea648
@ -168,5 +168,5 @@ def on_startup(command: Literal["build", "gh-deploy", "serve"], dirty: bool):
|
|||||||
doc_path = ARGPARSE_DOC_DIR / f"{stem}.md"
|
doc_path = ARGPARSE_DOC_DIR / f"{stem}.md"
|
||||||
# Specify encoding for building on Windows
|
# Specify encoding for building on Windows
|
||||||
with open(doc_path, "w", encoding="utf-8") as f:
|
with open(doc_path, "w", encoding="utf-8") as f:
|
||||||
f.write(parser.format_help())
|
f.write(super(type(parser), parser).format_help())
|
||||||
logger.info("Argparse generated: %s", doc_path.relative_to(ROOT_DIR))
|
logger.info("Argparse generated: %s", doc_path.relative_to(ROOT_DIR))
|
||||||
|
|||||||
@ -156,8 +156,8 @@ def is_online_quantization(quantization: Any) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
NEEDS_HELP = (
|
NEEDS_HELP = (
|
||||||
"--help" in (argv := sys.argv) # vllm SUBCOMMAND --help
|
any("--help" in arg for arg in sys.argv) # vllm SUBCOMMAND --help
|
||||||
or (argv0 := argv[0]).endswith("mkdocs") # mkdocs SUBCOMMAND
|
or (argv0 := sys.argv[0]).endswith("mkdocs") # mkdocs SUBCOMMAND
|
||||||
or argv0.endswith("mkdocs/__main__.py") # python -m mkdocs SUBCOMMAND
|
or argv0.endswith("mkdocs/__main__.py") # python -m mkdocs SUBCOMMAND
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -8,8 +8,7 @@ import typing
|
|||||||
|
|
||||||
from vllm.entrypoints.cli.benchmark.base import BenchmarkSubcommandBase
|
from vllm.entrypoints.cli.benchmark.base import BenchmarkSubcommandBase
|
||||||
from vllm.entrypoints.cli.types import CLISubcommand
|
from vllm.entrypoints.cli.types import CLISubcommand
|
||||||
from vllm.entrypoints.utils import (VLLM_SUBCMD_PARSER_EPILOG,
|
from vllm.entrypoints.utils import VLLM_SUBCMD_PARSER_EPILOG
|
||||||
show_filtered_argument_or_group_from_help)
|
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from vllm.utils import FlexibleArgumentParser
|
from vllm.utils import FlexibleArgumentParser
|
||||||
@ -33,9 +32,8 @@ class BenchmarkSubcommand(CLISubcommand):
|
|||||||
subparsers: argparse._SubParsersAction) -> FlexibleArgumentParser:
|
subparsers: argparse._SubParsersAction) -> FlexibleArgumentParser:
|
||||||
bench_parser = subparsers.add_parser(
|
bench_parser = subparsers.add_parser(
|
||||||
self.name,
|
self.name,
|
||||||
help=self.help,
|
|
||||||
description=self.help,
|
description=self.help,
|
||||||
usage="vllm bench <bench_type> [options]")
|
usage=f"vllm {self.name} <bench_type> [options]")
|
||||||
bench_subparsers = bench_parser.add_subparsers(required=True,
|
bench_subparsers = bench_parser.add_subparsers(required=True,
|
||||||
dest="bench_type")
|
dest="bench_type")
|
||||||
|
|
||||||
@ -44,13 +42,12 @@ class BenchmarkSubcommand(CLISubcommand):
|
|||||||
cmd_cls.name,
|
cmd_cls.name,
|
||||||
help=cmd_cls.help,
|
help=cmd_cls.help,
|
||||||
description=cmd_cls.help,
|
description=cmd_cls.help,
|
||||||
usage=f"vllm bench {cmd_cls.name} [options]",
|
usage=f"vllm {self.name} {cmd_cls.name} [options]",
|
||||||
)
|
)
|
||||||
cmd_subparser.set_defaults(dispatch_function=cmd_cls.cmd)
|
cmd_subparser.set_defaults(dispatch_function=cmd_cls.cmd)
|
||||||
cmd_cls.add_cli_args(cmd_subparser)
|
cmd_cls.add_cli_args(cmd_subparser)
|
||||||
show_filtered_argument_or_group_from_help(cmd_subparser,
|
cmd_subparser.epilog = VLLM_SUBCMD_PARSER_EPILOG.format(
|
||||||
["bench", cmd_cls.name])
|
subcmd=f"{self.name} {cmd_cls.name}")
|
||||||
cmd_subparser.epilog = VLLM_SUBCMD_PARSER_EPILOG
|
|
||||||
return bench_parser
|
return bench_parser
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ def main():
|
|||||||
|
|
||||||
parser = FlexibleArgumentParser(
|
parser = FlexibleArgumentParser(
|
||||||
description="vLLM CLI",
|
description="vLLM CLI",
|
||||||
epilog=VLLM_SUBCMD_PARSER_EPILOG,
|
epilog=VLLM_SUBCMD_PARSER_EPILOG.format(subcmd="[subcommand]"),
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-v',
|
'-v',
|
||||||
|
|||||||
@ -9,8 +9,7 @@ import importlib.metadata
|
|||||||
import typing
|
import typing
|
||||||
|
|
||||||
from vllm.entrypoints.cli.types import CLISubcommand
|
from vllm.entrypoints.cli.types import CLISubcommand
|
||||||
from vllm.entrypoints.utils import (VLLM_SUBCMD_PARSER_EPILOG,
|
from vllm.entrypoints.utils import VLLM_SUBCMD_PARSER_EPILOG
|
||||||
show_filtered_argument_or_group_from_help)
|
|
||||||
from vllm.logger import init_logger
|
from vllm.logger import init_logger
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
@ -50,7 +49,7 @@ class RunBatchSubcommand(CLISubcommand):
|
|||||||
from vllm.entrypoints.openai.run_batch import make_arg_parser
|
from vllm.entrypoints.openai.run_batch import make_arg_parser
|
||||||
|
|
||||||
run_batch_parser = subparsers.add_parser(
|
run_batch_parser = subparsers.add_parser(
|
||||||
"run-batch",
|
self.name,
|
||||||
help="Run batch prompts and write results to file.",
|
help="Run batch prompts and write results to file.",
|
||||||
description=(
|
description=(
|
||||||
"Run batch prompts using vLLM's OpenAI-compatible API.\n"
|
"Run batch prompts using vLLM's OpenAI-compatible API.\n"
|
||||||
@ -59,9 +58,8 @@ class RunBatchSubcommand(CLISubcommand):
|
|||||||
"vllm run-batch -i INPUT.jsonl -o OUTPUT.jsonl --model <model>",
|
"vllm run-batch -i INPUT.jsonl -o OUTPUT.jsonl --model <model>",
|
||||||
)
|
)
|
||||||
run_batch_parser = make_arg_parser(run_batch_parser)
|
run_batch_parser = make_arg_parser(run_batch_parser)
|
||||||
show_filtered_argument_or_group_from_help(run_batch_parser,
|
run_batch_parser.epilog = VLLM_SUBCMD_PARSER_EPILOG.format(
|
||||||
["run-batch"])
|
subcmd=self.name)
|
||||||
run_batch_parser.epilog = VLLM_SUBCMD_PARSER_EPILOG
|
|
||||||
return run_batch_parser
|
return run_batch_parser
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -14,8 +14,7 @@ from vllm.entrypoints.openai.api_server import (run_server, run_server_worker,
|
|||||||
setup_server)
|
setup_server)
|
||||||
from vllm.entrypoints.openai.cli_args import (make_arg_parser,
|
from vllm.entrypoints.openai.cli_args import (make_arg_parser,
|
||||||
validate_parsed_serve_args)
|
validate_parsed_serve_args)
|
||||||
from vllm.entrypoints.utils import (VLLM_SUBCMD_PARSER_EPILOG,
|
from vllm.entrypoints.utils import VLLM_SUBCMD_PARSER_EPILOG
|
||||||
show_filtered_argument_or_group_from_help)
|
|
||||||
from vllm.logger import init_logger
|
from vllm.logger import init_logger
|
||||||
from vllm.usage.usage_lib import UsageContext
|
from vllm.usage.usage_lib import UsageContext
|
||||||
from vllm.utils import (FlexibleArgumentParser, decorate_logs, get_tcp_uri,
|
from vllm.utils import (FlexibleArgumentParser, decorate_logs, get_tcp_uri,
|
||||||
@ -29,6 +28,14 @@ from vllm.v1.utils import (APIServerProcessManager,
|
|||||||
|
|
||||||
logger = init_logger(__name__)
|
logger = init_logger(__name__)
|
||||||
|
|
||||||
|
DESCRIPTION = """Launch a local OpenAI-compatible API server to serve LLM
|
||||||
|
completions via HTTP. Defaults to Qwen/Qwen3-0.6B if no model is specified.
|
||||||
|
|
||||||
|
Search by using: `--help=<ConfigGroup>` to explore options by section (e.g.,
|
||||||
|
--help=ModelConfig, --help=Frontend)
|
||||||
|
Use `--help=all` to show all available flags at once.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ServeSubcommand(CLISubcommand):
|
class ServeSubcommand(CLISubcommand):
|
||||||
"""The `serve` subcommand for the vLLM CLI. """
|
"""The `serve` subcommand for the vLLM CLI. """
|
||||||
@ -56,14 +63,13 @@ class ServeSubcommand(CLISubcommand):
|
|||||||
self,
|
self,
|
||||||
subparsers: argparse._SubParsersAction) -> FlexibleArgumentParser:
|
subparsers: argparse._SubParsersAction) -> FlexibleArgumentParser:
|
||||||
serve_parser = subparsers.add_parser(
|
serve_parser = subparsers.add_parser(
|
||||||
"serve",
|
self.name,
|
||||||
help="Start the vLLM OpenAI Compatible API server.",
|
description=DESCRIPTION,
|
||||||
description="Start the vLLM OpenAI Compatible API server.",
|
|
||||||
usage="vllm serve [model_tag] [options]")
|
usage="vllm serve [model_tag] [options]")
|
||||||
|
|
||||||
serve_parser = make_arg_parser(serve_parser)
|
serve_parser = make_arg_parser(serve_parser)
|
||||||
show_filtered_argument_or_group_from_help(serve_parser, ["serve"])
|
serve_parser.epilog = VLLM_SUBCMD_PARSER_EPILOG.format(
|
||||||
serve_parser.epilog = VLLM_SUBCMD_PARSER_EPILOG
|
subcmd=self.name)
|
||||||
return serve_parser
|
return serve_parser
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||||
|
|
||||||
import argparse
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import functools
|
import functools
|
||||||
import os
|
import os
|
||||||
import subprocess
|
from argparse import Namespace
|
||||||
import sys
|
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
@ -25,13 +23,10 @@ from vllm.utils import FlexibleArgumentParser
|
|||||||
logger = init_logger(__name__)
|
logger = init_logger(__name__)
|
||||||
|
|
||||||
VLLM_SUBCMD_PARSER_EPILOG = (
|
VLLM_SUBCMD_PARSER_EPILOG = (
|
||||||
"Tip: Use `vllm [serve|run-batch|bench <bench_type>] "
|
"For full list: vllm {subcmd} --help=all\n"
|
||||||
"--help=<keyword>` to explore arguments from help.\n"
|
"For a section: vllm {subcmd} --help=ModelConfig (case-insensitive)\n" # noqa: E501
|
||||||
" - To view a argument group: --help=ModelConfig\n"
|
"For a flag: vllm {subcmd} --help=max-model-len (_ or - accepted)\n" # noqa: E501
|
||||||
" - To view a single argument: --help=max-num-seqs\n"
|
"Documentation: https://docs.vllm.ai\n")
|
||||||
" - To search by keyword: --help=max\n"
|
|
||||||
" - To list all groups: --help=listgroup\n"
|
|
||||||
" - To view help with pager: --help=page")
|
|
||||||
|
|
||||||
|
|
||||||
async def listen_for_disconnect(request: Request) -> None:
|
async def listen_for_disconnect(request: Request) -> None:
|
||||||
@ -196,96 +191,6 @@ def _validate_truncation_size(
|
|||||||
return truncate_prompt_tokens
|
return truncate_prompt_tokens
|
||||||
|
|
||||||
|
|
||||||
def _output_with_pager(text: str):
|
|
||||||
"""Output text using scrolling view if available and appropriate."""
|
|
||||||
|
|
||||||
pagers = ['less -R', 'more']
|
|
||||||
for pager_cmd in pagers:
|
|
||||||
try:
|
|
||||||
proc = subprocess.Popen(pager_cmd.split(),
|
|
||||||
stdin=subprocess.PIPE,
|
|
||||||
text=True)
|
|
||||||
proc.communicate(input=text)
|
|
||||||
return
|
|
||||||
except (subprocess.SubprocessError, OSError, FileNotFoundError):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# No pager worked, fall back to normal print
|
|
||||||
print(text)
|
|
||||||
|
|
||||||
|
|
||||||
def show_filtered_argument_or_group_from_help(parser: argparse.ArgumentParser,
|
|
||||||
subcommand_name: list[str]):
|
|
||||||
|
|
||||||
# Only handle --help=<keyword> for the current subcommand.
|
|
||||||
# Since subparser_init() runs for all subcommands during CLI setup,
|
|
||||||
# we skip processing if the subcommand name is not in sys.argv.
|
|
||||||
# sys.argv[0] is the program name. The subcommand follows.
|
|
||||||
# e.g., for `vllm bench latency`,
|
|
||||||
# sys.argv is `['vllm', 'bench', 'latency', ...]`
|
|
||||||
# and subcommand_name is "bench latency".
|
|
||||||
if len(sys.argv) <= len(subcommand_name) or sys.argv[
|
|
||||||
1:1 + len(subcommand_name)] != subcommand_name:
|
|
||||||
return
|
|
||||||
|
|
||||||
for arg in sys.argv:
|
|
||||||
if arg.startswith('--help='):
|
|
||||||
search_keyword = arg.split('=', 1)[1]
|
|
||||||
|
|
||||||
# Enable paged view for full help
|
|
||||||
if search_keyword == 'page':
|
|
||||||
help_text = parser.format_help()
|
|
||||||
_output_with_pager(help_text)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# List available groups
|
|
||||||
if search_keyword == 'listgroup':
|
|
||||||
output_lines = ["\nAvailable argument groups:"]
|
|
||||||
for group in parser._action_groups:
|
|
||||||
if group.title and not group.title.startswith(
|
|
||||||
"positional arguments"):
|
|
||||||
output_lines.append(f" - {group.title}")
|
|
||||||
if group.description:
|
|
||||||
output_lines.append(" " +
|
|
||||||
group.description.strip())
|
|
||||||
output_lines.append("")
|
|
||||||
_output_with_pager("\n".join(output_lines))
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# For group search
|
|
||||||
formatter = parser._get_formatter()
|
|
||||||
for group in parser._action_groups:
|
|
||||||
if group.title and group.title.lower() == search_keyword.lower(
|
|
||||||
):
|
|
||||||
formatter.start_section(group.title)
|
|
||||||
formatter.add_text(group.description)
|
|
||||||
formatter.add_arguments(group._group_actions)
|
|
||||||
formatter.end_section()
|
|
||||||
_output_with_pager(formatter.format_help())
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# For single arg
|
|
||||||
matched_actions = []
|
|
||||||
|
|
||||||
for group in parser._action_groups:
|
|
||||||
for action in group._group_actions:
|
|
||||||
# search option name
|
|
||||||
if any(search_keyword.lower() in opt.lower()
|
|
||||||
for opt in action.option_strings):
|
|
||||||
matched_actions.append(action)
|
|
||||||
|
|
||||||
if matched_actions:
|
|
||||||
header = f"\nParameters matching '{search_keyword}':\n"
|
|
||||||
formatter = parser._get_formatter()
|
|
||||||
formatter.add_arguments(matched_actions)
|
|
||||||
_output_with_pager(header + formatter.format_help())
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
print(f"\nNo group or parameter matching '{search_keyword}'")
|
|
||||||
print("Tip: use `--help=listgroup` to view all groups.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def get_max_tokens(max_model_len: int, request: Union[ChatCompletionRequest,
|
def get_max_tokens(max_model_len: int, request: Union[ChatCompletionRequest,
|
||||||
CompletionRequest],
|
CompletionRequest],
|
||||||
input_length: int, default_sampling_params: dict) -> int:
|
input_length: int, default_sampling_params: dict) -> int:
|
||||||
@ -301,11 +206,11 @@ def get_max_tokens(max_model_len: int, request: Union[ChatCompletionRequest,
|
|||||||
if val is not None)
|
if val is not None)
|
||||||
|
|
||||||
|
|
||||||
def log_non_default_args(args: Union[argparse.Namespace, EngineArgs]):
|
def log_non_default_args(args: Union[Namespace, EngineArgs]):
|
||||||
non_default_args = {}
|
non_default_args = {}
|
||||||
|
|
||||||
# Handle argparse.Namespace
|
# Handle Namespace
|
||||||
if isinstance(args, argparse.Namespace):
|
if isinstance(args, Namespace):
|
||||||
parser = make_arg_parser(FlexibleArgumentParser())
|
parser = make_arg_parser(FlexibleArgumentParser())
|
||||||
for arg, default in vars(parser.parse_args([])).items():
|
for arg, default in vars(parser.parse_args([])).items():
|
||||||
if default != getattr(args, arg):
|
if default != getattr(args, arg):
|
||||||
@ -323,6 +228,6 @@ def log_non_default_args(args: Union[argparse.Namespace, EngineArgs]):
|
|||||||
non_default_args["model"] = default_args.model
|
non_default_args["model"] = default_args.model
|
||||||
else:
|
else:
|
||||||
raise TypeError("Unsupported argument type. " \
|
raise TypeError("Unsupported argument type. " \
|
||||||
"Must be argparse.Namespace or EngineArgs instance.")
|
"Must be Namespace or EngineArgs instance.")
|
||||||
|
|
||||||
logger.info("non-default args: %s", non_default_args)
|
logger.info("non-default args: %s", non_default_args)
|
||||||
|
|||||||
@ -1720,6 +1720,7 @@ class FlexibleArgumentParser(ArgumentParser):
|
|||||||
"Additionally, list elements can be passed individually using +:\n"
|
"Additionally, list elements can be passed individually using +:\n"
|
||||||
' --json-arg \'{"key4": ["value3", "value4", "value5"]}\'\n'
|
' --json-arg \'{"key4": ["value3", "value4", "value5"]}\'\n'
|
||||||
" --json-arg.key4+ value3 --json-arg.key4+=\'value4,value5\'\n\n")
|
" --json-arg.key4+ value3 --json-arg.key4+=\'value4,value5\'\n\n")
|
||||||
|
_search_keyword: Optional[str] = None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# Set the default "formatter_class" to SortedHelpFormatter
|
# Set the default "formatter_class" to SortedHelpFormatter
|
||||||
@ -1768,13 +1769,79 @@ class FlexibleArgumentParser(ArgumentParser):
|
|||||||
self._action_groups.append(group)
|
self._action_groups.append(group)
|
||||||
return group
|
return group
|
||||||
|
|
||||||
def format_help(self) -> str:
|
def format_help(self):
|
||||||
# Add tip about JSON arguments to the epilog
|
# Only use custom help formatting for bottom level parsers
|
||||||
epilog = self.epilog or ""
|
if self._subparsers is not None:
|
||||||
if (self.add_json_tip
|
return super().format_help()
|
||||||
and not epilog.startswith(FlexibleArgumentParser._json_tip)):
|
|
||||||
self.epilog = FlexibleArgumentParser._json_tip + epilog
|
formatter = self._get_formatter()
|
||||||
return super().format_help()
|
|
||||||
|
# Handle keyword search of the args
|
||||||
|
if (search_keyword := self._search_keyword) is not None:
|
||||||
|
# Normalise the search keyword
|
||||||
|
search_keyword = search_keyword.lower().replace("_", "-")
|
||||||
|
# Return full help if searching for 'all'
|
||||||
|
if search_keyword == 'all':
|
||||||
|
self.epilog = self._json_tip
|
||||||
|
return super().format_help()
|
||||||
|
|
||||||
|
# Return group help if searching for a group title
|
||||||
|
for group in self._action_groups:
|
||||||
|
if group.title and group.title.lower() == search_keyword:
|
||||||
|
formatter.start_section(group.title)
|
||||||
|
formatter.add_text(group.description)
|
||||||
|
formatter.add_arguments(group._group_actions)
|
||||||
|
formatter.end_section()
|
||||||
|
formatter.add_text(self._json_tip)
|
||||||
|
return formatter.format_help()
|
||||||
|
|
||||||
|
# Return matched args if searching for an arg name
|
||||||
|
matched_actions = []
|
||||||
|
for group in self._action_groups:
|
||||||
|
for action in group._group_actions:
|
||||||
|
# search option name
|
||||||
|
if any(search_keyword in opt.lower()
|
||||||
|
for opt in action.option_strings):
|
||||||
|
matched_actions.append(action)
|
||||||
|
if matched_actions:
|
||||||
|
formatter.start_section(
|
||||||
|
f"Arguments matching '{search_keyword}'")
|
||||||
|
formatter.add_arguments(matched_actions)
|
||||||
|
formatter.end_section()
|
||||||
|
formatter.add_text(self._json_tip)
|
||||||
|
return formatter.format_help()
|
||||||
|
|
||||||
|
# No match found
|
||||||
|
formatter.add_text(
|
||||||
|
f"No group or arguments matching '{search_keyword}'.\n"
|
||||||
|
"Use '--help' to see available groups or "
|
||||||
|
"'--help=all' to see all available parameters.")
|
||||||
|
return formatter.format_help()
|
||||||
|
|
||||||
|
# usage
|
||||||
|
formatter.add_usage(self.usage, self._actions,
|
||||||
|
self._mutually_exclusive_groups)
|
||||||
|
|
||||||
|
# description
|
||||||
|
formatter.add_text(self.description)
|
||||||
|
|
||||||
|
# positionals, optionals and user-defined groups
|
||||||
|
formatter.start_section("Config Groups")
|
||||||
|
config_groups = ""
|
||||||
|
for group in self._action_groups:
|
||||||
|
if not group._group_actions:
|
||||||
|
continue
|
||||||
|
title = group.title
|
||||||
|
description = group.description or ""
|
||||||
|
config_groups += f"{title: <24}{description}\n"
|
||||||
|
formatter.add_text(config_groups)
|
||||||
|
formatter.end_section()
|
||||||
|
|
||||||
|
# epilog
|
||||||
|
formatter.add_text(self.epilog)
|
||||||
|
|
||||||
|
# determine help from format above
|
||||||
|
return formatter.format_help()
|
||||||
|
|
||||||
def parse_args( # type: ignore[override]
|
def parse_args( # type: ignore[override]
|
||||||
self,
|
self,
|
||||||
@ -1807,7 +1874,11 @@ class FlexibleArgumentParser(ArgumentParser):
|
|||||||
# Convert underscores to dashes and vice versa in argument names
|
# Convert underscores to dashes and vice versa in argument names
|
||||||
processed_args = list[str]()
|
processed_args = list[str]()
|
||||||
for i, arg in enumerate(args):
|
for i, arg in enumerate(args):
|
||||||
if arg.startswith('--'):
|
if arg.startswith("--help="):
|
||||||
|
FlexibleArgumentParser._search_keyword = arg.split(
|
||||||
|
'=', 1)[-1].lower()
|
||||||
|
processed_args.append("--help")
|
||||||
|
elif arg.startswith('--'):
|
||||||
if '=' in arg:
|
if '=' in arg:
|
||||||
key, value = arg.split('=', 1)
|
key, value = arg.split('=', 1)
|
||||||
key = pattern.sub(repl, key, count=1)
|
key = pattern.sub(repl, key, count=1)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user