Skip to content

# click

## `xsdata.utils.click`

### `EnumChoice`

Bases: `Choice`

Custom click choice widget for enumerations.

Source code in `xsdata/utils/click.py`

    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116

    class EnumChoice(click.Choice):
        """Custom click choice widget for enumerations."""

        def __init__(self, enumeration: type[enum.Enum]):
            """Initialize the enum choice widget."""
            self.enumeration = enumeration
            super().__init__([e.value for e in enumeration])

        def convert(self, value: Any, *args: Any) -> enum.Enum:
            """Parse the value into an enumeration member."""
            return self.enumeration(value)

#### `__init__(enumeration)`

Initialize the enum choice widget.

Source code in `xsdata/utils/click.py`

    109
    110
    111
    112

    def __init__(self, enumeration: type[enum.Enum]):
        """Initialize the enum choice widget."""
        self.enumeration = enumeration
        super().__init__([e.value for e in enumeration])

#### `convert(value, *args)`

Parse the value into an enumeration member.

Source code in `xsdata/utils/click.py`

    114
    115
    116

    def convert(self, value: Any, *args: Any) -> enum.Enum:
        """Parse the value into an enumeration member."""
        return self.enumeration(value)

### `LogFormatter`

Bases: `Formatter`

Custom log formatter with click colors.

Source code in `xsdata/utils/click.py`

    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140

    class LogFormatter(logging.Formatter):
        """Custom log formatter with click colors."""

        colors: ClassVar[dict[str, Any]] = {
            "error": {"fg": "red"},
            "exception": {"fg": "red"},
            "critical": {"fg": "red"},
            "debug": {"fg": "blue"},
            "warning": {"fg": "yellow"},
        }

        def format(self, record: logging.LogRecord) -> str:
            """Format the log record with click styles."""
            if not record.exc_info:
                level = record.levelname.lower()
                msg = record.getMessage()
                if level in self.colors:
                    prefix = click.style(level, **self.colors[level])
                    msg = f"{prefix}: {msg}"
                return msg

            return super().format(record)  # pragma: no cover

#### `format(record)`

Format the log record with click styles.

Source code in `xsdata/utils/click.py`

    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140

    def format(self, record: logging.LogRecord) -> str:
        """Format the log record with click styles."""
        if not record.exc_info:
            level = record.levelname.lower()
            msg = record.getMessage()
            if level in self.colors:
                prefix = click.style(level, **self.colors[level])
                msg = f"{prefix}: {msg}"
            return msg

        return super().format(record)  # pragma: no cover

### `LogHandler`

Bases: `Handler`

Custom click log handler to record warnings.

Source code in `xsdata/utils/click.py`

    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170

    class LogHandler(logging.Handler):
        """Custom click log handler to record warnings."""

        def __init__(self, level: int | str = logging.NOTSET):
            """Initialize the log handler."""
            super().__init__(level)
            self.warnings: list[str] = []

        def emit(self, record: logging.LogRecord) -> None:
            """Override emit to record warnings."""
            try:
                msg = self.format(record)
                if record.levelno > logging.INFO:
                    self.warnings.append(msg)
                else:
                    click.echo(msg, err=True)
            except Exception:  # pragma: no cover
                self.handleError(record)

        def emit_warnings(self) -> None:
            """Print all recorded warnings to click stdout."""
            num = len(self.warnings)
            if num:
                click.echo(click.style(f"Warnings: {num}", bold=True))
                for msg in self.warnings:
                    click.echo(msg, err=True)

                self.warnings.clear()

#### `__init__(level=logging.NOTSET)`

Initialize the log handler.

Source code in `xsdata/utils/click.py`

    146
    147
    148
    149

    def __init__(self, level: int | str = logging.NOTSET):
        """Initialize the log handler."""
        super().__init__(level)
        self.warnings: list[str] = []

#### `emit(record)`

Override emit to record warnings.

Source code in `xsdata/utils/click.py`

    151
    152
    153
    154
    155
    156
    157
    158
    159
    160

    def emit(self, record: logging.LogRecord) -> None:
        """Override emit to record warnings."""
        try:
            msg = self.format(record)
            if record.levelno > logging.INFO:
                self.warnings.append(msg)
            else:
                click.echo(msg, err=True)
        except Exception:  # pragma: no cover
            self.handleError(record)

#### `emit_warnings()`

Print all recorded warnings to click stdout.

Source code in `xsdata/utils/click.py`

    162
    163
    164
    165
    166
    167
    168
    169
    170

    def emit_warnings(self) -> None:
        """Print all recorded warnings to click stdout."""
        num = len(self.warnings)
        if num:
            click.echo(click.style(f"Warnings: {num}", bold=True))
            for msg in self.warnings:
                click.echo(msg, err=True)

            self.warnings.clear()

