summaryrefslogtreecommitdiff
path: root/docs/render_cli.py
diff options
context:
space:
mode:
authorBernát Gábor <bgabor8@bloomberg.net>2020-02-04 14:51:00 +0000
committerGitHub <noreply@github.com>2020-02-04 14:51:00 +0000
commit7a5d03fe15f1d467f7b224c528b95eccc080b2c1 (patch)
tree7d1ccccac7eb588221fd3aa26c9d9227293ba179 /docs/render_cli.py
parent786c3d0addb5d6b155dc73f8bd444bde00877045 (diff)
downloadvirtualenv-7a5d03fe15f1d467f7b224c528b95eccc080b2c1.tar.gz
rewrite the documentation (#1519)
Diffstat (limited to 'docs/render_cli.py')
-rw-r--r--docs/render_cli.py230
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",
+)