Coverage for app/venv/lib/python3.14/site-packages/weblate/addons/management/commands/list_addons.py: 0%
65 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
1# Copyright © Michal Čihař <michal@weblate.org>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
5from __future__ import annotations
7from textwrap import wrap
8from typing import TYPE_CHECKING
10from weblate.addons.events import POST_CONFIGURE_EVENTS, AddonEvent
11from weblate.addons.models import ADDONS, Addon
12from weblate.trans.models import Component, Project
13from weblate.utils.management.base import BaseCommand
14from weblate.utils.rst import format_rst_string, format_table
16if TYPE_CHECKING:
17 from collections.abc import Iterable
19SKIP_FIELDS: tuple[tuple[str, str]] = (
20 ("weblate.flags.bulk", "path"), # Used internally only
21)
24def event_link(event: AddonEvent) -> str:
25 return f"addon-event-{event.label.lower().replace(' ', '-')}"
28def sorted_events(events: Iterable[AddonEvent]) -> Iterable[AddonEvent]:
29 return sorted(events, key=lambda event: event.label)
32class Command(BaseCommand):
33 help = "List installed add-ons"
35 @staticmethod
36 def get_help_text(field, name: str) -> str:
37 result = []
38 if field.help_text:
39 result.append(format_rst_string(field.help_text))
40 choices = getattr(field, "choices", None)
41 if choices and name not in {
42 "component",
43 "engines",
44 "file_format",
45 "source",
46 "target",
47 }:
48 if result:
49 result.append("")
51 result.extend(
52 (
53 ".. list-table:: Available choices:",
54 " :width: 100%",
55 "",
56 )
57 )
58 for value, description in choices:
59 result.extend(
60 (
61 f" * - ``{value}``".replace("\\", "\\\\"),
62 f" - {format_rst_string(description)}".replace(
63 "\\", "\\\\"
64 ),
65 )
66 )
67 return "\n".join(result)
69 def handle(self, *args, **options) -> None:
70 """List installed add-ons."""
71 self.stdout.write("""..
72 Partly generated using ./manage.py list_addons
73""")
74 self.stdout.write(".. _addon-event-install:\n\n")
75 self.stdout.write("Add-on installation\n")
76 self.stdout.write("-------------------\n\n")
77 for event in sorted_events(AddonEvent):
78 self.stdout.write(f".. _{event_link(event)}:\n\n")
79 self.stdout.write(f"{event.label}\n")
80 self.stdout.write("-" * len(event.label))
81 self.stdout.write("\n\n")
82 self.stdout.write("\n")
83 fake_addon = Addon(component=Component(project=Project(pk=-1), pk=-1))
84 for addon_name, obj in sorted(ADDONS.items()):
85 self.stdout.write(f".. _addon-{obj.name}:")
86 self.stdout.write("\n")
87 self.stdout.write(str(obj.verbose))
88 self.stdout.write("-" * len(obj.verbose))
89 self.stdout.write("\n")
90 self.stdout.write(f":Add-on ID: ``{obj.name}``")
91 prefix = ":Configuration: "
92 if obj.settings_form:
93 form = obj(fake_addon).get_settings_form(None)
94 table: list[list[str | list[list[str]]]] = [
95 [
96 f"``{name}``",
97 str(field.label),
98 self.get_help_text(field, name),
99 ]
100 for name, field in form.fields.items()
101 if (addon_name, name) not in SKIP_FIELDS
102 ]
104 for table_row in format_table(table, None):
105 self.stdout.write(f"{prefix}{table_row}")
106 if not prefix.isspace():
107 prefix = " " * len(prefix)
108 else:
109 self.stdout.write(f"{prefix}`This add-on has no configuration.`")
110 events = ", ".join(
111 f":ref:`{event_link(event)}`" for event in sorted_events(obj.events)
112 )
113 if POST_CONFIGURE_EVENTS & set(obj.events):
114 events = f":ref:`addon-event-install`, {events}"
115 self.stdout.write(f":Triggers: {events}")
116 self.stdout.write("\n")
117 self.stdout.write("\n".join(wrap(str(obj.description), 79)))
118 self.stdout.write("\n")