Skip to content

Commit

Permalink
馃悰 Include stack trace with fatal errors
Browse files Browse the repository at this point in the history
Also make InvalidYaml exceptions more helpful by including
the full message from the triggering YAMLError.

Closes #4973
  • Loading branch information
foosel committed Mar 28, 2024
1 parent d5b19b8 commit 5bde1df
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 62 deletions.
4 changes: 2 additions & 2 deletions src/octoprint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,8 +800,8 @@ def get_plugin_blacklist(settings, connectivity_checker=None):
return []

def format_blacklist(entries):
format_entry = (
lambda x: f"{x[0]} ({x[1]})"
format_entry = lambda x: (
f"{x[0]} ({x[1]})"
if isinstance(x, (list, tuple)) and len(x) == 2
else f"{x} (any)"
)
Expand Down
11 changes: 8 additions & 3 deletions src/octoprint/cli/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,14 @@ def cli(ctx, apikey, host, port, httpuser, httppass, https, prefix):
prefix=prefix,
)

except FatalStartupError as e:
click.echo(str(e), err=True)
click.echo("There was a fatal error initializing the client.", err=True)
except FatalStartupError as exc:
from traceback import format_exc

click.echo(format_exc(), err=True)
click.echo(
f"There was a fatal error initializing the client: {str(exc)}",
err=True,
)
ctx.exit(-1)


Expand Down
28 changes: 19 additions & 9 deletions src/octoprint/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,14 @@ def _init_pluginsettings(ctx):
ctx.obj.plugin_manager = init_pluginsystem(
ctx.obj.settings, safe_mode=get_ctx_obj_option(ctx, "safe_mode", False)
)
except FatalStartupError as e:
click.echo(str(e), err=True)
click.echo("There was a fatal error initializing the plugin manager.", err=True)
except FatalStartupError as exc:
from traceback import format_exc

click.echo(format_exc(), err=True)
click.echo(
f"There was a fatal error initializing the plugin manager: {str(exc)}",
err=True,
)
ctx.exit(-1)


Expand All @@ -60,19 +65,24 @@ def _init_pluginsettings(ctx):
def cli(ctx):
"""Basic config manipulation."""
logging.basicConfig(
level=logging.DEBUG
if get_ctx_obj_option(ctx, "verbosity", 0) > 0
else logging.WARN
level=(
logging.DEBUG if get_ctx_obj_option(ctx, "verbosity", 0) > 0 else logging.WARN
)
)
try:
ctx.obj.settings = init_settings(
get_ctx_obj_option(ctx, "basedir", None),
get_ctx_obj_option(ctx, "configfile", None),
overlays=get_ctx_obj_option(ctx, "overlays", None),
)
except FatalStartupError as e:
click.echo(str(e), err=True)
click.echo("There was a fatal error initializing the settings manager.", err=True)
except FatalStartupError as exc:
from traceback import format_exc

click.echo(format_exc(), err=True)
click.echo(
f"There was a fatal error initializing the settings manager: {str(exc)}",
err=True,
)
ctx.exit(-1)


Expand Down
8 changes: 5 additions & 3 deletions src/octoprint/cli/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,12 @@ def _initialize(self, ctx):
self.plugin_manager = init_pluginsystem(
self.settings, safe_mode=get_ctx_obj_option(ctx, "safe_mode", False)
)
except FatalStartupError as e:
click.echo(str(e), err=True)
except FatalStartupError as exc:
from traceback import format_exc

click.echo(format_exc(), err=True)
click.echo(
"There was a fatal error initializing the settings or the plugin system.",
f"There was a fatal error initializing the settings or the plugin system: {str(exc)}",
err=True,
)
ctx.exit(-1)
Expand Down
33 changes: 21 additions & 12 deletions src/octoprint/cli/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,17 @@ def _log(message, level=logging.INFO):
environment_detector,
) = components

except FatalStartupError as e:
logger = logging.getLogger("octoprint.startup").fatal
echo = lambda x: click.echo(x, err=True)
except FatalStartupError as exc:
from traceback import format_exc

