robotframework-libtoc 1.3.0__tar.gz → 1.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: robotframework-libtoc
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: Docs and TOC generator for Robot Framework resources and libs
5
5
  Home-page: https://github.com/amochin/robotframework-libtoc
6
6
  License: Apache-2.0
@@ -13,6 +13,8 @@ Classifier: Programming Language :: Python :: 3.8
13
13
  Classifier: Programming Language :: Python :: 3.9
14
14
  Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Dist: importlib-resources (>=5.12,<5.13)
16
18
  Requires-Dist: robotframework (>=4)
17
19
  Description-Content-Type: text/markdown
18
20
 
@@ -37,7 +39,8 @@ in the intranet or uploaded as CI artifact - so everybody can easily access the
37
39
  - The tool goes through the specified folders with RF resources and it's **direct** subfolders
38
40
  - It looks for the **config files** named `.libtoc` which contain items you would like to create docs for:
39
41
  1. Paths to resource files in [glob format](https://en.wikipedia.org/wiki/Glob_(programming))
40
- 2. Installed RF libraries - names and necessary import params like described in [libdoc user guide](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#general-usage)
42
+ 2. Paths to resources files in [glob format](https://en.wikipedia.org/wiki/Glob_(programming)) inside packages loaded from the pythonpath
43
+ 3. Installed RF libraries - names and necessary import params like described in [libdoc user guide](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#general-usage)
41
44
  > Other libdoc CLI options (e.g. version or name of the output file) are not supported
42
45
  - Then it generates the docs using `libdoc` - both for files paths, resolved from the glob patterns, and for the installed libraries. The created HTML files are placed in the **libtoc output_dir** - keeping the original subfolder structure of resources
43
46
  - Finally it generates a **TOC (Table of Contents)** HTML page with links to all the generated HTML files.
@@ -50,6 +53,9 @@ in the intranet or uploaded as CI artifact - so everybody can easily access the
50
53
  **/*.resource
51
54
  **/*.py
52
55
 
56
+ [packages]
57
+ example_package:resources/**/*.resource
58
+
53
59
  [libs]
54
60
  # Use RF library names with params - like for libdoc
55
61
  SeleniumLibrary
@@ -57,7 +63,7 @@ Remote::http://10.0.0.42:8270
57
63
  # You can use environment variables in lib params
58
64
  SomeLib::$some_env_var/somepath
59
65
  ```
60
- > The config file must contain at least one of the sections - `[paths]`, `[libs]` or both
66
+ > The config file must contain at least one of the sections - `[paths]`, `[libs]`, `[packages]`
61
67
  ## How to install it
62
68
  ### System requirements
63
69
  - Python >=3.7
@@ -19,7 +19,8 @@ in the intranet or uploaded as CI artifact - so everybody can easily access the
19
19
  - The tool goes through the specified folders with RF resources and it's **direct** subfolders
20
20
  - It looks for the **config files** named `.libtoc` which contain items you would like to create docs for:
21
21
  1. Paths to resource files in [glob format](https://en.wikipedia.org/wiki/Glob_(programming))
22
- 2. Installed RF libraries - names and necessary import params like described in [libdoc user guide](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#general-usage)
22
+ 2. Paths to resources files in [glob format](https://en.wikipedia.org/wiki/Glob_(programming)) inside packages loaded from the pythonpath
23
+ 3. Installed RF libraries - names and necessary import params like described in [libdoc user guide](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#general-usage)
23
24
  > Other libdoc CLI options (e.g. version or name of the output file) are not supported
24
25
  - Then it generates the docs using `libdoc` - both for files paths, resolved from the glob patterns, and for the installed libraries. The created HTML files are placed in the **libtoc output_dir** - keeping the original subfolder structure of resources
25
26
  - Finally it generates a **TOC (Table of Contents)** HTML page with links to all the generated HTML files.
@@ -32,6 +33,9 @@ in the intranet or uploaded as CI artifact - so everybody can easily access the
32
33
  **/*.resource
33
34
  **/*.py
34
35
 
36
+ [packages]
37
+ example_package:resources/**/*.resource
38
+
35
39
  [libs]
36
40
  # Use RF library names with params - like for libdoc
37
41
  SeleniumLibrary
@@ -39,7 +43,7 @@ Remote::http://10.0.0.42:8270
39
43
  # You can use environment variables in lib params
40
44
  SomeLib::$some_env_var/somepath
41
45
  ```
42
- > The config file must contain at least one of the sections - `[paths]`, `[libs]` or both
46
+ > The config file must contain at least one of the sections - `[paths]`, `[libs]`, `[packages]`
43
47
  ## How to install it
44
48
  ### System requirements
45
49
  - Python >=3.7
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "robotframework-libtoc"
3
- version = "1.3.0"
3
+ version = "1.4.0"
4
4
  description = "Docs and TOC generator for Robot Framework resources and libs"
5
5
  authors = ["Andre Mochinin"]
6
6
  license = "Apache-2.0"
@@ -11,6 +11,7 @@ include = ["robotframework_libtoc/*template.html"]
11
11
  [tool.poetry.dependencies]
12
12
  python = ">=3.7"
13
13
  robotframework = ">=4"
14
+ importlib-resources = "~5.12"
14
15
 
15
16
  [tool.poetry.scripts]
16
17
  libtoc = "robotframework_libtoc.libtoc:main"
@@ -1,15 +1,20 @@
1
+ import argparse
2
+ import glob
1
3
  import os
2
- import sys
3
4
  import shutil
4
- import glob
5
- import argparse
5
+ import sys
6
6
  from datetime import datetime
7
+ from pathlib import Path
8
+
9
+ import importlib_resources
7
10
  import robot.libdoc
8
11
 
12
+
9
13
  class LibdocException(Exception):
10
14
  def __init__(self, broken_file):
11
15
  self.broken_file = broken_file
12
16
 
17
+
13
18
  def toc(links, timestamp, home_page_path, template_file=""):
14
19
  """
15
20
  Returns a HTML source code for TOC (table of contents) page, based on the template and including
@@ -21,32 +26,36 @@ def toc(links, timestamp, home_page_path, template_file=""):
21
26
  html_template = f.read()
22
27
 
23
28
  # double all brackets to make the further formatting work
24
- html_with_escaped_braces = html_template.replace('{', '{{')
25
- html_with_escaped_braces = html_with_escaped_braces.replace('}', '}}')
29
+ html_with_escaped_braces = html_template.replace("{", "{{")
30
+ html_with_escaped_braces = html_with_escaped_braces.replace("}", "}}")
26
31
 
27
32
  # and convert the formatting brackets back
28
- html_with_escaped_braces = html_with_escaped_braces.replace('{{}}', '{}')
33
+ html_with_escaped_braces = html_with_escaped_braces.replace("{{}}", "{}")
29
34
 
30
35
  return html_with_escaped_braces.format(home_page_path, links, timestamp)
31
36
 
37
+
32
38
  def homepage(timestamp, template_file=""):
33
39
  """
34
40
  Returns a HTML source code for a landing page, based on the template and includig the provided `timestamp`.
35
41
  """
36
42
  if template_file == "":
37
- template_file = os.path.join(os.path.dirname(__file__), "homepage_template.html")
43
+ template_file = os.path.join(
44
+ os.path.dirname(__file__), "homepage_template.html"
45
+ )
38
46
  with open(template_file, encoding="utf_8") as f:
39
47
  html_template = f.read()
40
48
  return html_template.format(timestamp)
41
49
 
50
+
42
51
  def read_config(config_file):
43
52
  """
44
53
  Parses the content of the `config_file` and returns a dictionary `{"paths":[values], "libs":[values]}`.
45
-
54
+
46
55
  The `paths` values are glob patterns, which can be resolved in real paths and used for generating docs using `libdoc`.
47
56
  The `libs` values are names of Robot Framework libraries with necessary import params - in the way to be also used for docs generation using `libdoc`.
48
57
 
49
- The config file must be formatted like this:
58
+ The config file must be formatted like this:
50
59
  ```
51
60
  # Comments starting with # are ignored
52
61
  [Paths]
@@ -59,17 +68,18 @@ def read_config(config_file):
59
68
  ```
60
69
  """
61
70
  sections = {
62
- "paths": {"markers":["[paths]"], "values":[]},
63
- "libs": {"markers": ["[libs]", "[libraries]"], "values":[]}
71
+ "paths": {"markers": ["[paths]"], "values": []},
72
+ "packages": {"markers": ["[packages]"], "values": []},
73
+ "libs": {"markers": ["[libs]", "[libraries]"], "values": []},
64
74
  }
65
-
75
+
66
76
  with open(config_file, encoding="utf8") as f:
67
77
  section_to_add = ""
68
- lines = f.readlines()
78
+ lines = f.readlines()
69
79
  for line in lines:
70
80
  stripped_line = line.strip()
71
81
  if len(stripped_line) > 0:
72
- if not stripped_line.startswith('#'): # comments
82
+ if not stripped_line.startswith("#"): # comments
73
83
  skip_line = False
74
84
  for section_name, section_content in sections.items():
75
85
  if stripped_line.lower() in section_content["markers"]:
@@ -78,20 +88,27 @@ def read_config(config_file):
78
88
  break
79
89
  if not skip_line and section_to_add:
80
90
  sections[section_to_add]["values"].append(stripped_line)
81
-
82
- return {"paths": sections["paths"]["values"], "libs": sections["libs"]["values"]}
91
+
92
+ return {
93
+ "paths": sections["paths"]["values"],
94
+ "packages": sections["packages"]["values"],
95
+ "libs": sections["libs"]["values"],
96
+ }
97
+
83
98
 
84
99
  def add_files_from_folder(folder, base_dir_path, root=True):
85
100
  """
86
- Creates a HTML source code with links to all HTML files in the `folder` and all it's subfolders.
101
+ Creates a HTML source code with links to all HTML files in the `folder` and all it's subfolders.
87
102
  The links contain file paths relative to the `base_dir_path`.
88
-
103
+
89
104
  The `root` parameter is needed for internal usage only - it's set to False during deeper recursive calls.
90
105
  """
91
106
  result_str = ""
92
107
  if not root: # means we're in the root - no collapsible need in this case
93
108
  result_str += """<button class="collapsible">{}</button>
94
- """.format(os.path.basename(folder))
109
+ """.format(
110
+ os.path.basename(folder)
111
+ )
95
112
 
96
113
  result_str += """<div class="collapsible_content">
97
114
  """
@@ -99,12 +116,16 @@ def add_files_from_folder(folder, base_dir_path, root=True):
99
116
  for item in os.listdir(folder):
100
117
  item_path = os.path.abspath(os.path.join(folder, item))
101
118
  if item.endswith(".html"):
102
- name_without_ext = os.path.splitext(item)[0]
119
+ name_without_ext = os.path.splitext(item)[0]
103
120
  result_str += """<a class="link_not_selected" href="{}" target="targetFrame">{}</a>
104
- """.format(os.path.relpath(item_path, base_dir_path), name_without_ext)
121
+ """.format(
122
+ os.path.relpath(item_path, base_dir_path), name_without_ext
123
+ )
105
124
  else:
106
125
  if os.path.isdir(item_path):
107
- result_str += add_files_from_folder(item_path, base_dir_path, root=False)
126
+ result_str += add_files_from_folder(
127
+ item_path, base_dir_path, root=False
128
+ )
108
129
 
109
130
  if not root:
110
131
  # end of the "collapsible_content"
@@ -112,6 +133,7 @@ def add_files_from_folder(folder, base_dir_path, root=True):
112
133
  """
113
134
  return result_str
114
135
 
136
+
115
137
  def create_docs_for_dir(resource_dir, output_dir, config_file):
116
138
  """
117
139
  Creates HTML docs using Robot Framework module `libdoc` for all resources and libraries in the `resource_dir`.
@@ -119,7 +141,7 @@ def create_docs_for_dir(resource_dir, output_dir, config_file):
119
141
 
120
142
  Paths of resource/python files and libraries, which the docs should be generated for, are configured using the `config_file`.
121
143
 
122
- The `config_file` must be formatted like this:
144
+ The `config_file` must be formatted like this:
123
145
  ```
124
146
  # Comments starting with # are ignored
125
147
  [Paths]
@@ -131,32 +153,90 @@ def create_docs_for_dir(resource_dir, output_dir, config_file):
131
153
  SomeLibrary::some_import_param
132
154
  ```
133
155
  """
134
- target_dir = os.path.join(os.path.abspath(output_dir), os.path.basename(resource_dir))
156
+ target_dir = os.path.join(
157
+ os.path.abspath(output_dir), os.path.basename(resource_dir)
158
+ )
135
159
  doc_config = read_config(config_file)
136
-
160
+
137
161
  resource_path_patterns = doc_config["paths"]
162
+ if resource_path_patterns:
163
+ print(">> Processing paths")
138
164
  broken_files = []
139
165
  for path_pattern in resource_path_patterns:
140
- for real_path in glob.glob(os.path.join(resource_dir, path_pattern), recursive=True):
166
+ for real_path in glob.glob(
167
+ os.path.join(resource_dir, path_pattern), recursive=True
168
+ ):
141
169
  relative_path = os.path.relpath(real_path, resource_dir)
142
- target_path = os.path.join(target_dir, relative_path.rpartition('.')[0] + ".html")
143
- print(f">> Generating docs for resource: {relative_path}")
170
+ target_path = os.path.join(
171
+ target_dir, relative_path.rpartition(".")[0] + ".html"
172
+ )
173
+ print(f">>> Processing file: {relative_path}")
144
174
  return_code = robot.libdoc.libdoc(real_path, target_path, quiet=True)
145
175
  if return_code > 0:
146
176
  broken_files.append(relative_path)
147
177
 
178
+ package_definitions = doc_config["packages"]
179
+ if package_definitions:
180
+ print("---")
181
+ packages = {}
182
+ broken_packages = []
183
+ for package_definition in package_definitions:
184
+ package_name, path_pattern = package_definition.split(":", 1)
185
+ if package_name not in packages:
186
+ packages[package_name] = []
187
+ packages[package_name].append(path_pattern)
188
+
189
+ for package_name, paths_patterns in packages.items():
190
+ print(f">> Processing package: {package_name}")
191
+ try:
192
+ package_anchor = importlib_resources.files(package_name)
193
+ except ModuleNotFoundError as e:
194
+ print(f"Importing package '{package_name}' failed: {e}")
195
+ broken_packages.append(package_name)
196
+ else:
197
+ with importlib_resources.as_file(package_anchor) as package_path:
198
+ for path_pattern in paths_patterns:
199
+ package_resource_files = package_path.glob(path_pattern)
200
+ for real_path in package_resource_files:
201
+ relative_path = Path(package_name) / real_path.relative_to(
202
+ package_path
203
+ )
204
+ target_path = os.path.join(
205
+ target_dir, relative_path.with_suffix(".html")
206
+ )
207
+ print(f">>> Processing file: {relative_path}")
208
+ return_code = robot.libdoc.libdoc(
209
+ real_path, target_path, quiet=True
210
+ )
211
+ if return_code > 0:
212
+ broken_packages.append(relative_path)
213
+
148
214
  libs = doc_config["libs"]
215
+ if libs:
216
+ print("---")
217
+ print(">> Processing libraries")
149
218
  broken_libs = []
150
219
  for lib in libs:
151
220
  lib_str_with_resolved_vars = os.path.expandvars(lib)
152
- target_path = os.path.join(target_dir, lib_str_with_resolved_vars.partition("::")[0] + ".html")
153
- print(f">> Generating docs for library: {lib_str_with_resolved_vars}")
154
- return_code = robot.libdoc.libdoc(lib_str_with_resolved_vars, target_path, quiet=True)
221
+ target_path = os.path.join(
222
+ target_dir, lib_str_with_resolved_vars.partition("::")[0] + ".html"
223
+ )
224
+ print(f">>> Processing lib: {lib_str_with_resolved_vars}")
225
+ return_code = robot.libdoc.libdoc(
226
+ lib_str_with_resolved_vars, target_path, quiet=True
227
+ )
155
228
  if return_code > 0:
156
229
  broken_libs.append(lib_str_with_resolved_vars)
157
- return broken_files, broken_libs
230
+ return broken_files, broken_packages, broken_libs
158
231
 
159
- def create_toc(html_docs_dir, toc_file="keyword_docs.html", homepage_file="homepage.html", toc_template="", homepage_template=""):
232
+
233
+ def create_toc(
234
+ html_docs_dir,
235
+ toc_file="keyword_docs.html",
236
+ homepage_file="homepage.html",
237
+ toc_template="",
238
+ homepage_template="",
239
+ ):
160
240
  """
161
241
  Generates a `toc_file` (Table of Contents) HTML page with links to all HTML files inside the `html_docs_dir` and all it's subfolders.
162
242
 
@@ -165,7 +245,7 @@ def create_toc(html_docs_dir, toc_file="keyword_docs.html", homepage_file="homep
165
245
 
166
246
  All the content of the `html_docs_dir` will be moved in the new `src` subfolder, leaving only the `toc_file` directly inside.
167
247
  """
168
- print(f">>> Creating TOC in: {os.path.abspath(html_docs_dir)}")
248
+ print(f"> Creating TOC in: {os.path.abspath(html_docs_dir)}")
169
249
  # move all subfolders and files into "src"
170
250
  src_subdir = os.path.join(html_docs_dir, "src")
171
251
  os.makedirs(src_subdir, exist_ok=True)
@@ -176,79 +256,145 @@ def create_toc(html_docs_dir, toc_file="keyword_docs.html", homepage_file="homep
176
256
  src = os.path.join(html_docs_dir, doc_element)
177
257
  target = os.path.join(src_subdir, doc_element)
178
258
  shutil.move(src, target)
179
-
259
+
180
260
  # create homepage in "src"
181
261
  homepage_path = os.path.join(src_subdir, homepage_file)
182
- current_date_time = datetime.now().strftime('%d.%m.%Y %H:%M:%S')
262
+ current_date_time = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
183
263
  doc_files_links = add_files_from_folder(src_subdir, os.path.abspath(html_docs_dir))
184
- with open(homepage_path, 'w', encoding="utf8") as f:
264
+ with open(homepage_path, "w", encoding="utf8") as f:
185
265
  f.write(homepage(current_date_time, homepage_template))
186
266
 
187
267
  # create TOC
188
268
  toc_file_path = os.path.join(html_docs_dir, toc_file)
189
- with open(toc_file_path, 'w', encoding="utf8") as f:
190
- f.write(toc(doc_files_links, current_date_time, os.path.relpath(homepage_path, os.path.abspath(html_docs_dir)), toc_template))
191
-
269
+ with open(toc_file_path, "w", encoding="utf8") as f:
270
+ f.write(
271
+ toc(
272
+ doc_files_links,
273
+ current_date_time,
274
+ os.path.relpath(homepage_path, os.path.abspath(html_docs_dir)),
275
+ toc_template,
276
+ )
277
+ )
278
+
279
+ print("---")
192
280
  print("TOC finished. Output file: {}".format(os.path.abspath(toc_file_path)))
193
281
 
282
+
194
283
  def main():
195
- parser = argparse.ArgumentParser(description="Generates keyword docs using libdoc based on config files in direct subfolders of the resources dir and creates a TOC")
196
- parser.add_argument("resources_dirs", nargs="+", help="Folders with resources and keywords files")
197
- parser.add_argument("-d", "--output_dir", default="docs", help="Folder to create the docs in")
198
- parser.add_argument("--config_file", default=".libtoc", help="File in each folder with docs generation configs")
199
- parser.add_argument("--toc_file", default="keyword_docs.html", help="Name of the TOC file generated")
200
- parser.add_argument("--toc_template", default="", help = "Custom HTML template for the TOC file")
201
- parser.add_argument("--homepage_template", default="", help = "Custom HTML template for the homepage file")
202
- parser.add_argument("-P", "--pythonpath", default="", help="Additional locations where to search for libraries and resources similarly as when running tests")
284
+ parser = argparse.ArgumentParser(
285
+ description="Generates keyword docs using libdoc based on config files in direct subfolders of the resources dir and creates a TOC"
286
+ )
287
+ parser.add_argument(
288
+ "resources_dirs", nargs="+", help="Folders with resources and keywords files"
289
+ )
290
+ parser.add_argument(
291
+ "-d", "--output_dir", default="docs", help="Folder to create the docs in"
292
+ )
293
+ parser.add_argument(
294
+ "--config_file",
295
+ default=".libtoc",
296
+ help="File in each folder with docs generation configs",
297
+ )
298
+ parser.add_argument(
299
+ "--toc_file", default="keyword_docs.html", help="Name of the TOC file generated"
300
+ )
301
+ parser.add_argument(
302
+ "--toc_template", default="", help="Custom HTML template for the TOC file"
303
+ )
304
+ parser.add_argument(
305
+ "--homepage_template",
306
+ default="",
307
+ help="Custom HTML template for the homepage file",
308
+ )
309
+ parser.add_argument(
310
+ "-P",
311
+ "--pythonpath",
312
+ default="",
313
+ help="Additional locations where to search for libraries and resources similarly as when running tests",
314
+ )
203
315
 
204
316
  args = parser.parse_args()
205
317
 
206
318
  if args.pythonpath:
207
319
  sys.path.insert(0, args.pythonpath)
208
320
 
209
-
210
321
  if os.path.isdir(args.output_dir):
211
322
  print(f"Output dir already exists, deleting it: {args.output_dir}")
212
323
  shutil.rmtree(args.output_dir)
213
324
  total_broken_files = []
325
+ total_broken_packages = []
214
326
  total_broken_libs = []
215
-
327
+
216
328
  for resources_dir in args.resources_dirs:
217
329
  print("")
218
330
  print(f"> Creating docs for dir: {os.path.abspath(resources_dir)}")
219
331
  for child_element in os.listdir(resources_dir):
220
332
  child_element_path = os.path.join(resources_dir, child_element)
221
333
  current_broken_files = []
334
+ current_broken_packages = []
222
335
  current_broken_libs = []
223
336
  if os.path.isdir(child_element_path):
224
337
  config_file = os.path.join(child_element_path, args.config_file)
225
338
  if os.path.isfile(config_file):
226
- current_broken_files, current_broken_libs = create_docs_for_dir(child_element_path, args.output_dir, os.path.abspath(config_file))
339
+ (
340
+ current_broken_files,
341
+ current_broken_packages,
342
+ current_broken_libs,
343
+ ) = create_docs_for_dir(
344
+ child_element_path,
345
+ args.output_dir,
346
+ os.path.abspath(config_file),
347
+ )
227
348
  elif child_element == args.config_file:
228
- current_broken_files, current_broken_libs = create_docs_for_dir(resources_dir, args.output_dir, os.path.abspath(os.path.join(resources_dir, args.config_file)))
229
-
230
- total_broken_files += current_broken_files
349
+ current_broken_files, current_broken_packages, current_broken_libs = (
350
+ create_docs_for_dir(
351
+ resources_dir,
352
+ args.output_dir,
353
+ os.path.abspath(os.path.join(resources_dir, args.config_file)),
354
+ )
355
+ )
356
+
357
+ total_broken_files += current_broken_files
358
+ total_broken_packages += current_broken_packages
231
359
  total_broken_libs += current_broken_libs
232
-
360
+
233
361
  if total_broken_files:
234
362
  print("")
235
- print(f"---> !!! Errors occurred while generating docs for {len(total_broken_files)} files (see details above):")
363
+ print(
364
+ f"---> !!! Errors occurred while generating docs for {len(total_broken_files)} files (see details above):"
365
+ )
236
366
  for f in total_broken_files:
237
367
  print(f" - {f}")
238
368
 
369
+ if total_broken_packages:
370
+ print("")
371
+ print(
372
+ f"---> !!! Errors occurred while generating docs for {len(total_broken_packages)} packages (see details above):"
373
+ )
374
+ for f in total_broken_packages:
375
+ print(f" - {f}")
376
+
239
377
  if total_broken_libs:
240
378
  print("")
241
- print(f"---> !!! Errors occurred while generating docs for {len(total_broken_libs)} libs (see details above):")
379
+ print(
380
+ f"---> !!! Errors occurred while generating docs for {len(total_broken_libs)} libs (see details above):"
381
+ )
242
382
  for l in total_broken_libs:
243
383
  print(f" - {l}")
244
384
 
245
385
  if os.path.isdir(args.output_dir):
246
386
  print("")
247
- create_toc(args.output_dir, args.toc_file, toc_template=args.toc_template, homepage_template=args.homepage_template)
387
+ create_toc(
388
+ args.output_dir,
389
+ args.toc_file,
390
+ toc_template=args.toc_template,
391
+ homepage_template=args.homepage_template,
392
+ )
248
393
  else:
249
394
  print("No docs were created!")
250
395
 
251
396
  print("")
252
397
 
398
+
253
399
  if __name__ == "__main__":
254
400
  main()