---
title: "Checking package and resource properties"
order: 4
jupyter: python3
---

<!-- TODO: I think this section needs to be updated. -->

The structure and content of package and resource properties follow the
[Frictionless Data Package standard](https://datapackage.org). This
standard defines the available fields for each type of metadata (called
properties), specifies which fields are required, and describes the
allowed values for each field. In addition to these specifications,
Sprout introduces its own structural and formatting requirements.

To make it easy to check metadata against all these requirements, Sprout
provides a set of check functions. Each check function takes a
properties object (`PackageProperties` or `ResourceProperties`) as
input, runs all necessary checks, and raises a group of `CheckError`s if
any checks fail (run `help(CheckError)` for more details). Each
`CheckError` corresponds to a specific violated requirement. If certain
error types are not relevant to your use case, you can configure the
check functions to ignore these.

## Checking package properties

A set of package properties with only the required fields filled in
might look like:

```{python}
import seedcase_sprout as sp
from textwrap import dedent

# For pretty printing of output
from pprint import pprint

package_properties = sp.PackageProperties(
    name="woolly-dormice",
    title="Hibernation Physiology of the Woolly Dormouse: A Scoping Review.",
    description=dedent('''
        This scoping review explores the hibernation physiology of the
        woolly dormouse, drawing on data collected over a 10-year period
        along the Taurus Mountain range in Turkey.
        '''
    ),
    id="123-abc-123",
    created="2014-05-14T05:00:01+00:00",
    version="1.0.0",
    licenses=[sp.LicenseProperties(name="odc-pddl")],
)
```

To check that these properties are indeed complete and well-formed, we
use the `check_package_properties()` function. Since all required fields
(e.g. `name`, `description`, and `title`) are filled out and have the
correct format, the function will not raise any errors and will return
the original input:

```{python}
package_properties = sp.check_package_properties(package_properties)
pprint(package_properties)
```

Now, let's say we didn't include a `description` in our package
properties. Running the check again, we see that an error is raised,
alerting us that this is a required field. See the end of the output:

```{python}
#| error: true
package_properties = sp.PackageProperties(
    name="woolly-dormice",
    title="Hibernation Physiology of the Woolly Dormouse: A Scoping Review.",
    id="123-abc-123",
    created="2014-05-14T05:00:01+00:00",
    version="1.0.0",
    licenses=[sp.LicenseProperties(name="odc-pddl")],
)
sp.check_package_properties(package_properties)
```

Finally, let's say there are multiple errors in our package properties.
For example, we forgot the `description` and gave a `name` containing
special characters, which doesn't meet the expected format. In this
case, both errors will be listed in the output:

```{python}
#| error: true
package_properties = sp.PackageProperties(
    name="Woolly Dormice (Toros Dağları)",
    title="Hibernation Physiology of the Woolly Dormouse: A Scoping Review.",
    id="123-abc-123",
    created="2014-05-14T05:00:01+00:00",
    version="1.0.0",
    licenses=[sp.LicenseProperties(name="odc-pddl")],
)
sp.check_package_properties(package_properties)
```

Note that `check_package_properties()` is for checking that a set of
package properties is well-formed without considering whether any
associated resource properties are well-formed. This function will only
run checks on package properties fields and not look at resource
properties fields.

::: callout-warning
If your aim is to check whether your properties as a whole meet the
[Frictionless Data Package standard](https://datapackage.org), you need
to check your package and resource properties as a unit. See the
[Checking a full set of properties](#checking-a-full-set-of-properties)
section for detailed instructions.
:::

As an example, in the package properties below, there is a set of
resource properties with all required fields missing. This will be
ignored when the check runs:

```{python}
package_properties = sp.PackageProperties(
    name="woolly-dormice",
    title="Hibernation Physiology of the Woolly Dormouse: A Scoping Review.",
    description=dedent('''
        This scoping review explores the hibernation physiology of the
        woolly dormouse, drawing on data collected over a 10-year period
        along the Taurus Mountain range in Turkey.
        '''
    ),
    id="123-abc-123",
    created="2014-05-14T05:00:01+00:00",
    version="1.0.0",
    licenses=[sp.LicenseProperties(name="odc-pddl")],
    resources=[sp.ResourceProperties()],
)
pprint(sp.check_package_properties(package_properties))
```

## Checking resource properties

To check that a set of resource properties are complete and well-formed,
we use the `check_resource_properties()` function.

In the resource properties below, the required fields `title` and
`description` are missing, and `name` doesn't match the expected format.
An error will be raised for each problem:

```{python}
#| error: true
resource_properties = sp.ResourceProperties(
    name="Woolly Dormice (2015, Toros Dağları)",
)
sp.check_resource_properties(resource_properties)
```

## Checking a full set of properties {#checking-a-full-set-of-properties}

When we want to check both package and resource properties, we can use
the `check_properties()` function. In the properties below, we expect
three checks to fail: the required `description` field is missing in
both the package and resource properties, and the resource `path`
doesn't point to a data file. When we run the check, all three errors
are listed:

```{python}
#| error: true
properties = sp.PackageProperties(
    name="woolly-dormice",
    title="Hibernation Physiology of the Woolly Dormouse: A Scoping Review.",
    id="123-abc-123",
    created="2014-05-14T05:00:01+00:00",
    version="1.0.0",
    licenses=[sp.LicenseProperties(name="odc-pddl")],
    resources=[sp.ResourceProperties(
        name="woolly-dormice-2015",
        title="Body fat percentage in the hibernating woolly dormouse",
        path="https://en.wikipedia.org/wiki/Woolly_dormouse"
        )],
)
sp.check_properties(properties)
```

## Understanding error messages

Let's have a closer look at the (end of the) error message we got in the
previous section:

```{log}
...
|     raise ExceptionGroup(
| ExceptionGroup: The following checks failed on the properties:
| PackageProperties(name='woolly-dormice', id='123-abc-123', title='Hibernation Physiology of the Woolly Dormouse: A Scoping Review.', description=None, homepage=None, version='1.0.0', created='2014-05-14T05:00:01+00:00', contributors=None, keywords=None, image=None, licenses=[LicenseProperties(name='odc-pddl', path=None, title=None)], resources=[ResourceProperties(name='woolly-dormice-2015', path='https://en.wikipedia.org/wiki/Woolly_dormouse', type=None, title='Body fat percentage in the hibernating woolly dormouse', description=None, sources=None, licenses=None, format=None, mediatype=None, encoding=None, bytes=None, hash=None, dialect=None, schema=None)], sources=None) (3 sub-exceptions)
+-+---------------- 1 ----------------
| seedcase_sprout.check_datapackage.check_error.CheckError: Error at `$.description` caused by `required`: 'description' is a required property
+---------------- 2 ----------------
| seedcase_sprout.check_datapackage.check_error.CheckError: Error at `$.resources[0].description` caused by `required`: 'description' is a required property
+------------------------------------
```

First, the offending properties are printed, followed by the list of
errors. Error messages are composed of the following parts:

-   "seedcase_sprout.check_datapackage.check_error.CheckError": the class
    representing the error. Check functions will always throw
    `CheckError`s for failing checks.
-   "Error at `$.resources[0].description`": the location of the error
    in the properties object. `$` corresponds to the topmost layer of
    the object (the root); `.resources` points to the `resources` field
    of this layer; `[0]` indicates that the error is in the 0th (i.e.
    the first, counting from 0) resource properties object; and
    `.description` means that the `description` field of this resource
    properties is at fault.
-   "caused by `required`": the kind of requirement or expectation that
    was violated. Here, the expectation is that the field should be
    present (i.e. it is a required field).
-   "'description' is a required property": a longer, human-readable
    explanation of the error.
