Coverage for src/griffe2md/main.py: 78.31%
65 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-20 11:44 +0100
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-20 11:44 +0100
1"""Main function of the program."""
3from __future__ import annotations
5import re
6import sys
7from pathlib import Path
8from typing import IO, TYPE_CHECKING
10import mdformat
11from griffe.docstrings import Parser
12from griffe.loader import GriffeLoader
13from jinja2 import Environment, FileSystemLoader
15from griffe2md import rendering
17if TYPE_CHECKING:
18 from griffe import Object
21def _output(text: str, to: IO | str | None = None) -> None:
22 if isinstance(to, str): 22 ↛ 26line 22 didn't jump to line 26, because the condition on line 22 was never false
23 with open(to, "w") as output:
24 output.write(text)
25 else:
26 if to is None:
27 to = sys.stdout
28 to.write(text)
31def prepare_context(obj: Object, config: dict | None = None) -> dict:
32 """Prepare Jinja context.
34 Parameters:
35 obj: A Griffe object.
36 config: The configuration options.
38 Returns:
39 The Jinja context.
40 """
41 config = config or dict(rendering.default_config)
42 if config["filters"]: 42 ↛ 45line 42 didn't jump to line 45, because the condition on line 42 was never false
43 config["filters"] = [(re.compile(filtr.lstrip("!")), filtr.startswith("!")) for filtr in config["filters"]]
45 heading_level = config["heading_level"]
46 try:
47 config["members_order"] = rendering.Order(config["members_order"])
48 except ValueError as error:
49 choices = "', '".join(item.value for item in rendering.Order)
50 raise ValueError(
51 f"Unknown members_order '{config['members_order']}', choose between '{choices}'.",
52 ) from error
54 summary = config["summary"]
55 if summary is True: 55 ↛ 62line 55 didn't jump to line 62, because the condition on line 55 was never false
56 config["summary"] = {
57 "attributes": True,
58 "functions": True,
59 "classes": True,
60 "modules": True,
61 }
62 elif summary is False:
63 config["summary"] = {
64 "attributes": False,
65 "functions": False,
66 "classes": False,
67 "modules": False,
68 }
69 else:
70 config["summary"] = {
71 "attributes": summary.get("attributes", False),
72 "functions": summary.get("functions", False),
73 "classes": summary.get("classes", False),
74 "modules": summary.get("modules", False),
75 }
77 return {
78 "config": config,
79 obj.kind.value: obj,
80 "heading_level": heading_level,
81 "root": True,
82 }
85def prepare_env(env: Environment | None = None) -> Environment:
86 """Prepare Jinja environment.
88 Parameters:
89 env: A Jinja environment.
91 Returns:
92 The Jinja environment.
93 """
94 env = env or Environment(
95 autoescape=False, # noqa: S701
96 loader=FileSystemLoader([Path(__file__).parent / "templates"]),
97 auto_reload=False,
98 )
99 env.filters["any"] = rendering.do_any
100 env.filters["heading"] = rendering.do_heading
101 env.filters["as_attributes_section"] = rendering.do_as_attributes_section
102 env.filters["as_classes_section"] = rendering.do_as_classes_section
103 env.filters["as_functions_section"] = rendering.do_as_functions_section
104 env.filters["as_modules_section"] = rendering.do_as_modules_section
105 env.filters["filter_objects"] = rendering.do_filter_objects
106 env.filters["format_code"] = rendering.do_format_code
107 env.filters["format_signature"] = rendering.do_format_signature
108 env.filters["format_attribute"] = rendering.do_format_attribute
109 env.filters["order_members"] = rendering.do_order_members
110 env.filters["split_path"] = rendering.do_split_path
111 env.filters["stash_crossref"] = lambda ref, length: ref
112 env.filters["from_private_package"] = rendering.from_private_package
114 return env
117def render_object_docs(obj: Object, config: dict | None = None) -> str:
118 """Render docs for a given object.
120 Parameters:
121 obj: The Griffe object to render docs for.
122 config: The rendering configuration.
124 Returns:
125 Markdown.
126 """
127 env = prepare_env()
128 context = prepare_context(obj, config)
129 rendered = env.get_template(f"{obj.kind.value}.md.jinja").render(**context)
130 return mdformat.text(rendered)
133def render_package_docs(package: str, config: dict | None = None) -> str:
134 """Render docs for a given package.
136 Parameters:
137 package: The package (name) to render docs for.
138 config: The rendering configuration.
140 Returns:
141 Markdown.
142 """
143 config = config or dict(rendering.default_config)
144 parser = config["docstring_style"] and Parser(config["docstring_style"])
145 loader = GriffeLoader(docstring_parser=parser)
146 module = loader.load(package)
147 loader.resolve_aliases(external=True)
148 return render_object_docs(module, config)
151def write_package_docs(package: str, config: dict | None = None, output: IO | str | None = None) -> None:
152 """Write docs for a given package to a file or stdout.
154 Parameters:
155 package: The package to render docs for.
156 config: The rendering configuration.
157 output: The file to write to.
158 """
159 _output(render_package_docs(package, config), to=output)