Skip to main content

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:

  1. File Path: Checks if the value is an existing file using click.Path(exists=True).
  2. Ad-hoc: Checks if the value is 'adhoc' (requires the cryptography library).
  3. Import String: Attempts to import the value as an ssl.SSLContext object using flask.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 adhoc requires the cryptography library to be installed. If missing, CertParamType raises a click.BadParameter error.
  • SSL Support: CertParamType requires Python to be compiled with SSL support. It will raise an error if the ssl module cannot be imported.
  • Path Separators: SeparatedPathType uses os.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 CertParamType with a callback-validated option like --key, you must set is_eager=True on the --cert option. This ensures cert is processed and available in ctx.params before the --key callback runs.