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
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Version 0.5.1 (in development)

- Enhanced documentation by a new page that compiles
the code examples in the `examples` folder.

## Version 0.5.0 (from 2025-02-13)

Expand Down
18 changes: 18 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,26 @@ Note:
the `xrlint.all` convenience module exports all of the above from a
single module.

## CLI API

::: xrlint.cli.engine.XRLint

## Linter API

::: xrlint.linter.new_linter

::: xrlint.linter.Linter

## Plugin API

::: xrlint.plugin.new_plugin

::: xrlint.plugin.Plugin

::: xrlint.plugin.PluginMeta

## Configuration API

::: xrlint.config.Config

::: xrlint.config.ConfigObject
Expand All @@ -66,6 +74,8 @@ Note:

::: xrlint.config.ConfigObjectLike

## Rule API

::: xrlint.rule.define_rule

::: xrlint.rule.Rule
Expand All @@ -78,6 +88,8 @@ Note:

::: xrlint.rule.RuleExit

## Dataset Node API

::: xrlint.node.Node

::: xrlint.node.XarrayNode
Expand All @@ -92,6 +104,8 @@ Note:

::: xrlint.node.AttrNode

## Processor API

::: xrlint.processor.define_processor

::: xrlint.processor.Processor
Expand All @@ -100,12 +114,16 @@ Note:

::: xrlint.processor.ProcessorOp

## Result API

::: xrlint.result.Result

::: xrlint.result.Message

::: xrlint.result.Suggestion

## Testing API

::: xrlint.testing.RuleTester

::: xrlint.testing.RuleTest
Expand Down
35 changes: 35 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Examples

## Configuration

::: examples.plugin_config

options:
members: false

Source code: [`examples/plugin_config.py`](https://github.com/bcdev/xrlint/blob/main/examples/plugin_config.py)

::: examples.virtual_plugin_config

options:
members: false

Source code: [`examples/virtual_plugin_config.py`](https://github.com/bcdev/xrlint/blob/main/examples/virtual_plugin_config.py)

## Developing rules

::: examples.rule_testing

options:
members: false

Source code: [`examples/rule_testing.py`](https://github.com/bcdev/xrlint/blob/main/examples/rule_testing.py)

## API usage

::: examples.check_s3_bucket

options:
members: false

Source code: [`examples/check_s3_bucket.py`](https://github.com/bcdev/xrlint/blob/main/examples/check_s3_bucket.py)
5 changes: 5 additions & 0 deletions examples/check_s3_bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
# This software is distributed under the terms and conditions of the
# MIT license (https://mit-license.org/).

"""
This code example shows how to use the high-level
Python API to validate the contents of an S3 bucket.
"""

import xrlint.all as xrl

URL = "s3://xcube-test/"
Expand Down
16 changes: 12 additions & 4 deletions examples/plugin_config.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
# Copyright © 2025 Brockmann Consult GmbH.
# This software is distributed under the terms and conditions of the
# MIT license (https://mit-license.org/).

"""
This configuration example shows how to define and use a plugin
using the `Plugin` class and its `define_rule()` decorator method.
"""

# Copyright © 2025 Brockmann Consult GmbH.
# This software is distributed under the terms and conditions of the
# MIT license (https://mit-license.org/).
You can use this example directly via the Python API by passing it's
exported configuration to an instance of the `Linter` class or use
the XRLint CLI:

```bash
xrlint -c examples/plugin_config.py <OPTIONS> <FILES>
```
"""

from xrlint.node import DatasetNode
from xrlint.plugin import new_plugin
Expand Down
37 changes: 27 additions & 10 deletions examples/rule_testing.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
"""
This example demonstrates how to develop new rules.
"""

# Copyright © 2025 Brockmann Consult GmbH.
# This software is distributed under the terms and conditions of the
# MIT license (https://mit-license.org/).

"""
This example demonstrates how to develop new rules.
"""

import xarray as xr

from xrlint.node import DatasetNode
from xrlint.rule import RuleContext, RuleOp, define_rule
from xrlint.testing import RuleTest, RuleTester


# ----------------------------------------------------
# Place the rule implementation code in its own module
# ----------------------------------------------------


@define_rule("good-title")
class GoodTitle(RuleOp):
"""Dataset title should be 'Hello World!'."""

# We just validate the dataset instance here. You can also implement
# the validation of other nodes in this class, e.g.,
# validate_datatree(), validate_variable(), validate_attrs(),
# and validate_attr().
#
def validate_dataset(self, ctx: RuleContext, node: DatasetNode):
good_title = "Hello World!"
if node.dataset.attrs.get("title") != good_title:
Expand All @@ -26,27 +36,34 @@ def validate_dataset(self, ctx: RuleContext, node: DatasetNode):
)


# -----------------
# In another module
# -----------------
# ---------------------------------------------------
# Place the following rule test code in a test module
# ---------------------------------------------------

tester = RuleTester()

valid_dataset = xr.Dataset(attrs=dict(title="Hello World!"))
invalid_dataset = xr.Dataset(attrs=dict(title="Hello Hamburg!"))

# Run test directly
# You can use the tester to run a test directly
#
tester.run(
"good-title",
GoodTitle,
valid=[RuleTest(dataset=valid_dataset)],
# We expect one message to be emitted
invalid=[RuleTest(dataset=invalid_dataset, expected=1)],
)

# or define a test class derived from unitest.TestCase
# ... or generate a test class that will be derived from `unitest.TestCase`.
# This will provide you tooling support via your test runner, e.g., pytest,
# as the tests in `valid` and `invalid` will be transformed into
# test methods of the generated class.
#
GoodTitleTest = tester.define_test(
"good-title",
GoodTitle,
valid=[RuleTest(dataset=valid_dataset)],
invalid=[RuleTest(dataset=invalid_dataset)],
# Note, here we expect a specific message to be emitted
invalid=[RuleTest(dataset=invalid_dataset, expected=["Attribute 'title' wrong."])],
)
16 changes: 12 additions & 4 deletions examples/virtual_plugin_config.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
# Copyright © 2025 Brockmann Consult GmbH.
# This software is distributed under the terms and conditions of the
# MIT license (https://mit-license.org/).

"""
This configuration example demonstrates how to
define and use "virtual" plugins. Such plugins
can be defined inside a configuration item.
"""

# Copyright © 2025 Brockmann Consult GmbH.
# This software is distributed under the terms and conditions of the
# MIT license (https://mit-license.org/).
You can use this example directly via the Python API by passing it's
exported configuration to an instance of the `Linter` class or use
the XRLint CLI:

```bash
xrlint -c examples/virtual_plugin_config.py <OPTIONS> <FILES>
```
"""

from xrlint.node import DatasetNode
from xrlint.rule import RuleContext, RuleOp, define_rule
Expand Down
10 changes: 9 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ nav:
- Rule Reference: rule-ref.md
- CLI: cli.md
- Python API: api.md
- Examples: examples.md
- About: about.md

theme:
Expand Down Expand Up @@ -55,9 +56,16 @@ plugins:
python:
options:
docstring_style: google
docstring_section_style: list
show_object_full_path: true
show_root_toc_entry: true
show_root_heading: true
show_source: true
heading_level: 2
show_category_heading: true
show_symbol_type_heading: true
show_symbol_type_toc: true
heading_level: 3
annotations_path: brief
members_order: source
extra:
show_overloads: true