Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions docs/guide/config.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,26 @@ import check_datapackage as cdp
exclude_required = cdp.Exclude(type="required")
```

To exclude checks applying to specific fields in the descriptor, you
express the location of the target field or fields in [JSON
path](https://en.wikipedia.org/wiki/JSONPath) notation and define an
`Exclude` object with this `target`. For example, you can exclude all
To exclude checks of a specific field or fields, you can use a [JSON
path](https://en.wikipedia.org/wiki/JSONPath) in the `jsonpath`
attribute of an `Exclude` object. For example, you can exclude all
checks on the `name` field of the Data Package descriptor by writing:

```{python}
exclude_name = cdp.Exclude(target="$.name")
exclude_name = cdp.Exclude(jsonpath="$.name")
```

Or you can use the wildcard JSON path selector to exclude checks on the
`path` field of **all** Data Resource descriptors:

```{python}
exclude_path = cdp.Exclude(target="$.resources[*].path")
exclude_path = cdp.Exclude(jsonpath="$.resources[*].path")
```

The `type` and `target` arguments can also be combined:
The `type` and `jsonpath` arguments can also be combined:

```{python}
exclude_desc_required = cdp.Exclude(type="required", target="$.resources[*].description")
exclude_desc_required = cdp.Exclude(type="required", jsonpath="$.resources[*].description")
```

This would exclude required checks on the `description` field of Data
Expand Down Expand Up @@ -101,7 +100,7 @@ MIT. You can express this requirement in a `Rule` as follows:
```{python}
license_rule = cdp.Rule(
type="only-mit",
target="$.licenses[*].name",
jsonpath="$.licenses[*].name",
message=dedent("""
Data Packages may only be licensed under MIT. Please review
the licenses listed in the Data Package.
Expand All @@ -115,13 +114,13 @@ Here's a breakdown of what each argument does:
- `type`: An identifier for your rule. This is what will show up in
error messages and what you will use if you want to exclude your
rule. Each `Rule` should have a unique `type`.
- `target`: The location of the field or fields, expressed in [JSON
- `jsonpath`: The location of the field or fields, expressed in [JSON
path](https://en.wikipedia.org/wiki/JSONPath) notation, to which the
rule applies. This rule applies to the `name` field of all package
licenses.
- `message`: The message that is shown when the rule is violated.
- `check`: A function that expresses how compliance with the rule is
checked. It takes the value at the `target` location as input and
checked. It takes the value at the `jsonpath` location as input and
returns true if the rule is met, false if it isn't.

To register your custom rules with the `check()` function, you add them
Expand Down
6 changes: 3 additions & 3 deletions docs/guide/issues.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ descriptor. An `Issue` has three attributes:
- `type`: The type of the check that failed and caused the issue. This
can be a type that exists in the Data Package standard (e.g.,
`required` or `pattern`) or a custom type.
- `location`: The location of the field that failed the check
expressed in [JSON path](https://en.wikipedia.org/wiki/JSONPath)
notation. For example, `$.resources[2].name`.
- `jsonpath`: The [JSON path](https://en.wikipedia.org/wiki/JSONPath)
of the field that failed the check. For example,
`$.resources[2].name`.
- `message`: An explanation of what went wrong.

## The `explain()` function
Expand Down
2 changes: 1 addition & 1 deletion src/check_datapackage/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Config:
exclude_required = cdp.Exclude(type="required")
license_rule = cdp.Rule(
type="only-mit",
target="$.licenses[*].name",
jsonpath="$.licenses[*].name",
message="Data Packages may only be licensed under MIT.",
check=lambda license_name: license_name == "mit",
)
Expand Down
14 changes: 7 additions & 7 deletions src/check_datapackage/exclude.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,32 @@
class Exclude:
"""Exclude issues when checking a Data Package descriptor.

When both `target` and `type` are provided, an issue has to match both to be
When both `jsonpath` and `type` are provided, an issue has to match both to be
excluded.

Attributes:
target (str | None): [JSON path](https://jg-rp.github.io/python-jsonpath/syntax/)
jsonpath (str | None): [JSON path](https://jg-rp.github.io/python-jsonpath/syntax/)
to the field or fields in the input object where issues should be ignored,
e.g., `$.resources[*].name`. Needs to point to the location in the
descriptor of the issue to ignore. If not provided, issues of the given
`type` will be excluded for all fields.
type (str | None): The type of the issue to ignore (e.g., "required",
"pattern", or "format"). If not provided, all types of issues will be
ignored for the given `target`.
ignored for the given `jsonpath`.

Examples:
```{python}
import check_datapackage as cdp

exclude_required = cdp.Exclude(type="required")
exclude_name = cdp.Exclude(target="$.name")
exclude_name = cdp.Exclude(jsonpath="$.name")
exclude_desc_required = cdp.Exclude(
type="required", target="$.resources[*].description"
type="required", jsonpath="$.resources[*].description"
)
```
"""

target: str | None = None
jsonpath: str | None = None
type: str | None = None


Expand All @@ -49,7 +49,7 @@ def exclude(issues: list[Issue], excludes: list[Exclude]) -> list[Issue]:
"""
# kept_issues = _filter(
# issues,
# lambda issue: _drop_any_target(issue, excludes)
# lambda issue: _drop_any_jsonpath(issue, excludes)
# )
kept_issues: list[Issue] = _drop_any_matching_types(issues, excludes)
return kept_issues
Expand Down
2 changes: 1 addition & 1 deletion src/check_datapackage/internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def _validation_errors_to_issues(
issues = [
Issue(
message=error.message,
location=_get_full_json_path_from_error(error),
jsonpath=_get_full_json_path_from_error(error),
type=str(error.validator),
)
for error in _unwrap_errors(list(validation_errors))
Expand Down
8 changes: 4 additions & 4 deletions src/check_datapackage/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class Issue:
One `Issue` object represents one failed check on one field within the descriptor.

Attributes:
location (string): A [JSON path](https://jg-rp.github.io/python-jsonpath/syntax/)
jsonpath (string): A [JSON path](https://jg-rp.github.io/python-jsonpath/syntax/)
format pointing to the field in the input object where the issue is located.
For example, `$.resources[2].name`.
type (string): The type of the check that failed. Used mostly for excluding
Expand All @@ -20,13 +20,13 @@ class Issue:
import check_datapackage as cdp

issue = cdp.Issue(
location="$.resources[2].title",
jsonpath="$.resources[2].title",
type="required",
message="The `title` field is required but missing at the given location.",
message="The `title` field is required but missing at the given JSON path.",
)
```
"""

location: str
jsonpath: str
type: str
message: str
8 changes: 4 additions & 4 deletions src/check_datapackage/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ class Rule:
"""A custom check to be done on a Data Package descriptor.

Attributes:
target (str): The location of the field or fields, expressed in [JSON
jsonpath (str): The location of the field or fields, expressed in [JSON
path](https://jg-rp.github.io/python-jsonpath/syntax/) notation, to which
the rule applies (e.g., `$.resources[*].name`).
message (str): The message that is shown when the rule is violated.
check (Callable[[Any], bool]): A function that expresses how compliance with the
rule is checked. It takes the value at the `target` location as input and
rule is checked. It takes the value at the `jsonpath` location as input and
returns true if the rule is met, false if it isn't.
type (str): An identifier for the rule. It will be shown in error messages and
can be used to exclude the rule. Each rule should have a unique `type`.
Expand All @@ -23,14 +23,14 @@ class Rule:

license_rule = cdp.Rule(
type="only-mit",
target="$.licenses[*].name",
jsonpath="$.licenses[*].name",
message="Data Packages may only be licensed under MIT.",
check=lambda license_name: license_name == "mit",
)
```
"""

target: str
jsonpath: str
message: str
check: Callable[[Any], bool]
type: str = "custom"
18 changes: 9 additions & 9 deletions tests/test_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,18 @@ def test_fails_descriptor_without_resources():

assert len(issues) == 1
assert issues[0].type == "required"
assert issues[0].location == "$.resources"
assert issues[0].jsonpath == "$.resources"


@mark.parametrize(
"resources, location, num_issues",
"resources, jsonpath, num_issues",
[
([], "$.resources", 1),
([{}], "$.resources[0].data", 3),
([{"name": "a name", "path": "/a/bad/path"}], "$.resources[0].path", 2),
],
)
def test_fails_descriptor_with_bad_resources(resources, location, num_issues):
def test_fails_descriptor_with_bad_resources(resources, jsonpath, num_issues):
"""Should fail descriptor with malformed resources."""
descriptor = {
"name": "a name with spaces",
Expand All @@ -50,7 +50,7 @@ def test_fails_descriptor_with_bad_resources(resources, location, num_issues):
issues = check(descriptor)

assert len(issues) == num_issues
assert issues[0].location == location
assert issues[0].jsonpath == jsonpath


def test_fails_descriptor_with_missing_required_fields():
Expand All @@ -65,7 +65,7 @@ def test_fails_descriptor_with_missing_required_fields():

assert len(issues) == 2
assert all(issue.type == "required" for issue in issues)
assert {issue.location for issue in issues} == {
assert {issue.jsonpath for issue in issues} == {
"$.licenses[0].name",
"$.licenses[0].path",
}
Expand All @@ -81,7 +81,7 @@ def test_fails_descriptor_with_bad_type():

assert len(issues) == 1
assert issues[0].type == "type"
assert issues[0].location == "$.name"
assert issues[0].jsonpath == "$.name"


def test_fails_descriptor_with_bad_format():
Expand All @@ -96,7 +96,7 @@ def test_fails_descriptor_with_bad_format():

assert len(issues) == 1
assert issues[0].type == "format"
assert issues[0].location == "$.homepage"
assert issues[0].jsonpath == "$.homepage"


def test_fails_descriptor_with_pattern_mismatch():
Expand All @@ -111,7 +111,7 @@ def test_fails_descriptor_with_pattern_mismatch():

assert len(issues) == 1
assert issues[0].type == "pattern"
assert issues[0].location == "$.contributors[0].path"
assert issues[0].jsonpath == "$.contributors[0].path"


# With recommendations
Expand Down Expand Up @@ -161,7 +161,7 @@ def test_fails_descriptor_violating_recommendations():
issues = check(descriptor, config=Config(strict=True))

assert len(issues) == 5
assert {issue.location for issue in issues} == {
assert {issue.jsonpath for issue in issues} == {
"$.name",
"$.version",
"$.contributors[0].title",
Expand Down
2 changes: 0 additions & 2 deletions tools/vulture-allowlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,4 @@
rules # unused variable (src/check_datapackage/config.py:38)
strict # unused variable (src/check_datapackage/config.py:39)
version # unused variable (src/check_datapackage/config.py:40)
target # unused variable (src/check_datapackage/exclude.py:33)
target # unused variable (src/check_datapackage/rule.py:34)
check # unused variable (src/check_datapackage/rule.py:36)