Less scary message on config.yaml parsing error

This commit is contained in:
Gina Häußge 2016-07-08 12:35:46 +02:00
parent 55ed4211f9
commit 0fd510a51f
5 changed files with 81 additions and 23 deletions

View file

@ -52,6 +52,11 @@ if version_info.major == 2 and version_info.minor <= 7 and version_info.micro <
del version_info
#~~ custom exceptions
class FatalStartupError(BaseException):
pass
#~~ init methods to bring up platform
def init_platform(basedir, configfile, use_logging_file=True, logging_file=None,
@ -79,8 +84,14 @@ def init_platform(basedir, configfile, use_logging_file=True, logging_file=None,
def init_settings(basedir, configfile):
"""Inits the settings instance based on basedir and configfile to use."""
from octoprint.settings import settings
return settings(init=True, basedir=basedir, configfile=configfile)
from octoprint.settings import settings, InvalidSettings
try:
return settings(init=True, basedir=basedir, configfile=configfile)
except InvalidSettings as e:
message = "Error parsing the configuration file, it appears to be invalid YAML."
if e.line is not None and e.column is not None:
message += " The parser reported an error on line {}, column {}.".format(e.line, e.column)
raise FatalStartupError(message)
def init_logging(settings, use_logging_file=True, logging_file=None, default_config=None, debug=False, verbosity=0, uncaught_logger=None, uncaught_handler=None):

View file

@ -10,7 +10,7 @@ import json
import octoprint_client
from octoprint.cli import pass_octoprint_ctx, bulk_options, standard_options
from octoprint import init_settings
from octoprint import init_settings, FatalStartupError
class JsonStringParamType(click.ParamType):
@ -36,10 +36,16 @@ def client_commands():
@click.option("--https", is_flag=True)
@click.option("--prefix", type=click.STRING)
@pass_octoprint_ctx
def client(obj, host, port, httpuser, httppass, https, prefix):
@click.pass_context
def client(ctx, obj, host, port, httpuser, httppass, https, prefix):
"""Basic API client."""
obj.settings = init_settings(obj.basedir, obj.configfile)
octoprint_client.init_client(obj.settings, https=https, httpuser=httpuser, httppass=httppass, host=host, port=port, prefix=prefix)
try:
obj.settings = init_settings(obj.basedir, obj.configfile)
octoprint_client.init_client(obj.settings, https=https, httpuser=httpuser, httppass=httppass, host=host, port=port, prefix=prefix)
except FatalStartupError as e:
click.echo(e.message, err=True)
click.echo("There was a fatal error initializing the client.", err=True)
ctx.exit(-1)
def log_response(response, status_code=True, body=True, headers=False):

View file

@ -48,9 +48,14 @@ class OctoPrintPluginCommands(click.MultiCommand):
# initialize settings and plugin manager based on provided
# context (basedir and configfile)
from octoprint import init_settings, init_pluginsystem
self.settings = init_settings(ctx.obj.basedir, ctx.obj.configfile)
self.plugin_manager = init_pluginsystem(self.settings)
from octoprint import init_settings, init_pluginsystem, FatalStartupError
try:
self.settings = init_settings(ctx.obj.basedir, ctx.obj.configfile)
self.plugin_manager = init_pluginsystem(self.settings)
except FatalStartupError as e:
click.echo(e.message, err=True)
click.echo("There was a fatal error initializing the settings or the plugin system.", err=True)
ctx.exit(-1)
# fetch registered hooks
self.hooks = self.plugin_manager.get_hooks("octoprint.cli.commands")

View file

@ -14,7 +14,7 @@ from octoprint.cli import pass_octoprint_ctx, bulk_options, standard_options
def run_server(basedir, configfile, host, port, debug, allow_root, logging_config, verbosity, octoprint_daemon = None):
"""Initializes the environment and starts up the server."""
from octoprint import init_platform, __display_version__
from octoprint import init_platform, __display_version__, FatalStartupError
def log_startup(_):
logging.getLogger("octoprint.server").info("Starting OctoPrint {}".format(__display_version__))
@ -29,17 +29,27 @@ def run_server(basedir, configfile, host, port, debug, allow_root, logging_confi
"install PyOpenSSL plus its dependencies. For details see "
"https://urllib3.readthedocs.org/en/latest/security.html#openssl-pyopenssl")
settings, _, plugin_manager = init_platform(basedir,
configfile,
logging_file=logging_config,
debug=debug,
verbosity=verbosity,
uncaught_logger=__name__,
after_logging=log_startup)
from octoprint.server import Server
octoprint_server = Server(settings=settings, plugin_manager=plugin_manager, host=host, port=port, debug=debug, allow_root=allow_root, octoprint_daemon=octoprint_daemon)
octoprint_server.run()
try:
settings, _, plugin_manager = init_platform(basedir,
configfile,
logging_file=logging_config,
debug=debug,
verbosity=verbosity,
uncaught_logger=__name__,
after_logging=log_startup)
except FatalStartupError as e:
click.echo(e.message, err=True)
click.echo("There was a fatal error starting up OctoPrint.", err=True)
else:
from octoprint.server import Server
octoprint_server = Server(settings=settings,
plugin_manager=plugin_manager,
host=host,
port=port,
debug=debug,
allow_root=allow_root,
octoprint_daemon=octoprint_daemon)
octoprint_server.run()
#~~ server options

View file

@ -26,6 +26,7 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms
import sys
import os
import yaml
import yaml.parser
import logging
import re
import uuid
@ -345,6 +346,14 @@ class NoSuchSettingsPath(BaseException):
pass
class InvalidSettings(BaseException):
def __init__(self, message, line=None, column=None, details=None):
self.message = message
self.line = line
self.column = column
self.details = details
class HierarchicalChainMap(ChainMap):
def deep_dict(self, root=None):
@ -747,8 +756,25 @@ class Settings(object):
def load(self, migrate=False):
if os.path.exists(self._configfile) and os.path.isfile(self._configfile):
with open(self._configfile, "r") as f:
self._config = yaml.safe_load(f)
self._mtime = self.last_modified
try:
self._config = yaml.safe_load(f)
self._mtime = self.last_modified
except yaml.YAMLError as e:
details = e.message
if hasattr(e, "problem_mark"):
line = e.problem_mark.line
column = e.problem_mark.column
else:
line = None
column = None
raise InvalidSettings("Invalid YAML file: {}".format(self._configfile),
details=details,
line=line,
column=column)
except:
raise
# changed from else to handle cases where the file exists, but is empty / 0 bytes
if not self._config:
self._config = dict()