Skip to content

Commit

Permalink
Don't abort mkdocs serve for build errors during rebuilds (#3255)
Browse files Browse the repository at this point in the history
  • Loading branch information
oprypin authored Jun 12, 2023
1 parent 82aa863 commit b6ff158
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 14 deletions.
4 changes: 2 additions & 2 deletions mkdocs/commands/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ def build(
counts = warning_counter.get_counts()
if counts:
msg = ', '.join(f'{v} {k.lower()}s' for k, v in counts)
raise Abort(f'\nAborted with {msg} in strict mode!')
raise Abort(f'Aborted with {msg} in strict mode!')

log.info(f'Documentation built in {time.monotonic() - start:.2f} seconds')

Expand All @@ -362,7 +362,7 @@ def build(
config.plugins.run_event('build_error', error=e)
if isinstance(e, BuildError):
log.error(str(e))
raise Abort('\nAborted with a BuildError!')
raise Abort('Aborted with a BuildError!')
raise

finally:
Expand Down
5 changes: 1 addition & 4 deletions mkdocs/commands/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,7 @@ def builder(config: MkDocsConfig | None = None):
config = get_config()

# combine CLI watch arguments with config file values
if config.watch is None:
config.watch = watch
else:
config.watch.extend(watch)
config.watch.extend(watch)

# Override a few config settings after validation
config.site_url = f'http://{config.dev_addr}{mount_path(config)}'
Expand Down
6 changes: 3 additions & 3 deletions mkdocs/config/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,10 @@ def load_config(config_file: str | IO | None = None, **kwargs) -> MkDocsConfig:
log.debug(f"Config value '{key}' = {value!r}")

if len(errors) > 0:
raise exceptions.Abort(f"Aborted with {len(errors)} Configuration Errors!")
elif cfg['strict'] and len(warnings) > 0:
raise exceptions.Abort(f"Aborted with {len(errors)} configuration errors!")
elif cfg.strict and len(warnings) > 0:
raise exceptions.Abort(
f"Aborted with {len(warnings)} Configuration Warnings in 'strict' mode!"
f"Aborted with {len(warnings)} configuration warnings in 'strict' mode!"
)

return cfg
6 changes: 4 additions & 2 deletions mkdocs/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ class MkDocsException(ClickException):
not be raised directly. One of the subclasses should be raised instead."""


class Abort(MkDocsException):
class Abort(MkDocsException, SystemExit):
"""Abort the build"""

code = 1

def show(self, *args, **kwargs) -> None:
echo(self.format_message())
echo('\n' + self.format_message())


class ConfigurationError(MkDocsException):
Expand Down
16 changes: 14 additions & 2 deletions mkdocs/livereload/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
import socket
import socketserver
import string
import sys
import threading
import time
import traceback
import urllib.parse
import warnings
import wsgiref.simple_server
Expand Down Expand Up @@ -185,8 +187,18 @@ def _build_loop(self):
funcs = list(self._to_rebuild)
self._to_rebuild.clear()

for func in funcs:
func()
try:
for func in funcs:
func()
except Exception as e:
if isinstance(e, SystemExit):
print(e, file=sys.stderr)
else:
traceback.print_exc()
log.error(
"An error happened during the rebuild. The server will appear stuck until build errors are resolved."
)
continue

with self._epoch_cond:
log.info("Reloading browsers")
Expand Down
39 changes: 38 additions & 1 deletion mkdocs/tests/livereload_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,44 @@ def rebuild():
_, output = do_request(server, "GET /foo.site")
self.assertEqual(output, "ccccc")

@tempdir({"foo.docs": "a"})
@tempdir({"foo.site": "original"})
def test_recovers_from_build_error(self, site_dir, docs_dir):
started_building = threading.Event()
build_count = 0

def rebuild():
started_building.set()
nonlocal build_count
build_count += 1
if build_count == 1:
raise ValueError("oh no")
else:
content = Path(docs_dir, "foo.docs").read_text()
Path(site_dir, "foo.site").write_text(content * 5)

with testing_server(site_dir, rebuild) as server:
server.watch(docs_dir)
time.sleep(0.01)

err = io.StringIO()
with contextlib.redirect_stderr(err), self.assertLogs("mkdocs.livereload") as cm:
Path(docs_dir, "foo.docs").write_text("b")
started_building.wait(timeout=10)

Path(docs_dir, "foo.docs").write_text("c")

_, output = do_request(server, "GET /foo.site")

self.assertIn("ValueError: oh no", err.getvalue())
self.assertRegex(
"\n".join(cm.output),
r".*Detected file changes\n"
r".*An error happened during the rebuild.*\n"
r".*Detected file changes\n",
)
self.assertEqual(output, "ccccc")

@tempdir(
{
"normal.html": "<html><body>hello</body></html>",
Expand Down Expand Up @@ -426,7 +464,6 @@ def test_error_handler(self, site_dir):

@tempdir()
def test_bad_error_handler(self, site_dir):
self.maxDiff = None
with testing_server(site_dir) as server:
server.error_handler = lambda code: 0 / 0
with self.assertLogs("mkdocs.livereload") as cm:
Expand Down

0 comments on commit b6ff158

Please sign in to comment.