diff options
author | Bernát Gábor <bgabor8@bloomberg.net> | 2020-02-04 14:51:00 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-04 14:51:00 +0000 |
commit | 7a5d03fe15f1d467f7b224c528b95eccc080b2c1 (patch) | |
tree | 7d1ccccac7eb588221fd3aa26c9d9227293ba179 /docs/render_cli.py | |
parent | 786c3d0addb5d6b155dc73f8bd444bde00877045 (diff) | |
download | virtualenv-7a5d03fe15f1d467f7b224c528b95eccc080b2c1.tar.gz |
rewrite the documentation (#1519)
Diffstat (limited to 'docs/render_cli.py')
-rw-r--r-- | docs/render_cli.py | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/docs/render_cli.py b/docs/render_cli.py new file mode 100644 index 0000000..af350d1 --- /dev/null +++ b/docs/render_cli.py @@ -0,0 +1,230 @@ +from argparse import SUPPRESS +from collections import namedtuple +from contextlib import contextmanager + +from docutils import nodes as n +from docutils.parsers.rst.directives import unchanged_required +from sphinx.util.docutils import SphinxDirective +from sphinxarg.parser import parse_parser + +from virtualenv.run.plugin.base import ComponentBuilder + +TableRow = namedtuple("TableRow", ["names", "default", "choices", "help"]) + +TextAsDefault = namedtuple("TextAsDefault", ["text"]) + +CUSTOM = { + "discovery": ComponentBuilder.entry_points_for("virtualenv.discovery"), + "creator": ComponentBuilder.entry_points_for("virtualenv.create"), + "seeder": ComponentBuilder.entry_points_for("virtualenv.seed"), + "activators": ComponentBuilder.entry_points_for("virtualenv.activate"), +} + + +class CliTable(SphinxDirective): + name = "table_cli" + option_spec = dict(module=unchanged_required, func=unchanged_required) + + def run(self): + module_name, attr_name = self.options["module"], self.options["func"] + parser_creator = getattr(__import__(module_name, fromlist=[attr_name]), attr_name) + core_result = parse_parser(parser_creator()) + core_result["action_groups"] = [i for i in core_result["action_groups"] if i["title"] not in CUSTOM] + + content = [] + for i in core_result["action_groups"]: + content.append(self._build_table(i["options"], i["title"], i["description"])) + for key, name_to_class in CUSTOM.items(): + section = n.section("", ids=["section-{}".format(key)]) + title = n.title("", key) + section += title + self.state.document.note_implicit_target(title) + content.append(section) + results = {} + + for name, class_n in name_to_class.items(): + with self._run_parser(class_n, key, name): + cmd = ["--{}".format(key), name] + parser_result = parse_parser(parser_creator(cmd)) + opt_group = next(i["options"] for i in parser_result["action_groups"] if i["title"] == key) + results[name] = opt_group + core_names = set.intersection(*list({tuple(i["name"]) for i in v} for v in results.values())) + if core_names: + rows = [i for i in next(iter(results.values())) if tuple(i["name"]) in core_names] + content.append( + self._build_table(rows, title="core", description="options shared across all {}".format(key)) + ) + for name, group in results.items(): + rows = [i for i in group if tuple(i["name"]) not in core_names] + if rows: + content.append( + self._build_table(rows, title=name, description="options specific to {} {}".format(key, name)) + ) + return content + + @contextmanager + def _run_parser(self, class_n, key, name): + test_name = {"creator": "can_create", "activators": "supports"} + func_name = test_name.get(key) + try: + if func_name is not None: + prev = getattr(class_n, func_name) + + def a(*args, **kwargs): + prev(*args, **kwargs) + if key == "activators": + return True + elif key == "creator": + if name == "venv": + from virtualenv.create.via_global_ref.venv import Meta + + return Meta(True, True) + from virtualenv.create.via_global_ref.builtin.via_global_self_do import Meta + + return Meta([], True, True) + raise RuntimeError + + setattr(class_n, func_name, a) + yield + finally: + if func_name is not None: + # noinspection PyUnboundLocalVariable + setattr(class_n, func_name, prev) + + def _build_table(self, options, title, description): + table = n.table() + table["classes"] += ["colwidths-auto"] + + options_group = n.tgroup(cols=3) + table += options_group + for _ in range(3): + options_group += n.colspec() + body = self._make_table_body(self.build_rows(options), title, description) + options_group += body + return table + + plugins = { + "creator": "virtualenv.create", + "seed": "virtualenv.seed", + "activators": "virtualenv.activate", + "discovery": "virtualenv.discovery", + } + + @staticmethod + def build_rows(options): + result = [] + for option in options: + names = option["name"] + default = option["default"] + if default is not None: + if isinstance(default, str) and default[0] == default[-1] and default[0] == '"': + default = default[1:-1] + if default == SUPPRESS: + default = None + choices = option.get("choices") + key = names[0].strip("-") + if key in CliTable.plugins: + choices = list(ComponentBuilder.entry_points_for(CliTable.plugins[key]).keys()) + help_text = option["help"] + row = TableRow(names, default, choices, help_text) + result.append(row) + return result + + def _make_table_body(self, rows, title, description): + t_body = n.tbody() + header_row = n.paragraph() + header_row += n.strong(text=title) + if description: + header_row += n.Text(" ⇒ ") + header_row += n.Text(description) + t_body += n.row("", n.entry("", header_row, morecols=2)) + for row in rows: + name_list = self._get_targeted_names(row) + default = CliTable._get_default(row) + help_text = CliTable._get_help_text(row) + row_node = n.row("", n.entry("", name_list), n.entry("", default), n.entry("", help_text)) + t_body += row_node + return t_body + + def _get_targeted_names(self, row): + names = [name.lstrip("-") for name in row.names] + target = n.target("", "", ids=names, names=names) + self.register_target_option(target) + first = True + for name, orig in zip(names, row.names): + if first: + first = False + else: + target += n.Text(", ") + self_ref = n.reference(refid=name) + self_ref += n.literal(text=orig) + target += self_ref + para = n.paragraph(text="") + para += target + return para + + @staticmethod + def _get_help_text(row): + name = row.names[0] + if name in ("--creator", "--clear-app-data"): + content = row.help[: row.help.index("(") - 1] + else: + content = row.help + if name in ("--setuptools", "--pip", "--wheel"): + text = row.help + at = text.index(" bundle ") + help_body = n.paragraph("") + help_body += n.Text(text[: at + 1]) + help_body += n.literal(text="bundle") + help_body += n.Text(text[at + 7 :]) + else: + help_body = n.paragraph("", "", n.Text(content)) + if row.choices is not None: + help_body += n.Text("; choice of: ") + first = True + for choice in row.choices: + if first: + first = False + else: + help_body += n.Text(", ") + help_body += n.literal(text=choice) + return help_body + + @staticmethod + def _get_default(row): + default = row.default + name = row.names[0] + if name == "-p": + default_body = n.Text("the python executable virtualenv is installed into") + elif name == "--activators": + default_body = n.Text("comma separated list of activators supported") + elif name == "--creator": + default_body = n.paragraph("") + default_body += n.literal(text="builtin") + default_body += n.Text(" if exist, else ") + default_body += n.literal(text="venv") + else: + if default is None: + default_body = n.paragraph("", text="") + else: + default_body = n.literal(text=default if isinstance(default, str) else str(default)) + return default_body + + def register_target_option(self, target) -> None: + domain = self.env.get_domain("std") + self.state.document.note_explicit_target(target) + for key in target["ids"]: + domain.add_program_option(None, key, self.env.docname, key) + + +def literal_data(rawtext, app, type, slug, options): + """Create a link to a BitBucket resource.""" + of_class = type.split(".") + data = getattr(__import__(".".join(of_class[:-1]), fromlist=[of_class[-1]]), of_class[-1]) + return [n.literal("", text=",".join(data))], [] + + +__all__ = ( + "CliTable", + "literal_data", +) |