Exemple #1
0
    def read_configuration(self) -> Configuration:
        """Search for a configuration file and validate it against a Marshmallow schema."""
        config_file: Path | None = None
        for possible_file in CONFIG_FILES:
            path: Path = self.root / possible_file
            if not path.exists():
                continue

            if not config_file:
                logger.info(f"Config file: reading from {path}")
                config_file = path
            else:
                logger.warning(f"Config file: ignoring existing {path}")

        if not config_file:
            logger.warning("Config file: none found")
            return Configuration(None, [], "")

        toml_doc = TomlDoc(path=config_file)
        config_dict = search_json(toml_doc.as_object, TOOL_NITPICK_JMEX, {})
        validation_errors = ToolNitpickSectionSchema().validate(config_dict)
        if not validation_errors:
            return Configuration(config_file, config_dict.get("style", []),
                                 config_dict.get("cache", ""))

        # pylint: disable=import-outside-toplevel
        from nitpick.plugins.info import FileInfo

        raise QuitComplainingError(
            Reporter(FileInfo(self, PYPROJECT_TOML)).make_fuss(
                StyleViolations.INVALID_DATA_TOOL_NITPICK,
                flatten_marshmallow_errors(validation_errors),
                section=TOOL_NITPICK_KEY,
            ))
Exemple #2
0
def test_flatten_marshmallow_errors():
    """Flatten Marshmallow errors."""
    examples = [
        ({
            "list": ["multi", "part", "message"]
        }, "list: multi, part, message"),
        ({
            "dict": {
                "a": "blargh",
                "b": "blergh"
            }
        }, "dict.a: blargh\ndict.b: blergh"),
        ({
            "dict": {
                "a": ["x", "y"],
                "b": "blergh"
            }
        }, "dict.a: x, y\ndict.b: blergh"),
        ({
            "tuple": ("c", "meh")
        }, "tuple: c, meh"),
        ({
            "err": ValidationError("some error")
        }, "err: some error"),
    ]
    for error, expected in examples:
        compare(actual=flatten_marshmallow_errors(error), expected=expected)
Exemple #3
0
 def validate_style(self, style_file_name: str, original_data: JsonDict):
     """Validate a style file (TOML) against a Marshmallow schema."""
     self.rebuild_dynamic_schema(original_data)
     style_errors = self._dynamic_schema_class().validate(original_data)
     if style_errors:
         Nitpick.current_app().add_style_error(
             style_file_name, "Invalid config:",
             flatten_marshmallow_errors(style_errors))
Exemple #4
0
 def validate_pyproject_tool_nitpick(self) -> bool:
     """Validate the ``pyroject.toml``'s ``[tool.nitpick]`` section against a Marshmallow schema."""
     pyproject_path = Nitpick.current_app().root_dir / PyProjectTomlFile.file_name  # type: Path
     if pyproject_path.exists():
         self.pyproject_toml = TomlFormat(path=pyproject_path)
         self.tool_nitpick_dict = search_dict(TOOL_NITPICK_JMEX, self.pyproject_toml.as_data, {})
         pyproject_errors = ToolNitpickSectionSchema().validate(self.tool_nitpick_dict)
         if pyproject_errors:
             Nitpick.current_app().add_style_error(
                 PyProjectTomlFile.file_name,
                 "Invalid data in [{}]:".format(TOOL_NITPICK),
                 flatten_marshmallow_errors(pyproject_errors),
             )
             return False
     return True
Exemple #5
0
    def validate_style(self, style_file_name: str, original_data: JsonDict):
        """Validate a style file (TOML) against a Marshmallow schema."""
        self.rebuild_dynamic_schema(original_data)
        style_errors = self._dynamic_schema_class().validate(original_data)

        if style_errors:
            has_nitpick_jsonfile_section = style_errors.get(PROJECT_NAME, {}).pop("JSONFile", None)
            if has_nitpick_jsonfile_section:
                warnings.warn(
                    "The [nitpick.JSONFile] section is not needed anymore; just declare your JSON files directly",
                    DeprecationWarning,
                )
                if not style_errors[PROJECT_NAME]:
                    style_errors.pop(PROJECT_NAME)

        if style_errors:
            NitpickApp.current().add_style_error(
                style_file_name, "Invalid config:", flatten_marshmallow_errors(style_errors)
            )
Exemple #6
0
    def _include_style(self, style_url: furl) -> Iterator[Fuss]:
        if style_url.url in self._already_included:
            return
        self._already_included.add(style_url.url)

        file_contents = self._style_fetcher_manager.fetch(style_url)
        if file_contents is None:
            return

        # generate a 'human readable' version of the URL; a relative path for local files
        # and the URL otherwise.
        display_name = style_url.url
        if style_url.scheme == "file":
            path = furl_path_to_python_path(style_url.path)
            with suppress(ValueError):
                path = path.relative_to(self.project.root)
            display_name = str(path)

        read_toml_dict = self._read_toml(file_contents, display_name)

        # normalize sub-style URIs, before merging
        sub_styles = [
            self._style_fetcher_manager.normalize_url(ref, style_url)
            for ref in always_iterable(
                search_json(read_toml_dict, NITPICK_STYLES_INCLUDE_JMEX, []))
        ]
        if sub_styles:
            read_toml_dict.setdefault("nitpick", {}).setdefault(
                "styles", {})["include"] = [str(url) for url in sub_styles]

        toml_dict, validation_errors = self._config_validator.validate(
            read_toml_dict)

        if validation_errors:
            yield Reporter(FileInfo(self.project, display_name)).make_fuss(
                StyleViolations.INVALID_CONFIG,
                flatten_marshmallow_errors(validation_errors))

        dpath.util.merge(self._merged_styles,
                         flatten(toml_dict, custom_reducer(SEPARATOR_FLATTEN)))

        yield from self.include_multiple_styles(sub_styles)