for method in logger, echo:
method(str(e))
method("There was a fatal error starting up OctoPrint.")
logging.getLogger("octoprint.startup").exception(
"There was a fatal error initializing OctoPrint:"
)
click.echo(format_exc(), err=True)
click.echo(
f"There was a fatal error initializing OctoPrint: {str(exc)}",
err=True,
)

else:
from octoprint.server import CannotStartServerException, Server
Expand Down Expand Up @@ -262,20 +266,25 @@ def enable_safemode(ctx, **kwargs):
from octoprint import FatalStartupError, init_settings

logging.basicConfig(
level=logging.DEBUG
if get_ctx_obj_option(ctx, "verbosity", 0) > 0
else logging.WARN
level=(
logging.DEBUG if get_ctx_obj_option(ctx, "verbosity", 0) > 0 else logging.WARN
)
)
try:
settings = init_settings(
get_ctx_obj_option(ctx, "basedir", None),
get_ctx_obj_option(ctx, "configfile", None),
overlays=get_ctx_obj_option(ctx, "overlays", None),
)
except FatalStartupError as e:
click.echo(str(e), err=True)
click.echo("There was a fatal error initializing the settings manager.", err=True)
except FatalStartupError as exc:
from traceback import format_exc

ctx.exit(-1)
click.echo(format_exc(), err=True)
click.echo(
f"There was a fatal error initializing the settings manager: {str(exc)}",
err=True,
)
else:
settings.setBoolean(["server", "startOnceInSafeMode"], True)
settings.save()
Expand Down
17 changes: 11 additions & 6 deletions src/octoprint/cli/timelapse.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
def cli(ctx):
"""Basic config manipulation."""
logging.basicConfig(
level=logging.DEBUG
if get_ctx_obj_option(ctx, "verbosity", 0) > 0
else logging.WARN
level=(
logging.DEBUG if get_ctx_obj_option(ctx, "verbosity", 0) > 0 else logging.WARN
)
)
try:
ctx.obj.settings = init_settings(
Expand All @@ -34,9 +34,14 @@ def cli(ctx):
ctx.obj.plugin_manager = init_pluginsystem(
ctx.obj.settings, safe_mode=get_ctx_obj_option(ctx, "safe_mode", False)
)
except FatalStartupError as e:
click.echo(str(e), err=True)
click.echo("There was a fatal error initializing the settings manager.", err=True)
except FatalStartupError as exc:
from traceback import format_exc

click.echo(format_exc(), err=True)
click.echo(
f"There was a fatal error initializing the settings manager: {str(exc)}",
err=True,
)
ctx.exit(-1)


Expand Down
33 changes: 6 additions & 27 deletions src/octoprint/settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,21 +107,14 @@ class InvalidSettings(Exception):


class InvalidYaml(InvalidSettings):
def __init__(self, file, line=None, column=None, details=None):
def __init__(self, file, error=None):
self.file = file
self.line = line
self.column = column
self.details = details
self.error = error

def __str__(self):
message = (
"Error parsing the configuration file {}, "
"it is invalid YAML.".format(self.file)
)
if self.line and self.column:
message += " The parser reported an error on line {}, column {}.".format(
self.line, self.column
)
message = f"Error parsing the configuration file {self.file}, it is invalid YAML"
if self.error:
message += f": {self.error}"
return message


Expand Down Expand Up @@ -920,21 +913,7 @@ def load(self, migrate=False):
mtime = self.last_modified

except YAMLError as e:
details = str(e)

if hasattr(e, "problem_mark"):
line = e.problem_mark.line
column = e.problem_mark.column
else:
line = None
column = None

raise InvalidYaml(
self._configfile,
details=details,
line=line,
column=column,
)
raise InvalidYaml(self._configfile, error=str(e))

# changed from else to handle cases where the file exists, but is empty / 0 bytes
if not config or not isinstance(config, dict):
Expand Down

0 comments on commit 5bde1df

Please sign in to comment.