示例#1
0
    def test_late_init(self, init_patch, monkeypatch, fake_save_manager, args,
                       mocker, errors):
        configinit.early_init(args)

        if errors:
            err = configexc.ConfigErrorDesc("Error text",
                                            Exception("Exception"))
            errs = configexc.ConfigFileErrors("config.py", [err])
            if errors == 'fatal':
                errs.fatal = True

            monkeypatch.setattr(configinit, '_init_errors', errs)

        msgbox_mock = mocker.patch(
            'qutebrowser.config.configinit.msgbox.msgbox', autospec=True)
        exit_mock = mocker.patch(
            'qutebrowser.config.configinit.sys.exit', autospec=True)

        configinit.late_init(fake_save_manager)

        fake_save_manager.add_saveable.assert_any_call(
            'state-config', unittest.mock.ANY)
        fake_save_manager.add_saveable.assert_any_call(
            'yaml-config', unittest.mock.ANY, unittest.mock.ANY)

        if errors:
            assert len(msgbox_mock.call_args_list) == 1
            _call_posargs, call_kwargs = msgbox_mock.call_args_list[0]
            text = call_kwargs['text'].strip()
            assert text.startswith('Errors occurred while reading config.py:')
            assert '<b>Error text</b>: Exception' in text

            assert exit_mock.called == (errors == 'fatal')
        else:
            assert not msgbox_mock.called
示例#2
0
 def _handle_error(self, action: str, name: str) -> typing.Iterator[None]:
     """Catch config-related exceptions and save them in self.errors."""
     try:
         yield
     except configexc.ConfigFileErrors as e:
         for err in e.errors:
             new_err = err.with_text(e.basename)
             self.errors.append(new_err)
     except configexc.Error as e:
         text = "While {} '{}'".format(action, name)
         self.errors.append(configexc.ConfigErrorDesc(text, e))
     except urlmatch.ParseError as e:
         text = "While {} '{}' and parsing pattern".format(action, name)
         self.errors.append(configexc.ConfigErrorDesc(text, e))
     except keyutils.KeyParseError as e:
         text = "While {} '{}' and parsing key".format(action, name)
         self.errors.append(configexc.ConfigErrorDesc(text, e))
示例#3
0
 def _handle_error(self, action: str, name: str) -> Iterator[None]:
     try:
         yield
     except configexc.Error as e:
         if self._configapi is None:
             raise
         text = "While {} '{}'".format(action, name)
         self._configapi.errors.append(configexc.ConfigErrorDesc(text, e))
示例#4
0
def read_config_py(filename=None):
    """Read a config.py file."""
    api = ConfigAPI(config.instance, config.key_instance)

    if filename is None:
        filename = os.path.join(standarddir.config(), 'config.py')
        if not os.path.exists(filename):
            return api

    container = config.ConfigContainer(config.instance, configapi=api)
    basename = os.path.basename(filename)

    module = types.ModuleType('config')
    module.config = api
    module.c = container
    module.__file__ = filename

    try:
        with open(filename, mode='rb') as f:
            source = f.read()
    except OSError as e:
        text = "Error while reading {}".format(basename)
        desc = configexc.ConfigErrorDesc(text, e)
        raise configexc.ConfigFileErrors(basename, [desc])

    try:
        code = compile(source, filename, 'exec')
    except (ValueError, TypeError) as e:
        # source contains NUL bytes
        desc = configexc.ConfigErrorDesc("Error while compiling", e)
        raise configexc.ConfigFileErrors(basename, [desc])
    except SyntaxError as e:
        desc = configexc.ConfigErrorDesc("Syntax Error", e,
                                         traceback=traceback.format_exc())
        raise configexc.ConfigFileErrors(basename, [desc])

    try:
        exec(code, module.__dict__)
    except Exception as e:
        api.errors.append(configexc.ConfigErrorDesc(
            "Unhandled exception",
            exception=e, traceback=traceback.format_exc()))

    api.finalize()
    return api
示例#5
0
def read_autoconfig():
    """Read the autoconfig.yml file."""
    try:
        config.instance.read_yaml()
    except configexc.ConfigFileErrors:  # pylint: disable=try-except-raise
        raise  # caught in outer block
    except configexc.Error as e:
        desc = configexc.ConfigErrorDesc("Error", e)
        raise configexc.ConfigFileErrors('autoconfig.yml', [desc])
