Beispiel #1
0
def test_config_file_error() -> None:
    error = ConfigFileError("A")
    assert str(error) == "A"

    error = ConfigFileError("A", FileNotFoundError(1, "B"))
    assert str(error) == "A: B (errno: 1)"

    error = ConfigFileError("A", ValueError("B"))
    assert str(error) == "A: B"
Beispiel #2
0
def save_config_file(config: Config) -> None:  # pragma: no cover
    data = config.dump()
    try:
        with open(CONFIG_FILE, "w") as fp:
            json_dump(data, fp, sort_keys=True, indent=2)

    except ValueError as err:
        raise ConfigFileError("Bad JSON", err)
    except Exception as err:
        raise ConfigFileError(f"Cannot save {CONFIG_FILE}", err)
Beispiel #3
0
def load_config_file() -> Config:  # pragma: no cover
    config = Config()
    try:
        with open(CONFIG_FILE, "r") as fp:
            data = json_load(fp)

    except FileNotFoundError:
        return config
    except ValueError as err:
        raise ConfigFileError("Bad JSON", err)
    except Exception as err:
        raise ConfigFileError(f"Cannot load {CONFIG_FILE}", err)
    else:
        config.update(data)
        return config
Beispiel #4
0
def edit_config_file(editor: str = None):  # pragma: no cover
    editor = editor or os.environ.get("VISUAL") or os.environ.get("EDITOR")
    if editor:
        editor = shlex.split(editor)[0]  # Prevent arbitrary code execution
    elif sys.platform.startswith("win"):
        editor = "notepad"
    else:
        editor = "vi"
    try:
        shutil.copy(CONFIG_FILE, TMP_CONFIG_FILE)
        subprocess.check_call([editor, str(TMP_CONFIG_FILE)])

        with open(TMP_CONFIG_FILE, "r") as fp:
            raw_config = json_load(fp)

    except Exception as err:
        raise ConfigFileError(f"Cannot update {CONFIG_FILE}", err)
    else:
        config = Config()
        config.update(raw_config)
        save_config_file(config)
        return config
    finally:
        try:
            TMP_CONFIG_FILE.unlink()
        except FileNotFoundError:
            pass
Beispiel #5
0
def init_config_file() -> Config:  # pragma: no cover
    try:
        CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    except Exception as err:
        raise ConfigFileError(f"Cannot create {CONFIG_DIR}", err)
    else:
        config = Config()
        save_config_file(config)
        return config
Beispiel #6
0
    def update(self, data: Dict[str, Any]) -> None:
        if type(data) != dict:
            raise ConfigFileError("Bad JSON: expecting an object")

        def validate_string(name):
            if type(getattr(self, name)) != str:
                raise ConfigValueError(name, "a string")

        def validate_boolean(name):
            if type(getattr(self, name)) != bool:
                raise ConfigValueError(name, "true or false")

        def validate_number(name):
            value = getattr(self, name)
            if not (type(value) == int and 1 <= value <= 100):
                raise ConfigValueError(name, "an integer between 1 and 100")

        def validate_view_keys(name):
            keys = getattr(self, name)
            if not (type(keys) in (list, frozenset) and len(keys) > 0
                    and set(keys).issubset(VIEW_KEYS)):
                raise ConfigValueError(
                    name, f"non-empty array of strings in {list(VIEW_KEYS)}")

        def validate_json_keys(name):
            keys = getattr(self, name)
            if not (type(keys) in (list, frozenset) and len(keys) > 0
                    and set(keys).issubset(JSON_KEYS)):
                raise ConfigValueError(
                    name, f"non-empty array of strings in {list(JSON_KEYS)}")

        for key, val in data.items():
            if hasattr(self, key):
                setattr(self, key, val)
            else:
                raise ConfigKeyError(key)

        validate_string("approx_name_suffix")
        validate_boolean("always_output_json")
        validate_boolean("display_degree_symbol")
        validate_boolean("display_percent_symbol")
        validate_boolean("uppercase_hex_codes")
        validate_number("default_shades_count")
        validate_number("get_view_color_height")
        validate_number("get_view_color_width")
        validate_number("list_view_color_width")
        validate_view_keys("get_view_keys")
        validate_view_keys("list_view_keys")
        validate_json_keys("json_keys")

        self.get_view_keys = frozenset(self.get_view_keys)
        self.list_view_keys = frozenset(self.list_view_keys)
        self.json_keys = frozenset(self.json_keys)