### `URL`

Bases: `ParamType`

Click parameter type for validating HTTP/HTTPS URLs.

This ensures that only remote HTTP or HTTPS URLs are accepted, rejecting local file paths or file:// URIs.

Source code in `xsdata/utils/click.py`

    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200

    class URL(click.ParamType):
        """Click parameter type for validating HTTP/HTTPS URLs.

        This ensures that only remote HTTP or HTTPS URLs are accepted,
        rejecting local file paths or file:// URIs.
        """

        name = "url"

        def convert(self, value: str, param: Parameter | None, ctx: Context | None) -> str:
            """Validate that the value is an HTTP or HTTPS URL.

            Args:
                value: The parameter value to validate
                param: The parameter object
                ctx: The click context

            Returns:
                The validated URL string

            Raises:
                click.BadParameter: If the URL scheme is not http or https
            """
            parsed = urlparse(value)
            if parsed.scheme not in ("http", "https"):
                self.fail(f"Source must be an HTTP URL, got: {value}\n", param, ctx)

            return value

#### `convert(value, param, ctx)`

Validate that the value is an HTTP or HTTPS URL.

Parameters:

Name | Type | Description | Default
--- | --- | --- | ---
`value` | `str` | The parameter value to validate | required
`param` | `Parameter | None` | The parameter object | required
`ctx` | `Context | None` | The click context | required

Returns:

Type | Description
--- | ---
`str` | The validated URL string

Raises:

Type | Description
--- | ---
`BadParameter` | If the URL scheme is not http or https

Source code in `xsdata/utils/click.py`

    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200

    def convert(self, value: str, param: Parameter | None, ctx: Context | None) -> str:
        """Validate that the value is an HTTP or HTTPS URL.

        Args:
            value: The parameter value to validate
            param: The parameter object
            ctx: The click context

        Returns:
            The validated URL string

        Raises:
            click.BadParameter: If the URL scheme is not http or https
        """
        parsed = urlparse(value)
        if parsed.scheme not in ("http", "https"):
            self.fail(f"Source must be an HTTP URL, got: {value}\n", param, ctx)

        return value

### `model_options(obj)`

Decorate click commands to add model options.

Source code in `xsdata/utils/click.py`

    24
    25
    26
    27
    28
    29
    30
    31
    32

    def model_options(obj: Any) -> Callable[[FC], FC]:
        """Decorate click commands to add model options."""

        def decorator(f: F) -> F:
            for option in reversed(list(build_options(obj, ""))):
                option(f)
            return f

        return decorator

### `build_options(obj, parent)`

Build click options by a data class.

Source code in `xsdata/utils/click.py`

    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87

    def build_options(obj: Any, parent: str) -> Iterator[Callable[[FC], FC]]:
        """Build click options by a data class."""
        type_hints = get_type_hints(obj)
        doc_hints = get_doc_hints(obj)

        for field in fields(obj):
            type_hint = type_hints[field.name]
            doc_hint = doc_hints[field.name]
            name = field.metadata.get("cli", field.name)

            if not name:
                continue

            qname = f"{parent}.{field.name}".strip(".")

            if is_dataclass(type_hint):
                yield from build_options(type_hint, qname)
            else:
                is_flag = False
                opt_type = type_hint
                if name == "output":
                    opt_type = click.Choice(CodeWriter.generators.keys())
                    names = ["-o", "--output"]
                elif type_hint is bool:
                    is_flag = True
                    opt_type = None
                    name = text.kebab_case(name)
                    names = [f"--{name}/--no-{name}"]
                else:
                    if issubclass(type_hint, enum.Enum):
                        opt_type = EnumChoice(type_hint)

                    parts = text.split_words(name)
                    name = "-".join(parts)
                    name_short = "".join(part[0] for part in parts)
                    names = [f"--{name}", f"-{name_short}"]

                names.append("__".join(qname.split(".")))

                default_value = (
                    field.default.value
                    if isinstance(field.default, enum.Enum)
                    else field.default
                )
                doc_hint += f" [default: {default_value}]"

                yield click.option(
                    *names,
                    help=doc_hint,
                    is_flag=is_flag,
                    type=opt_type,
                    default=None,
                )

### `get_doc_hints(obj)`

Return a param-docstring map of the class arguments.

Source code in `xsdata/utils/click.py`

     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103

    def get_doc_hints(obj: Any) -> dict[str, str]:
        """Return a param-docstring map of the class arguments."""
        docstrings = inspect.getdoc(obj)
        assert docstrings is not None

        start = docstrings.index("Args:") + 6
        params = docstrings[start:].replace("\n        ", " ")

        result = {}
        for line in params.splitlines():
            param, hint = line.split(":", 1)
            result[param.strip()] = " ".join(hint.split())

        return result