示例#6
0
def read_autoconfig() -> None:
    """Read the autoconfig.yml file."""
    try:
        config.instance.read_yaml()
    except configexc.ConfigFileErrors:
        raise  # caught in outer block
    except configexc.Error as e:
        desc = configexc.ConfigErrorDesc("Error", e)
        raise configexc.ConfigFileErrors('autoconfig.yml', [desc])
示例#7
0
 def finalize(self) -> None:
     """Do work which needs to be done after reading config.py."""
     if self._config.warn_autoconfig:
         desc = configexc.ConfigErrorDesc(
             "autoconfig loading not specified",
             ("Your config.py should call either `config.load_autoconfig()`"
              " (to load settings configured via the GUI) or "
              "`config.load_autoconfig(False)` (to not do so)"))
         self.errors.append(desc)
     self._config.update_mutables()
示例#8
0
 def _validate(self):
     """Make sure all settings exist."""
     unknown = set(self._values) - set(configdata.DATA)
     if unknown:
         errors = [
             configexc.ConfigErrorDesc("While loading options",
                                       "Unknown option {}".format(e))
             for e in sorted(unknown)
         ]
         raise configexc.ConfigFileErrors('autoconfig.yml', errors)
示例#9
0
 def _handle_error(self, action, name):
     try:
         yield
     except configexc.ConfigFileErrors as e:
         for err in e.errors:
             new_err = err.with_text(e.basename)
             self.errors.append(new_err)
     except configexc.Error as e:
         text = "While {} '{}'".format(action, name)
         self.errors.append(configexc.ConfigErrorDesc(text, e))
示例#10
0
    def _validate(self, settings: _SettingsType) -> None:
        """Make sure all settings exist."""
        unknown = []
        for name in settings:
            if name not in configdata.DATA:
                unknown.append(name)

        if unknown:
            errors = [configexc.ConfigErrorDesc("While loading options",
                                                "Unknown option {}".format(e))
                      for e in sorted(unknown)]
            raise configexc.ConfigFileErrors('autoconfig.yml', errors)
示例#11
0
    def load(self):
        """Load configuration from the configured YAML file."""
        try:
            with open(self._filename, 'r', encoding='utf-8') as f:
                yaml_data = utils.yaml_load(f)
        except FileNotFoundError:
            return {}
        except OSError as e:
            desc = configexc.ConfigErrorDesc("While reading", e)
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])
        except yaml.YAMLError as e:
            desc = configexc.ConfigErrorDesc("While parsing", e)
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])

        try:
            global_obj = yaml_data['global']
        except KeyError:
            desc = configexc.ConfigErrorDesc(
                "While loading data",
                "Toplevel object does not contain 'global' key")
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])
        except TypeError:
            desc = configexc.ConfigErrorDesc("While loading data",
                                             "Toplevel object is not a dict")
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])

        if not isinstance(global_obj, dict):
            desc = configexc.ConfigErrorDesc(
                "While loading data",
                "'global' object is not a dict")
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])

        # Delete unknown values
        # (e.g. options which were removed from configdata.yml)
        for name in list(global_obj):
            if name not in configdata.DATA:
                del global_obj[name]

        self._values = global_obj
        self._dirty = False
示例#12
0
    def _pop_object(self, yaml_data: Any, key: str, typ: type) -> Any:
        """Get a global object from the given data."""
        if not isinstance(yaml_data, dict):
            desc = configexc.ConfigErrorDesc("While loading data",
                                             "Toplevel object is not a dict")
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])

        if key not in yaml_data:
            desc = configexc.ConfigErrorDesc(
                "While loading data",
                "Toplevel object does not contain '{}' key".format(key))
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])

        data = yaml_data.pop(key)

        if not isinstance(data, typ):
            desc = configexc.ConfigErrorDesc(
                "While loading data",
                "'{}' object is not a {}".format(key, typ.__name__))
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])

        return data
