Using Specialized CLI Parameter Types
To implement advanced input validation for CLI commands, use the specialized parameter types provided in src/flask/cli.py. These types allow you to handle complex inputs like platform-specific path lists and multi-modal SSL configurations.
Accept Multiple Paths in a Single Option
Use SeparatedPathType when you need an option to accept multiple file or directory paths separated by the operating system's path separator (: on Linux/macOS, ; on Windows).
import click
from flask.cli import SeparatedPathType
@click.command("watch")
@click.option(
"--extra-files",
type=SeparatedPathType(),
help="Paths to watch, separated by the OS path separator."
)
def watch_command(extra_files):
if extra_files:
for path in extra_files:
print(f"Watching: {path}")
In this codebase, SeparatedPathType is used by the run command for --extra-files and --exclude-patterns:
# src/flask/cli.py
@click.option(
"--extra-files",
default=None,
type=SeparatedPathType(),
help=(
"Extra files that trigger a reload on change. Multiple paths"
f" are separated by {os.path.pathsep!r}."
),
)
Implement Multi-Modal SSL Configuration
Use CertParamType to allow a single option to accept a file path, the literal string 'adhoc', or an import string for an ssl.SSLContext object.
import click
from flask.cli import CertParamType
@click.command("serve")
@click.option(
"--cert",
type=CertParamType(),
is_eager=True, # Important for cross-option validation
help="Path to cert, 'adhoc', or an SSLContext import string."
)
def serve_command(cert):
# cert will be a string path, the string 'adhoc', or an SSLContext object
pass
CertParamType performs validation in this order:
- File Path: Checks if the value is an existing file using
click.Path(exists=True). - Ad-hoc: Checks if the value is
'adhoc'(requires thecryptographylibrary). - Import String: Attempts to import the value as an
ssl.SSLContextobject usingflask.helpers.import_string.
Perform Cross-Option Validation
When using CertParamType with a file path, you often need a corresponding private key. You can implement this using a Click callback like _validate_key found in src/flask/cli.py.
# src/flask/cli.py
def _validate_key(ctx: click.Context, param: click.Parameter, value: t.Any) -> t.Any:
cert = ctx.params.get("cert")
is_adhoc = cert == "adhoc"
# ... (SSLContext check omitted for brevity)
if value is not None:
if is_adhoc:
raise click.BadParameter('When "--cert" is "adhoc", "--key" is not used.', ctx, param)
if not cert:
raise click.BadParameter('"--cert" must also be specified.', ctx, param)
# Combine cert and key into a tuple for the 'cert' parameter
ctx.params["cert"] = cert, value
else:
if cert and not is_adhoc:
raise click.BadParameter('Required when using "--cert".', ctx, param)
return value
@click.command("run")
@click.option("--cert", type=CertParamType(), is_eager=True)
@click.option("--key", type=click.Path(exists=True), callback=_validate_key, expose_value=False)
def run_command(cert):
# If a file path was used, cert is now (cert_path, key_path)
pass
Troubleshooting and Requirements
- Cryptography Library: Using
--cert adhocrequires thecryptographylibrary to be installed. If missing,CertParamTyperaises aclick.BadParametererror. - SSL Support:
CertParamTyperequires Python to be compiled with SSL support. It will raise an error if thesslmodule cannot be imported. - Path Separators:
SeparatedPathTypeusesos.pathsep. Ensure your users are aware that the separator changes based on the operating system (e.g., use:on Linux and;on Windows). - Eager Evaluation: When using
CertParamTypewith a callback-validated option like--key, you must setis_eager=Trueon the--certoption. This ensurescertis processed and available inctx.paramsbefore the--keycallback runs.