Skip to content

click

xsdata.utils.click

EnumChoice

Bases: Choice

Custom click choice widget for enumerations.

Source code in xsdata/utils/click.py
109
110
111
112
113
114
115
116
117
118
class EnumChoice(click.Choice):
    """Custom click choice widget for enumerations."""

    def __init__(self, enumeration: Type[enum.Enum]):
        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)

convert(value, *args)

Parse the value into an enumeration member.

Source code in xsdata/utils/click.py
116
117
118
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
class LogFormatter(logging.Formatter):
    """Custom log formatter with click colors."""

    colors: 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(f"{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
132
133
134
135
136
137
138
139
140
141
142
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(f"{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
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
171
class LogHandler(logging.Handler):
    """Custom click log handler to record warnings."""

    def __init__(self, level: Union[int, str] = logging.NOTSET):
        super().__init__(level)
        self.warnings: List[str] = []

    def emit(self, record: logging.LogRecord):
        """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):
        """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()

emit(record)

Override emit to record warnings.

Source code in xsdata/utils/click.py
152
153
154
155
156
157
158
159
160
161
def emit(self, record: logging.LogRecord):
    """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
163
164
165
166
167
168
169
170
171
def emit_warnings(self):
    """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()

model_options(obj)

Decorate click commands to add model options.

Source code in xsdata/utils/click.py
27
28
29
30
31
32
33
34
35
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
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
88
89
90
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
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
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