示例#13
0
    def _build_values(self, settings: typing.Mapping) -> None:
        """Build up self._values from the values in the given dict."""
        errors = []
        for name, yaml_values in settings.items():
            if not isinstance(yaml_values, dict):
                errors.append(
                    configexc.ConfigErrorDesc(
                        "While parsing {!r}".format(name),
                        "value is not a dict"))
                continue

            values = configutils.Values(configdata.DATA[name])
            if 'global' in yaml_values:
                values.add(yaml_values.pop('global'))

            for pattern, value in yaml_values.items():
                if not isinstance(pattern, str):
                    errors.append(
                        configexc.ConfigErrorDesc(
                            "While parsing {!r}".format(name),
                            "pattern is not of type string"))
                    continue
                try:
                    urlpattern = urlmatch.UrlPattern(pattern)
                except urlmatch.ParseError as e:
                    errors.append(
                        configexc.ConfigErrorDesc(
                            "While parsing pattern {!r} for {!r}".format(
                                pattern, name), e))
                    continue
                values.add(value, urlpattern)

            self._values[name] = values

        if errors:
            raise configexc.ConfigFileErrors('autoconfig.yml', errors)
示例#14
0
    def load(self):
        """Load configuration from the configured YAML file."""
        try:
            with open(self._filename, 'r', encoding='utf-8') as f:
                yaml_data = utils.yaml_load(f)
        except FileNotFoundError:
            return
        except OSError as e:
            desc = configexc.ConfigErrorDesc("While reading", e)
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])
        except yaml.YAMLError as e:
            desc = configexc.ConfigErrorDesc("While parsing", e)
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])

        try:
            global_obj = yaml_data['global']
        except KeyError:
            desc = configexc.ConfigErrorDesc(
                "While loading data",
                "Toplevel object does not contain 'global' key")
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])
        except TypeError:
            desc = configexc.ConfigErrorDesc("While loading data",
                                             "Toplevel object is not a dict")
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])

        if not isinstance(global_obj, dict):
            desc = configexc.ConfigErrorDesc("While loading data",
                                             "'global' object is not a dict")
            raise configexc.ConfigFileErrors('autoconfig.yml', [desc])

        self._values = global_obj
        self._dirty = False

        self._handle_migrations()
        self._validate()
示例#15
0
 def bind(self, key: str,
          command: typing.Optional[str],
          mode: str = 'normal') -> None:
     """Bind a key to a command, with an optional key mode."""
     with self._handle_error('binding', key):
         seq = keyutils.KeySequence.parse(key)
         if command is None:
             text = ("Unbinding commands with config.bind('{key}', None) "
                     "is deprecated. Use config.unbind('{key}') instead."
                     .format(key=key))
             self.errors.append(configexc.ConfigErrorDesc(
                 "While unbinding '{}'".format(key), text))
             self._keyconfig.unbind(seq, mode=mode)
         else:
             self._keyconfig.bind(seq, command, mode=mode)
示例#16
0
def early_init(args):
    """Initialize the part of the config which works without a QApplication."""
    configdata.init()

    yaml_config = configfiles.YamlConfig()

    global val, instance, key_instance
    instance = Config(yaml_config=yaml_config)
    val = ConfigContainer(instance)
    key_instance = KeyConfig(instance)

    for cf in _change_filters:
        cf.validate()

    configtypes.Font.monospace_fonts = val.fonts.monospace

    config_commands = ConfigCommands(instance, key_instance)
    objreg.register('config-commands', config_commands)

    config_api = None

    try:
        config_api = configfiles.read_config_py()
        # Raised here so we get the config_api back.
        if config_api.errors:
            raise configexc.ConfigFileErrors('config.py', config_api.errors)
    except configexc.ConfigFileErrors as e:
        log.config.exception("Error while loading config.py")
        _init_errors.append(e)

    try:
        if getattr(config_api, 'load_autoconfig', True):
            try:
                instance.read_yaml()
            except configexc.ConfigFileErrors as e:
                raise  # caught in outer block
            except configexc.Error as e:
                desc = configexc.ConfigErrorDesc("Error", e)
                raise configexc.ConfigFileErrors('autoconfig.yml', [desc])
    except configexc.ConfigFileErrors as e:
        log.config.exception("Error while loading config.py")
        _init_errors.append(e)

    configfiles.init()

    objects.backend = get_backend(args)
    earlyinit.init_with_backend(objects.backend)
