Skip to content

Commit c98d10f

Browse files
x0rwPLeVasseur
authored andcommitted
Add --verbose flag to reduce logging to warning+ level, Add Progress bars (rustfoundation#67)
* Add --verbose flag to reduce logging to warning+ level, Add spinner * Remove spinner, Introduce tqdm, wrapping important loops in tqdm * Add tqdm to pyproject.toml * refactor: Add common.py to hold logger and tqdm wrapper, Disable tqdm progress on verbose mode, Remove Unused imports * Remove extra return * Apply suggestions from code review Co-authored-by: Pete LeVasseur <plevasseur@gmail.com> * Fix logger indentation * merge --verbose into --debug, remove time.sleep() --------- Co-authored-by: Pete LeVasseur <plevasseur@gmail.com>
1 parent 521c19d commit c98d10f

File tree

9 files changed

+108
-43
lines changed

9 files changed

+108
-43
lines changed

builder/build_cli.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def build_docs(
2828
serve: bool,
2929
debug: bool,
3030
offline: bool,
31-
spec_lock_consistency_check: bool
31+
spec_lock_consistency_check: bool,
3232
) -> Path:
3333
"""
3434
Builds the Sphinx documentation with the specified options.
@@ -70,10 +70,13 @@ def build_docs(
7070
conf_opt_values.append("enable_spec_lock_consistency=0")
7171
if offline:
7272
conf_opt_values.append("offline=1")
73+
if debug:
74+
conf_opt_values.append("debug=1")
75+
7376
# Only add the --define argument if there are options to define
7477
if conf_opt_values:
75-
args.append("--define")
7678
for opt in conf_opt_values:
79+
args.append("--define") # each option needs its own --define
7780
args.append(opt)
7881

7982
if serve:
@@ -172,6 +175,12 @@ def main(root):
172175
group.add_argument(
173176
"--xml", help="Generate Sphinx XML rather than HTML", action="store_true"
174177
)
178+
group.add_argument(
179+
"-v",
180+
"--verbose",
181+
help="Debug mode for the extensions, showing exceptions",
182+
action="store_true",
183+
)
175184
group.add_argument(
176185
"--debug",
177186
help="Debug mode for the extensions, showing exceptions",
@@ -183,6 +192,6 @@ def main(root):
183192
update_spec_lockfile(SPEC_CHECKSUM_URL, root / "src" / SPEC_LOCKFILE)
184193

185194
rendered = build_docs(
186-
root, "xml" if args.xml else "html", args.clear, args.serve, args.debug, args.offline, not args.ignore_spec_lock_diff
195+
root, "xml" if args.xml else "html", args.clear, args.serve, args.debug, args.offline, not args.ignore_spec_lock_diff,
187196
)
188197

exts/coding_guidelines/__init__.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
# SPDX-License-Identifier: MIT OR Apache-2.0
22
# SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors
3-
3+
44
from . import fls_checks
55
from . import write_guidelines_ids
66
from . import std_role
77
from . import fls_linking
88
from . import guidelines_checks
99

10-
from sphinx_needs.api import add_dynamic_function
11-
from sphinx.errors import SphinxError
10+
from .common import logger, get_tqdm, bar_format, logging
1211
from sphinx.domains import Domain
1312

14-
import logging
15-
16-
# Get the Sphinx logger
17-
logger = logging.getLogger('sphinx')
18-
1913
class CodingGuidelinesDomain(Domain):
2014
name = "coding-guidelines"
2115
label = "Rust Standard Library"
@@ -32,6 +26,19 @@ def get_objects(self):
3226
def merge_domaindata(self, docnames, other):
3327
pass # No domain data to merge
3428

29+
30+
def on_build_finished(app, exception):
31+
print("\nFinalizing build:")
32+
for _ in get_tqdm(iterable=range(1), desc="Finalizing",bar_format=bar_format):
33+
pass
34+
35+
outdir = app.outdir
36+
if exception is not None:
37+
print(" - Build failed")
38+
else:
39+
if not app.config.debug:
40+
print(f" + Build complete -> {outdir}")
41+
3542
def setup(app):
3643

3744
app.add_domain(CodingGuidelinesDomain)
@@ -46,6 +53,10 @@ def setup(app):
4653
rebuild="env", # Rebuild the environment when this changes
4754
types=[str],
4855
)
56+
app.add_config_value(name='debug',
57+
default=False,
58+
rebuild='env'
59+
)
4960
app.add_config_value(name='fls_paragraph_ids_url',
5061
default='https://rust-lang.github.io/fls/paragraph-ids.json',
5162
rebuild='env')
@@ -58,15 +69,16 @@ def setup(app):
5869
rebuild='env',
5970
types=[list],
6071
)
61-
72+
if app.config.debug:
73+
logger.setLevel(logging.INFO)
74+
common.disable_tqdm = True
75+
6276
app.connect('env-check-consistency', guidelines_checks.validate_required_fields)
63-
6477
app.connect('env-check-consistency', fls_checks.check_fls)
65-
6678
app.connect('build-finished', write_guidelines_ids.build_finished)
67-
6879
app.connect('build-finished', fls_linking.build_finished)
69-
80+
app.connect('build-finished', on_build_finished)
81+
7082
return {
7183
'version': '0.1',
7284
'parallel_read_safe': True,

exts/coding_guidelines/common.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
from tqdm import tqdm
3+
import logging
4+
5+
# This is a wrapper around tqdm that allows us to disable it with this global variable
6+
disable_tqdm = False
7+
def get_tqdm(**kwargs):
8+
kwargs['disable'] = disable_tqdm
9+
return tqdm(**kwargs)
10+
# Get the Sphinx logger
11+
logger = logging.getLogger('sphinx')
12+
logger.setLevel(logging.WARNING)
13+
14+
# This is what controls the progress bar format
15+
bar_format = "{l_bar}{bar}| {n_fmt}/{total_fmt} {postfix}"
16+

exts/coding_guidelines/fls_checks.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
# SPDX-License-Identifier: MIT OR Apache-2.0
22
# SPDX-FileCopyrightText: The Coding Guidelines Subcommittee Contributors
33

4+
5+
from .common import logger, get_tqdm, bar_format, logging
6+
import time
47
import requests
5-
import logging
68
import re
79
import json
8-
from pathlib import Path
910
from sphinx.errors import SphinxError
1011
from sphinx_needs.data import SphinxNeedsData
1112

12-
# Get the Sphinx logger
13-
logger = logging.getLogger('sphinx')
1413
fls_paragraph_ids_url = "https://rust-lang.github.io/fls/paragraph-ids.json"
1514

1615
class FLSValidationError(SphinxError):
@@ -88,9 +87,9 @@ def check_fls_exists_and_valid_format(app, env):
8887
# Regular expression for FLS ID validation
8988
# Format: fls_<12 alphanumeric chars including upper and lowercase>
9089
fls_pattern = re.compile(r'^fls_[a-zA-Z0-9]{9,12}$')
90+
9191
for need_id, need in needs.items():
9292
logger.debug(f"ID: {need_id}, Need: {need}")
93-
9493
if need.get('type') == 'guideline':
9594
fls_value = need.get("fls")
9695

@@ -128,9 +127,15 @@ def check_fls_ids_correct(app, env, fls_ids):
128127
# Track any errors found
129128
invalid_ids = []
130129

130+
# prefiltering: this is mainly done for tqdm progress
131+
guidelines = {k: v for k, v in needs.items() if v.get('type') == 'guideline'}
132+
133+
pbar = get_tqdm(iterable=guidelines.items(), desc="Validating FLS IDs",bar_format=bar_format, unit="need")
134+
131135
# Check each guideline's FLS reference
132-
for need_id, need in needs.items():
136+
for need_id, need in pbar:
133137
if need.get('type') == 'guideline':
138+
pbar.set_postfix(fls_id=need_id)
134139
fls_value = need.get("fls")
135140

136141
# Skip needs we already validated format for
@@ -141,17 +146,19 @@ def check_fls_ids_correct(app, env, fls_ids):
141146
if fls_value not in fls_ids:
142147
invalid_ids.append((need_id, fls_value))
143148
logger.warning(f"Need {need_id} references non-existent FLS ID: '{fls_value}'")
144-
145-
# Raise error if any invalid IDs were found
146-
if invalid_ids:
147-
error_message = "The following needs reference non-existent FLS IDs:\n"
148-
for need_id, fls_id in invalid_ids:
149-
error_message += f" - Need {need_id} references '{fls_id}'\n"
150-
logger.error(error_message)
151-
raise FLSValidationError(error_message)
152-
149+
150+
# Raise error if any invalid IDs were found
151+
if invalid_ids:
152+
error_message = "The following needs reference non-existent FLS IDs:\n"
153+
for need_id, fls_id in invalid_ids:
154+
error_message += f" - Need {need_id} references '{fls_id}'\n"
155+
logger.error(error_message)
156+
raise FLSValidationError(error_message)
157+
153158
logger.info("All FLS references in guidelines are valid")
154159

160+
pbar.close() # Ensure cleanup
161+
155162

156163
def gather_fls_paragraph_ids(app, json_url):
157164
"""
@@ -308,8 +315,14 @@ def check_fls_lock_consistency(app, env, fls_raw_data):
308315

309316
# Map of FLS IDs to guidelines that reference them
310317
fls_to_guidelines = {}
311-
for need_id, need in needs.items():
318+
319+
# prefiltering: this is mainly done for tqdm progress
320+
guidelines = {k: v for k, v in needs.items() if v.get('type') == 'guideline'}
321+
pbar = get_tqdm(iterable=guidelines.items(), desc="Checking fls lock consistency", bar_format=bar_format, unit="need")
322+
323+
for need_id, need in pbar:
312324
if need.get('type') == 'guideline':
325+
pbar.set_postfix(fls_id=need_id)
313326
fls_value = need.get("fls")
314327
if fls_value:
315328
if fls_value not in fls_to_guidelines:

exts/coding_guidelines/fls_linking.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import re
22
import os
3-
import logging
43
import sphinx
4+
from .common import logger
55

6-
# Get the Sphinx logger
7-
logger = logging.getLogger('sphinx')
86

97
def build_finished(app, exception):
108
"""Hook to run at the end of the build process."""

exts/coding_guidelines/guidelines_checks.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33

44
from sphinx.errors import SphinxError
55
from sphinx_needs.data import SphinxNeedsData
6-
import logging
6+
from .common import logger, get_tqdm, bar_format
77

8-
logger = logging.getLogger('sphinx')
98

109
class IntegrityCheckError(SphinxError):
1110
category = "Integrity Check Error"
@@ -20,20 +19,25 @@ def validate_required_fields(app, env):
2019

2120
required_fields = app.config.required_guideline_fields # Access the configured values
2221

23-
for key, value in needs.items():
22+
# prefiltering: this is mainly done for tqdm progress
23+
guidelines = {k: v for k, v in needs.items() if v.get('type') == 'guideline'}
24+
pbar = get_tqdm(iterable=guidelines.items(), desc="Checking for required fields", bar_format=bar_format, unit="need")
25+
26+
for key, value in pbar:
2427
if value.get('type') == 'guideline':
2528
missing_fields = []
2629
for field in required_fields:
30+
pbar.set_postfix(field=field if field is not None else "Missing")
2731
if value.get(field) in (None, '', []):
2832
missing_fields.append(field)
2933

34+
3035
if missing_fields:
3136
error_message = (
3237
f"Guideline '{value.get('title')}' (ID: {value.get('id')}) "
3338
f"in {value.get('docname')}:{value.get('lineno')} is missing the following required fields: "
3439
f"{', '.join(missing_fields)}"
3540
)
3641
logger.error(error_message)
37-
app.builder.statuscode = 1
42+
app.builder.statuscode = 1 # mark the build as failed (0 means success)
3843
raise IntegrityCheckError(error_message)
39-
logger.info("No missing required field")

exts/coding_guidelines/write_guidelines_ids.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33
"""
44
import hashlib
55
import json
6-
import logging
76
import os
87
from collections import defaultdict
98
import sphinx
109
from sphinx_needs.data import SphinxNeedsData
10+
from .common import logger
1111

12-
# Get the Sphinx logger
13-
logger = logging.getLogger('sphinx')
1412

1513
def calculate_checksum(content, options):
1614
"""Calculate a SHA-256 checksum of content and options"""

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ readme = "README.md"
66
requires-python = ">=3.12"
77
dependencies = [
88
"builder",
9+
"tqdm",
910
"sphinx>=8.2.3",
1011
"sphinx-autobuild>=2024.10.3",
1112
"sphinx-needs>=5.1.0",

uv.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)