示例#17
0
 def _handle_migrations(self):
     """Handle unknown/renamed keys."""
     for name in list(self._values):
         if name in configdata.MIGRATIONS.renamed:
             new_name = configdata.MIGRATIONS.renamed[name]
             log.config.debug("Renaming {} to {}".format(name, new_name))
             self._values[new_name] = self._values[name]
             del self._values[name]
         elif name in configdata.MIGRATIONS.deleted:
             log.config.debug("Removing {}".format(name))
             del self._values[name]
         elif name in configdata.DATA:
             pass
         else:
             desc = configexc.ConfigErrorDesc(
                 "While loading options", "Unknown option {}".format(name))
             raise configexc.ConfigFileErrors('autoconfig.yml', [desc])
示例#18
0
def init() -> None:
    """Initialize config storage not related to the main config."""
    global state

    try:
        state = StateConfig()
    except configparser.Error as e:
        msg = "While loading state file from {}".format(standarddir.data())
        desc = configexc.ConfigErrorDesc(msg, e)
        raise configexc.ConfigFileErrors('state', [desc], fatal=True)

    # Set the QSettings path to something like
    # ~/.config/qutebrowser/qsettings/qutebrowser/qutebrowser.conf so it
    # doesn't overwrite our config.
    #
    # This fixes one of the corruption issues here:
    # https://github.com/qutebrowser/qutebrowser/issues/515

    path = os.path.join(standarddir.config(auto=True), 'qsettings')
    for fmt in [QSettings.NativeFormat, QSettings.IniFormat]:
        QSettings.setPath(fmt, QSettings.UserScope, path)
示例#19
0
def errors():
    """Get a ConfigFileErrors object."""
    err1 = configexc.ConfigErrorDesc("Error text 1", Exception("Exception 1"))
    err2 = configexc.ConfigErrorDesc("Error text 2", Exception("Exception 2"),
                                     "Fake traceback")
    return configexc.ConfigFileErrors("config.py", [err1, err2])
示例#20
0
def read_config_py(filename: str, raising: bool = False) -> None:
    """Read a config.py file.

    Arguments;
        filename: The name of the file to read.
        raising: Raise exceptions happening in config.py.
                 This is needed during tests to use pytest's inspection.
    """
    assert config.instance is not None
    assert config.key_instance is not None

    api = ConfigAPI(config.instance, config.key_instance)
    container = config.ConfigContainer(config.instance, configapi=api)
    basename = os.path.basename(filename)

    module = types.ModuleType('config')
    module.config = api  # type: ignore
    module.c = container  # type: ignore
    module.__file__ = filename

    try:
        with open(filename, mode='rb') as f:
            source = f.read()
    except OSError as e:
        text = "Error while reading {}".format(basename)
        desc = configexc.ConfigErrorDesc(text, e)
        raise configexc.ConfigFileErrors(basename, [desc])

    try:
        code = compile(source, filename, 'exec')
    except ValueError as e:
        # source contains NUL bytes
        desc = configexc.ConfigErrorDesc("Error while compiling", e)
        raise configexc.ConfigFileErrors(basename, [desc])
    except SyntaxError as e:
        desc = configexc.ConfigErrorDesc("Unhandled exception",
                                         e,
                                         traceback=traceback.format_exc())
        raise configexc.ConfigFileErrors(basename, [desc])

    try:
        # Save and restore sys variables
        with saved_sys_properties():
            # Add config directory to python path, so config.py can import
            # other files in logical places
            config_dir = os.path.dirname(filename)
            if config_dir not in sys.path:
                sys.path.insert(0, config_dir)

            exec(code, module.__dict__)
    except Exception as e:
        if raising:
            raise
        api.errors.append(
            configexc.ConfigErrorDesc("Unhandled exception",
                                      exception=e,
                                      traceback=traceback.format_exc()))

    api.finalize()
    config.instance.config_py_loaded = True

    if api.errors:
        raise configexc.ConfigFileErrors('config.py', api.errors)
示例#21
0
def test_desc_with_text():
    """Test ConfigErrorDesc.with_text."""
    old = configexc.ConfigErrorDesc("Error text", Exception("Exception text"))
    new = old.with_text("additional text")
    assert str(new) == 'Error text (additional text): Exception text'
示例#22
0
def test_config_file_errors_fatal():
    err = configexc.ConfigErrorDesc("Text", Exception("Text"))
    errors = configexc.ConfigFileErrors("state", [err], fatal=True)
    assert errors.fatal
示例#23
0
 def _handle_error(self, action, name):
     try:
         yield
     except configexc.Error as e:
         text = "While {} '{}'".format(action, name)
         self.errors.append(configexc.ConfigErrorDesc(text, e))