diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7f8d029..13bdd3f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,12 +26,9 @@ jobs: python -m pip install --upgrade pip pip install .[dev,doc] - - name: Lint with flake8 + - name: Lint with ruff run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + ruff check - name: Run unit tests shell: bash -l {0} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0a03232..fa2d1d1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,12 +29,9 @@ jobs: run: | python -m pip install --upgrade pip pip install .[dev,doc] - - name: Lint with flake8 + - name: Lint with ruff run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + ruff check - name: Test with pytest run: | pytest --cov=xrlint --cov-branch --cov-report=xml diff --git a/CHANGES.md b/CHANGES.md index c61a513..6ea22ae 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,9 @@ - Improved overall test coverage. +- Switched to [ruff](https://docs.astral.sh/ruff/) + as default linter and formatter. + ## Version 0.2.0 (14.01.2025) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 91b9c8c..f9fefa0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,8 +20,9 @@ checklist are addressed in your PR. **PR checklist** -* Format code using [black](https://black.readthedocs.io/) with default settings. - Check also section [code style](#code-style) below. +* Format and check code using [ruff](https://docs.astral.sh/ruff/) with + default settings: `ruff format` and `ruff check`. See also section + [code style](#code-style) below. * Your change shall not break existing unit tests. `pytest` must run without errors. * Add unit tests for any new code not yet covered by tests. @@ -33,16 +34,18 @@ checklist are addressed in your PR. ## Code style -The XRLint code complies to [PEP-8](https://pep8.org/) except for a line -length of 88 characters as recommended by [black](https://black.readthedocs.io/). -Since black is un-opinionated regarding the order of imports, -we use the following three import blocks separated by an empty -line: +The code style of XRLint equals the default settings +of [black](https://black.readthedocs.io/). Since black is +un-opinionated regarding the order of imports, we group and +sort imports statements according to the default settings of +[isort](https://pycqa.github.io/isort/) which boils down to +0. Future imports 1. Python standard library imports, e.g., `os`, `typing`, etc 2. 3rd-party imports, e.g., `xarray`, `zarr`, etc -3. XRLint module imports: - Prefer absolute import paths: `from xrlint.a.b.c import d`. +3. 1st-party XRLint module imports using absolute paths, + e.g., `from xrlint.a.b.c import d`. +4. 1st-party XRLint module imports from local modules: Relative imports such as `from .c import d` are ok while `..c import d` are not ok. diff --git a/README.md b/README.md index bf9b83b..a66acd9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ [![CI](https://github.com/bcdev/xrlint/actions/workflows/tests.yml/badge.svg)](https://github.com/bcdev/xrlint/actions/workflows/tests.yml) [![codecov](https://codecov.io/gh/bcdev/xrlint/graph/badge.svg?token=GVKuJao97t)](https://codecov.io/gh/bcdev/xrlint) [![PyPI Version](https://img.shields.io/pypi/v/xrlint)](https://pypi.org/project/xrlint/) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -[![GitHub License](https://img.shields.io/github/license/bcdev/xrlint)](https://github.com/bcdev/xrlint) +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v0.json)](https://github.com/charliermarsh/ruff)[![GitHub License](https://img.shields.io/github/license/bcdev/xrlint)](https://github.com/bcdev/xrlint) # XRLint - A linter for xarray datasets diff --git a/docs/about.md b/docs/about.md index 5c9df78..ce83e5e 100644 --- a/docs/about.md +++ b/docs/about.md @@ -50,12 +50,12 @@ pytest --cov=xrlint --cov-report html ### Code Style -XRLint source code is formatted using the [black](https://black.readthedocs.io/) tool and -quality-controlled using [flake8](https://flake8.pycqa.org/). +XRLint source code is formatted and quality-controlled using +using [ruff](https://docs.astral.sh/ruff/): ```bash -black . -flake8 --max-line-length=88 . +ruff format +ruff check ``` ### Documentation diff --git a/environment.yml b/environment.yml index 3d9a0f2..5b3fd1b 100644 --- a/environment.yml +++ b/environment.yml @@ -10,8 +10,6 @@ dependencies: - tabulate - xarray # Dev Dependencies - - black - - flake8 - mkdocs - mkdocs-autorefs - mkdocs-material @@ -20,6 +18,7 @@ dependencies: - pytest - pytest-cov - requests-mock + - ruff # Testing Datasets - pandas - netcdf4 diff --git a/examples/plugin_config.py b/examples/plugin_config.py index dd72060..b08139e 100644 --- a/examples/plugin_config.py +++ b/examples/plugin_config.py @@ -6,9 +6,7 @@ from xrlint.config import Config from xrlint.node import DatasetNode from xrlint.plugin import new_plugin -from xrlint.rule import RuleContext -from xrlint.rule import RuleOp - +from xrlint.rule import RuleContext, RuleOp plugin = new_plugin( name="hello-plugin", diff --git a/examples/rule_testing.py b/examples/rule_testing.py index 7ec7917..98373f1 100644 --- a/examples/rule_testing.py +++ b/examples/rule_testing.py @@ -5,11 +5,8 @@ import xarray as xr from xrlint.node import DatasetNode -from xrlint.rule import RuleContext -from xrlint.rule import RuleOp -from xrlint.rule import define_rule -from xrlint.testing import RuleTest -from xrlint.testing import RuleTester +from xrlint.rule import RuleContext, RuleOp, define_rule +from xrlint.testing import RuleTest, RuleTester @define_rule("good-title") diff --git a/examples/virtual_plugin_config.py b/examples/virtual_plugin_config.py index 4837d59..858d878 100644 --- a/examples/virtual_plugin_config.py +++ b/examples/virtual_plugin_config.py @@ -5,9 +5,7 @@ """ from xrlint.node import DatasetNode -from xrlint.rule import RuleContext -from xrlint.rule import RuleOp -from xrlint.rule import define_rule +from xrlint.rule import RuleContext, RuleOp, define_rule @define_rule("good-title", description="Dataset title should be 'Hello World!'.") diff --git a/notebooks/xrlint-cli.ipynb b/notebooks/xrlint-cli.ipynb index 5b471c9..0118519 100644 --- a/notebooks/xrlint-cli.ipynb +++ b/notebooks/xrlint-cli.ipynb @@ -11,7 +11,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -22,15 +22,19 @@ "\n", " Validate the given dataset FILES.\n", "\n", - " Reads configuration from `./xrlint_config.*` if such file exists and unless\n", - " `--no_config_lookup` is set or `--config` is provided. Then validates each\n", - " dataset in FILES against the configuration. The default dataset patters are\n", - " `**/*.zarr` and `**/.nc`. FILES may comprise also directories. If a\n", - " directory is not matched by any file pattern, it will be traversed\n", - " recursively. The validation result is dumped to standard output if not\n", - " otherwise stated by `--output-file`. The output format is `simple` by\n", - " default. Other inbuilt formats are `json` and `html` which you can specify\n", - " using the `--format` option.\n", + " Reads configuration from './xrlint_config.*' if such file exists and unless\n", + " '--no_config_lookup' is set or '--config' is provided. It then validates\n", + " each dataset in FILES against the configuration. The default dataset patters\n", + " are '**/*.zarr' and '**/.nc'. FILES may comprise also directories or URLs.\n", + " The supported URL protocols are the ones supported by xarray. Using remote\n", + " protocols may require installing additional packages such as S3Fs\n", + " (https://s3fs.readthedocs.io/) for the 's3' protocol.\n", + "\n", + " If a directory is provided that not matched by any file pattern, it will be\n", + " traversed recursively. The validation result is dumped to standard output if\n", + " not otherwise stated by '--output-file'. The output format is 'simple' by\n", + " default. Other inbuilt formats are 'json' and 'html' which you can specify\n", + " using the '--format' option.\n", "\n", "Options:\n", " --no-config-lookup Disable use of default configuration from\n", @@ -59,7 +63,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -68,26 +72,27 @@ "'C:\\\\Users\\\\norma\\\\Projects\\\\xrlint\\\\notebooks'" ] }, - "execution_count": 5, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import os\n", + "\n", "os.getcwd()" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "metadata": {}, "outputs": [ { - "name": "stderr", + "name": "stdout", "output_type": "stream", "text": [ - "Error: file xrlint_config.yaml already exists.\n" + "Configuration template written to xrlint_config.yaml\n" ] } ], @@ -97,16 +102,16 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 7, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -120,7 +125,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -146,7 +151,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -180,7 +185,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -190,7 +195,7 @@ "{\n", " \"name\": \"\",\n", " \"plugins\": {\n", - " \"__core__\": \"xrlint.plugins.core\"\n", + " \"__core__\": \"xrlint.plugins.core:export_plugin\"\n", " },\n", " \"rules\": {\n", " \"coords-for-dims\": 2,\n", @@ -231,7 +236,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.11" + "version": "3.13.1" } }, "nbformat": 4, diff --git a/notebooks/xrlint-linter.ipynb b/notebooks/xrlint-linter.ipynb index 2e4ba44..693f719 100644 --- a/notebooks/xrlint-linter.ipynb +++ b/notebooks/xrlint-linter.ipynb @@ -39,7 +39,7 @@ { "data": { "text/plain": [ - "'0.1.0.dev0'" + "'0.3.0.dev0'" ] }, "execution_count": 2, @@ -437,38 +437,38 @@ " * time (time) int64 32B 2010 2011 2012 2013\n", " spatial_ref int64 8B 0\n", "Data variables:\n", - " sst (time, y, x) float64 192B 0.6358 0.8142 ... 0.7259 0.7808\n", - " sst_anomaly (time, y, x) float64 192B 0.6082 0.7162 ... 0.8605 0.3188\n", + " sst (time, y, x) float64 192B 0.1676 0.4534 0.7707 ... 0.9883 0.771\n", + " sst_anomaly (time, y, x) float64 192B 0.7321 0.6003 ... 0.7045 0.7164\n", "Attributes:\n", - " title: SST-Climatology Subset" + " title: SST-Climatology Subset" ], "text/plain": [ " Size: 464B\n", @@ -479,8 +479,8 @@ " * time (time) int64 32B 2010 2011 2012 2013\n", " spatial_ref int64 8B 0\n", "Data variables:\n", - " sst (time, y, x) float64 192B 0.6358 0.8142 ... 0.7259 0.7808\n", - " sst_anomaly (time, y, x) float64 192B 0.6082 0.7162 ... 0.8605 0.3188\n", + " sst (time, y, x) float64 192B 0.1676 0.4534 0.7707 ... 0.9883 0.771\n", + " sst_anomaly (time, y, x) float64 192B 0.7321 0.6003 ... 0.7045 0.7164\n", "Attributes:\n", " title: SST-Climatology Subset" ] @@ -492,6 +492,7 @@ ], "source": [ "from mkdataset import make_dataset\n", + "\n", "ds = make_dataset()\n", "ds" ] @@ -516,7 +517,7 @@ "

<dataset> - ok

\n" ], "text/plain": [ - "Result(config=Config(name=None, files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'__core__': Plugin(meta=PluginMeta(name='__core__', version='0.1.0.dev0', module='xrlint.plugins.core'), configs={'recommended': Config(name='recommended', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), 'all': Config(name='all', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=2, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=2, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=2, args=(), kwargs={})}, settings=None)}, rules={'coords-for-dims': Rule(meta=RuleMeta(name='coords-for-dims', version='1.0.0', description='Dimensions of data variables should have corresponding coordinates.', docs_url=None, schema=None, type='problem'), op_class=), 'dataset-title-attr': Rule(meta=RuleMeta(name='dataset-title-attr', version='1.0.0', description='Datasets should be given a non-empty title.', docs_url=None, schema=None, type='suggestion'), op_class=), 'grid-mappings': Rule(meta=RuleMeta(name='grid-mappings', version='1.0.0', description='Grid mappings, if any, shall have valid grid mapping coordinate variables.', docs_url=None, schema=None, type='problem'), op_class=), 'no-empty-attrs': Rule(meta=RuleMeta(name='no-empty-attrs', version='1.0.0', description='Every dataset element should have metadata that describes it.', docs_url=None, schema=None, type='suggestion'), op_class=), 'var-units-attr': Rule(meta=RuleMeta(name='var-units-attr', version='1.0.0', description=\"Every variable should have a valid 'units' attribute.\", docs_url=None, schema=None, type='suggestion'), op_class=)}, processors={})}, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), file_path='', messages=[], fixable_error_count=0, fixable_warning_count=0, error_count=0, fatal_error_count=0, warning_count=0)" + "Result(config=Config(name=None, files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'__core__': Plugin(meta=PluginMeta(name='__core__', version='0.3.0.dev0', ref='xrlint.plugins.core:export_plugin'), rules={'coords-for-dims': Rule(meta=RuleMeta(name='coords-for-dims', version='1.0.0', description='Dimensions of data variables should have corresponding coordinates.', schema=None, ref=None, docs_url=None, type='problem'), op_class=), 'dataset-title-attr': Rule(meta=RuleMeta(name='dataset-title-attr', version='1.0.0', description='Datasets should be given a non-empty title.', schema=None, ref=None, docs_url=None, type='suggestion'), op_class=), 'grid-mappings': Rule(meta=RuleMeta(name='grid-mappings', version='1.0.0', description='Grid mappings, if any, shall have valid grid mapping coordinate variables.', schema=None, ref=None, docs_url=None, type='problem'), op_class=), 'no-empty-attrs': Rule(meta=RuleMeta(name='no-empty-attrs', version='1.0.0', description='Every dataset element should have metadata that describes it.', schema=None, ref=None, docs_url=None, type='suggestion'), op_class=), 'var-units-attr': Rule(meta=RuleMeta(name='var-units-attr', version='1.0.0', description=\"Every variable should have a valid 'units' attribute.\", schema=None, ref=None, docs_url=None, type='suggestion'), op_class=)}, processors={}, configs={'recommended': Config(name='recommended', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), 'all': Config(name='all', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=2, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=2, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=2, args=(), kwargs={})}, settings=None)})}, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), file_path='', messages=[], fixable_error_count=0, fixable_warning_count=0, error_count=0, fatal_error_count=0, warning_count=0)" ] }, "execution_count": 5, @@ -530,7 +531,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -914,38 +915,38 @@ " * time (time) int64 32B 2010 2011 2012 2013\n", " spatial_ref int64 8B 0\n", "Data variables:\n", - " sst (time, y, x) float64 192B 0.9772 0.2759 ... 0.08963 0.3726\n", - " sst_anomaly (time, y, x) float64 192B 0.9646 0.2598 ... 0.4992 0.4611\n", - " sst_avg (x, y) float64 48B 0.7927 0.9416 0.3733 0.389 0.596 0.02801" + " sst (time, y, x) float64 192B 0.7409 0.5421 ... 0.09942 0.921\n", + " sst_anomaly (time, y, x) float64 192B 0.2994 0.2066 0.2855 ... 0.304 0.2268\n", + " sst_avg (x, y) float64 48B 0.8309 0.2449 0.4027 0.01275 0.7599 0.4267" ], "text/plain": [ " Size: 512B\n", @@ -956,12 +957,12 @@ " * time (time) int64 32B 2010 2011 2012 2013\n", " spatial_ref int64 8B 0\n", "Data variables:\n", - " sst (time, y, x) float64 192B 0.9772 0.2759 ... 0.08963 0.3726\n", - " sst_anomaly (time, y, x) float64 192B 0.9646 0.2598 ... 0.4992 0.4611\n", - " sst_avg (x, y) float64 48B 0.7927 0.9416 0.3733 0.389 0.596 0.02801" + " sst (time, y, x) float64 192B 0.7409 0.5421 ... 0.09942 0.921\n", + " sst_anomaly (time, y, x) float64 192B 0.2994 0.2066 0.2855 ... 0.304 0.2268\n", + " sst_avg (x, y) float64 48B 0.8309 0.2449 0.4027 0.01275 0.7599 0.4267" ] }, - "execution_count": 21, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -975,7 +976,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -991,10 +992,10 @@ "

3 warnings

\n" ], "text/plain": [ - "Result(config=Config(name=None, files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'__core__': Plugin(meta=PluginMeta(name='__core__', version='0.1.0.dev0', module='xrlint.plugins.core'), configs={'recommended': Config(name='recommended', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), 'all': Config(name='all', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=2, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=2, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=2, args=(), kwargs={})}, settings=None)}, rules={'coords-for-dims': Rule(meta=RuleMeta(name='coords-for-dims', version='1.0.0', description='Dimensions of data variables should have corresponding coordinates.', docs_url=None, schema=None, type='problem'), op_class=), 'dataset-title-attr': Rule(meta=RuleMeta(name='dataset-title-attr', version='1.0.0', description='Datasets should be given a non-empty title.', docs_url=None, schema=None, type='suggestion'), op_class=), 'grid-mappings': Rule(meta=RuleMeta(name='grid-mappings', version='1.0.0', description='Grid mappings, if any, shall have valid grid mapping coordinate variables.', docs_url=None, schema=None, type='problem'), op_class=), 'no-empty-attrs': Rule(meta=RuleMeta(name='no-empty-attrs', version='1.0.0', description='Every dataset element should have metadata that describes it.', docs_url=None, schema=None, type='suggestion'), op_class=), 'var-units-attr': Rule(meta=RuleMeta(name='var-units-attr', version='1.0.0', description=\"Every variable should have a valid 'units' attribute.\", docs_url=None, schema=None, type='suggestion'), op_class=)}, processors={})}, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), file_path='', messages=[Message(message=\"Missing 'title' attribute in dataset.\", node_path='dataset', rule_id='dataset-title-attr', severity=1, fatal=None, fix=None, suggestions=None), Message(message='Missing metadata, attributes are empty.', node_path='dataset.attrs', rule_id='no-empty-attrs', severity=1, fatal=None, fix=None, suggestions=[Suggestion(desc='Make sure to add appropriate metadata attributes to dataset elements.', data=None, fix=None)]), Message(message=\"Invalid 'units' attribute in variable 'sst'.\", node_path=\"dataset.data_vars['sst']\", rule_id='var-units-attr', severity=1, fatal=None, fix=None, suggestions=None)], fixable_error_count=0, fixable_warning_count=0, error_count=0, fatal_error_count=0, warning_count=3)" + "Result(config=Config(name=None, files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'__core__': Plugin(meta=PluginMeta(name='__core__', version='0.3.0.dev0', ref='xrlint.plugins.core:export_plugin'), rules={'coords-for-dims': Rule(meta=RuleMeta(name='coords-for-dims', version='1.0.0', description='Dimensions of data variables should have corresponding coordinates.', schema=None, ref=None, docs_url=None, type='problem'), op_class=), 'dataset-title-attr': Rule(meta=RuleMeta(name='dataset-title-attr', version='1.0.0', description='Datasets should be given a non-empty title.', schema=None, ref=None, docs_url=None, type='suggestion'), op_class=), 'grid-mappings': Rule(meta=RuleMeta(name='grid-mappings', version='1.0.0', description='Grid mappings, if any, shall have valid grid mapping coordinate variables.', schema=None, ref=None, docs_url=None, type='problem'), op_class=), 'no-empty-attrs': Rule(meta=RuleMeta(name='no-empty-attrs', version='1.0.0', description='Every dataset element should have metadata that describes it.', schema=None, ref=None, docs_url=None, type='suggestion'), op_class=), 'var-units-attr': Rule(meta=RuleMeta(name='var-units-attr', version='1.0.0', description=\"Every variable should have a valid 'units' attribute.\", schema=None, ref=None, docs_url=None, type='suggestion'), op_class=)}, processors={}, configs={'recommended': Config(name='recommended', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), 'all': Config(name='all', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=2, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=2, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=2, args=(), kwargs={})}, settings=None)})}, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), file_path='', messages=[Message(message=\"Missing 'title' attribute in dataset.\", node_path='dataset', rule_id='dataset-title-attr', severity=1, fatal=None, fix=None, suggestions=None), Message(message='Missing metadata, attributes are empty.', node_path='dataset.attrs', rule_id='no-empty-attrs', severity=1, fatal=None, fix=None, suggestions=[Suggestion(desc='Make sure to add appropriate metadata attributes to dataset elements.', data=None, fix=None)]), Message(message=\"Invalid 'units' attribute in variable 'sst'.\", node_path=\"dataset.data_vars['sst']\", rule_id='var-units-attr', severity=1, fatal=None, fix=None, suggestions=None)], fixable_error_count=0, fixable_warning_count=0, error_count=0, fatal_error_count=0, warning_count=3)" ] }, - "execution_count": 22, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -1024,11 +1025,11 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ - "from xrlint.plugins.xcube import export_plugin \n", + "from xrlint.plugins.xcube import export_plugin\n", "\n", "xcube_plugin = export_plugin()\n", "xcube_recommended = xcube_plugin.configs[\"recommended\"]\n", @@ -1041,14 +1042,14 @@ " \"dataset-title-attr\": \"warn\",\n", " \"grid-mappings\": \"error\",\n", " \"var-units-attr\": \"error\",\n", - " **xcube_recommended.rules\n", - " }\n", + " **xcube_recommended.rules,\n", + " },\n", ")" ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -1068,10 +1069,10 @@ "

7 problems (2 errors and 5 warnings)

\n" ], "text/plain": [ - "Result(config=Config(name=None, files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'__core__': Plugin(meta=PluginMeta(name='__core__', version='0.1.0.dev0', module='xrlint.plugins.core'), configs={'recommended': Config(name='recommended', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), 'all': Config(name='all', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=2, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=2, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=2, args=(), kwargs={})}, settings=None)}, rules={'coords-for-dims': Rule(meta=RuleMeta(name='coords-for-dims', version='1.0.0', description='Dimensions of data variables should have corresponding coordinates.', docs_url=None, schema=None, type='problem'), op_class=), 'dataset-title-attr': Rule(meta=RuleMeta(name='dataset-title-attr', version='1.0.0', description='Datasets should be given a non-empty title.', docs_url=None, schema=None, type='suggestion'), op_class=), 'grid-mappings': Rule(meta=RuleMeta(name='grid-mappings', version='1.0.0', description='Grid mappings, if any, shall have valid grid mapping coordinate variables.', docs_url=None, schema=None, type='problem'), op_class=), 'no-empty-attrs': Rule(meta=RuleMeta(name='no-empty-attrs', version='1.0.0', description='Every dataset element should have metadata that describes it.', docs_url=None, schema=None, type='suggestion'), op_class=), 'var-units-attr': Rule(meta=RuleMeta(name='var-units-attr', version='1.0.0', description=\"Every variable should have a valid 'units' attribute.\", docs_url=None, schema=None, type='suggestion'), op_class=)}, processors={}), 'xcube': Plugin(meta=PluginMeta(name='xcube', version='0.1.0.dev0', module='xrlint.plugins.xcube'), configs={'recommended': Config(name='xcube-recommended', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'xcube': ...}, rules={'xcube/any-spatial-data-var': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/cube-dims-order': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/data-var-colors': RuleConfig(severity=1, args=(), kwargs={}), 'xcube/grid-mapping-naming': RuleConfig(severity=1, args=(), kwargs={}), 'xcube/increasing-time': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/lat-lon-naming': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/single-grid-mapping': RuleConfig(severity=2, args=(), kwargs={})}, settings=None), 'all': Config(name='xcube-all', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'xcube': ...}, rules={'xcube/any-spatial-data-var': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/cube-dims-order': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/data-var-colors': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/grid-mapping-naming': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/increasing-time': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/lat-lon-naming': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/single-grid-mapping': RuleConfig(severity=2, args=(), kwargs={})}, settings=None)}, rules={'any-spatial-data-var': Rule(meta=RuleMeta(name='any-spatial-data-var', version='1.0.0', description='A datacube should have spatial data variables.', docs_url=None, schema=None, type='problem'), op_class=), 'cube-dims-order': Rule(meta=RuleMeta(name='cube-dims-order', version='1.0.0', description='Order of dimensions in spatio-temporal datacube variables should be [time, ..., y, x].', docs_url=None, schema=None, type='problem'), op_class=), 'data-var-colors': Rule(meta=RuleMeta(name='data-var-colors', version='1.0.0', description='Spatial data variables should encode xcube color mappings in their metadata.', docs_url='https://xcube.readthedocs.io/en/latest/cubespec.html#encoding-of-colors', schema=None, type='suggestion'), op_class=), 'grid-mapping-naming': Rule(meta=RuleMeta(name='grid-mapping-naming', version='1.0.0', description=\"Grid mapping variables should be called 'spatial_ref' or 'crs' for compatibility with rioxarray and other packages.\", docs_url=None, schema=None, type='suggestion'), op_class=), 'increasing-time': Rule(meta=RuleMeta(name='increasing-time', version='1.0.0', description='Time coordinate labels should be monotonically increasing.', docs_url=None, schema=None, type='problem'), op_class=), 'lat-lon-naming': Rule(meta=RuleMeta(name='lat-lon-naming', version='1.0.0', description=\"Latitude and longitude coordinates and dimensions should be called 'lat' and 'lon'.\", docs_url=None, schema=None, type='problem'), op_class=), 'single-grid-mapping': Rule(meta=RuleMeta(name='single-grid-mapping', version='1.0.0', description='A single grid mapping shall be used for all spatial data variables of a datacube.', docs_url=None, schema=None, type='problem'), op_class=)}, processors={})}, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=[], kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=[], kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=[], kwargs={}), 'var-units-attr': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/any-spatial-data-var': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/cube-dims-order': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/data-var-colors': RuleConfig(severity=1, args=(), kwargs={}), 'xcube/grid-mapping-naming': RuleConfig(severity=1, args=(), kwargs={}), 'xcube/increasing-time': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/lat-lon-naming': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/single-grid-mapping': RuleConfig(severity=2, args=(), kwargs={})}, settings=None), file_path='', messages=[Message(message=\"Missing 'title' attribute in dataset.\", node_path='dataset', rule_id='dataset-title-attr', severity=1, fatal=None, fix=None, suggestions=None), Message(message='Missing metadata, attributes are empty.', node_path='dataset.attrs', rule_id='no-empty-attrs', severity=1, fatal=None, fix=None, suggestions=[Suggestion(desc='Make sure to add appropriate metadata attributes to dataset elements.', data=None, fix=None)]), Message(message=\"Invalid 'units' attribute in variable 'sst'.\", node_path=\"dataset.data_vars['sst']\", rule_id='var-units-attr', severity=2, fatal=None, fix=None, suggestions=None), Message(message='Order of dimensions should be y,x, but found x,y.', node_path=\"dataset.data_vars['sst_avg']\", rule_id='xcube/cube-dims-order', severity=2, fatal=None, fix=None, suggestions=[Suggestion(desc='Use xarray.transpose(...) to reorder dimensions.', data=None, fix=None)]), Message(message=\"Missing attribute 'color_bar_name'\", node_path=\"dataset.data_vars['sst']\", rule_id='xcube/data-var-colors', severity=1, fatal=None, fix=None, suggestions=None), Message(message=\"Missing attribute 'color_bar_name'\", node_path=\"dataset.data_vars['sst_anomaly']\", rule_id='xcube/data-var-colors', severity=1, fatal=None, fix=None, suggestions=None), Message(message=\"Missing attribute 'color_bar_name'\", node_path=\"dataset.data_vars['sst_avg']\", rule_id='xcube/data-var-colors', severity=1, fatal=None, fix=None, suggestions=None)], fixable_error_count=0, fixable_warning_count=0, error_count=2, fatal_error_count=0, warning_count=5)" + "Result(config=Config(name=None, files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'__core__': Plugin(meta=PluginMeta(name='__core__', version='0.3.0.dev0', ref='xrlint.plugins.core:export_plugin'), rules={'coords-for-dims': Rule(meta=RuleMeta(name='coords-for-dims', version='1.0.0', description='Dimensions of data variables should have corresponding coordinates.', schema=None, ref=None, docs_url=None, type='problem'), op_class=), 'dataset-title-attr': Rule(meta=RuleMeta(name='dataset-title-attr', version='1.0.0', description='Datasets should be given a non-empty title.', schema=None, ref=None, docs_url=None, type='suggestion'), op_class=), 'grid-mappings': Rule(meta=RuleMeta(name='grid-mappings', version='1.0.0', description='Grid mappings, if any, shall have valid grid mapping coordinate variables.', schema=None, ref=None, docs_url=None, type='problem'), op_class=), 'no-empty-attrs': Rule(meta=RuleMeta(name='no-empty-attrs', version='1.0.0', description='Every dataset element should have metadata that describes it.', schema=None, ref=None, docs_url=None, type='suggestion'), op_class=), 'var-units-attr': Rule(meta=RuleMeta(name='var-units-attr', version='1.0.0', description=\"Every variable should have a valid 'units' attribute.\", schema=None, ref=None, docs_url=None, type='suggestion'), op_class=)}, processors={}, configs={'recommended': Config(name='recommended', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), 'all': Config(name='all', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=2, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=2, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=2, args=(), kwargs={})}, settings=None)}), 'xcube': Plugin(meta=PluginMeta(name='xcube', version='0.3.0.dev0', ref='xrlint.plugins.xcube:export_plugin'), rules={'any-spatial-data-var': Rule(meta=RuleMeta(name='any-spatial-data-var', version='1.0.0', description='A datacube should have spatial data variables.', schema=None, ref=None, docs_url='https://xcube.readthedocs.io/en/latest/cubespec.html#data-model-and-format', type='problem'), op_class=), 'cube-dims-order': Rule(meta=RuleMeta(name='cube-dims-order', version='1.0.0', description='Order of dimensions in spatio-temporal datacube variables should be [time, ..., y, x].', schema=None, ref=None, docs_url='https://xcube.readthedocs.io/en/latest/cubespec.html#data-model-and-format', type='problem'), op_class=), 'data-var-colors': Rule(meta=RuleMeta(name='data-var-colors', version='1.0.0', description='Spatial data variables should encode xcube color mappings in their metadata.', schema=None, ref=None, docs_url='https://xcube.readthedocs.io/en/latest/cubespec.html#encoding-of-colors', type='suggestion'), op_class=), 'grid-mapping-naming': Rule(meta=RuleMeta(name='grid-mapping-naming', version='1.0.0', description=\"Grid mapping variables should be called 'spatial_ref' or 'crs' for compatibility with rioxarray and other packages.\", schema=None, ref=None, docs_url='https://xcube.readthedocs.io/en/latest/cubespec.html#spatial-reference', type='suggestion'), op_class=), 'increasing-time': Rule(meta=RuleMeta(name='increasing-time', version='1.0.0', description='Time coordinate labels should be monotonically increasing.', schema=None, ref=None, docs_url='https://xcube.readthedocs.io/en/latest/cubespec.html#temporal-reference', type='problem'), op_class=), 'lat-lon-naming': Rule(meta=RuleMeta(name='lat-lon-naming', version='1.0.0', description=\"Latitude and longitude coordinates and dimensions should be called 'lat' and 'lon'.\", schema=None, ref=None, docs_url='https://xcube.readthedocs.io/en/latest/cubespec.html#spatial-reference', type='problem'), op_class=), 'single-grid-mapping': Rule(meta=RuleMeta(name='single-grid-mapping', version='1.0.0', description='A single grid mapping shall be used for all spatial data variables of a datacube.', schema=None, ref=None, docs_url='https://xcube.readthedocs.io/en/latest/cubespec.html#spatial-reference', type='problem'), op_class=)}, processors={}, configs={'recommended': Config(name='xcube-recommended', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'xcube': ...}, rules={'xcube/any-spatial-data-var': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/cube-dims-order': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/data-var-colors': RuleConfig(severity=1, args=(), kwargs={}), 'xcube/grid-mapping-naming': RuleConfig(severity=1, args=(), kwargs={}), 'xcube/increasing-time': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/lat-lon-naming': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/single-grid-mapping': RuleConfig(severity=2, args=(), kwargs={})}, settings=None), 'all': Config(name='xcube-all', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'xcube': ...}, rules={'xcube/any-spatial-data-var': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/cube-dims-order': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/data-var-colors': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/grid-mapping-naming': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/increasing-time': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/lat-lon-naming': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/single-grid-mapping': RuleConfig(severity=2, args=(), kwargs={})}, settings=None)})}, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=[], kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=[], kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=[], kwargs={}), 'var-units-attr': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/any-spatial-data-var': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/cube-dims-order': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/data-var-colors': RuleConfig(severity=1, args=(), kwargs={}), 'xcube/grid-mapping-naming': RuleConfig(severity=1, args=(), kwargs={}), 'xcube/increasing-time': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/lat-lon-naming': RuleConfig(severity=2, args=(), kwargs={}), 'xcube/single-grid-mapping': RuleConfig(severity=2, args=(), kwargs={})}, settings=None), file_path='', messages=[Message(message=\"Missing 'title' attribute in dataset.\", node_path='dataset', rule_id='dataset-title-attr', severity=1, fatal=None, fix=None, suggestions=None), Message(message='Missing metadata, attributes are empty.', node_path='dataset.attrs', rule_id='no-empty-attrs', severity=1, fatal=None, fix=None, suggestions=[Suggestion(desc='Make sure to add appropriate metadata attributes to dataset elements.', data=None, fix=None)]), Message(message=\"Invalid 'units' attribute in variable 'sst'.\", node_path=\"dataset.data_vars['sst']\", rule_id='var-units-attr', severity=2, fatal=None, fix=None, suggestions=None), Message(message='Order of dimensions should be y,x, but found x,y.', node_path=\"dataset.data_vars['sst_avg']\", rule_id='xcube/cube-dims-order', severity=2, fatal=None, fix=None, suggestions=[Suggestion(desc='Use xarray.transpose(...) to reorder dimensions.', data=None, fix=None)]), Message(message=\"Missing attribute 'color_bar_name'\", node_path=\"dataset.data_vars['sst']\", rule_id='xcube/data-var-colors', severity=1, fatal=None, fix=None, suggestions=None), Message(message=\"Missing attribute 'color_bar_name'\", node_path=\"dataset.data_vars['sst_anomaly']\", rule_id='xcube/data-var-colors', severity=1, fatal=None, fix=None, suggestions=None), Message(message=\"Missing attribute 'color_bar_name'\", node_path=\"dataset.data_vars['sst_avg']\", rule_id='xcube/data-var-colors', severity=1, fatal=None, fix=None, suggestions=None)], fixable_error_count=0, fixable_warning_count=0, error_count=2, fatal_error_count=0, warning_count=5)" ] }, - "execution_count": 24, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -1091,29 +1092,27 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ - "from xrlint.plugins.core import export_plugin \n", + "from xrlint.plugins.core import export_plugin\n", "\n", "core_plugin = export_plugin()\n", "\n", "linter = xrl.Linter(\n", - " plugins={\n", - " \"humpty-dumpty\": core_plugin\n", - " }, \n", + " plugins={\"humpty-dumpty\": core_plugin},\n", " rules={\n", " \"humpty-dumpty/no-empty-attrs\": \"warn\",\n", " \"humpty-dumpty/dataset-title-attr\": \"error\",\n", - " \"humpty-dumpty/var-units-attr\": \"warn\"\n", - " }\n", + " \"humpty-dumpty/var-units-attr\": \"warn\",\n", + " },\n", ")" ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -1129,10 +1128,10 @@ "

3 problems (one error and 2 warnings)

\n" ], "text/plain": [ - "Result(config=Config(name=None, files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'humpty-dumpty': Plugin(meta=PluginMeta(name='__core__', version='0.1.0.dev0', module='xrlint.plugins.core'), configs={'recommended': Config(name='recommended', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), 'all': Config(name='all', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=2, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=2, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=2, args=(), kwargs={})}, settings=None)}, rules={'coords-for-dims': Rule(meta=RuleMeta(name='coords-for-dims', version='1.0.0', description='Dimensions of data variables should have corresponding coordinates.', docs_url=None, schema=None, type='problem'), op_class=), 'dataset-title-attr': Rule(meta=RuleMeta(name='dataset-title-attr', version='1.0.0', description='Datasets should be given a non-empty title.', docs_url=None, schema=None, type='suggestion'), op_class=), 'grid-mappings': Rule(meta=RuleMeta(name='grid-mappings', version='1.0.0', description='Grid mappings, if any, shall have valid grid mapping coordinate variables.', docs_url=None, schema=None, type='problem'), op_class=), 'no-empty-attrs': Rule(meta=RuleMeta(name='no-empty-attrs', version='1.0.0', description='Every dataset element should have metadata that describes it.', docs_url=None, schema=None, type='suggestion'), op_class=), 'var-units-attr': Rule(meta=RuleMeta(name='var-units-attr', version='1.0.0', description=\"Every variable should have a valid 'units' attribute.\", docs_url=None, schema=None, type='suggestion'), op_class=)}, processors={})}, rules={'humpty-dumpty/no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'humpty-dumpty/dataset-title-attr': RuleConfig(severity=2, args=(), kwargs={}), 'humpty-dumpty/var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), file_path='', messages=[Message(message='Missing metadata, attributes are empty.', node_path='dataset.attrs', rule_id='humpty-dumpty/no-empty-attrs', severity=1, fatal=None, fix=None, suggestions=[Suggestion(desc='Make sure to add appropriate metadata attributes to dataset elements.', data=None, fix=None)]), Message(message=\"Missing 'title' attribute in dataset.\", node_path='dataset', rule_id='humpty-dumpty/dataset-title-attr', severity=2, fatal=None, fix=None, suggestions=None), Message(message=\"Invalid 'units' attribute in variable 'sst'.\", node_path=\"dataset.data_vars['sst']\", rule_id='humpty-dumpty/var-units-attr', severity=1, fatal=None, fix=None, suggestions=None)], fixable_error_count=0, fixable_warning_count=0, error_count=1, fatal_error_count=0, warning_count=2)" + "Result(config=Config(name=None, files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins={'humpty-dumpty': Plugin(meta=PluginMeta(name='__core__', version='0.3.0.dev0', ref='xrlint.plugins.core:export_plugin'), rules={'coords-for-dims': Rule(meta=RuleMeta(name='coords-for-dims', version='1.0.0', description='Dimensions of data variables should have corresponding coordinates.', schema=None, ref=None, docs_url=None, type='problem'), op_class=), 'dataset-title-attr': Rule(meta=RuleMeta(name='dataset-title-attr', version='1.0.0', description='Datasets should be given a non-empty title.', schema=None, ref=None, docs_url=None, type='suggestion'), op_class=), 'grid-mappings': Rule(meta=RuleMeta(name='grid-mappings', version='1.0.0', description='Grid mappings, if any, shall have valid grid mapping coordinate variables.', schema=None, ref=None, docs_url=None, type='problem'), op_class=), 'no-empty-attrs': Rule(meta=RuleMeta(name='no-empty-attrs', version='1.0.0', description='Every dataset element should have metadata that describes it.', schema=None, ref=None, docs_url=None, type='suggestion'), op_class=), 'var-units-attr': Rule(meta=RuleMeta(name='var-units-attr', version='1.0.0', description=\"Every variable should have a valid 'units' attribute.\", schema=None, ref=None, docs_url=None, type='suggestion'), op_class=)}, processors={}, configs={'recommended': Config(name='recommended', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=1, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), 'all': Config(name='all', files=None, ignores=None, linter_options=None, opener_options=None, processor=None, plugins=None, rules={'coords-for-dims': RuleConfig(severity=2, args=(), kwargs={}), 'dataset-title-attr': RuleConfig(severity=2, args=(), kwargs={}), 'grid-mappings': RuleConfig(severity=2, args=(), kwargs={}), 'no-empty-attrs': RuleConfig(severity=2, args=(), kwargs={}), 'var-units-attr': RuleConfig(severity=2, args=(), kwargs={})}, settings=None)})}, rules={'humpty-dumpty/no-empty-attrs': RuleConfig(severity=1, args=(), kwargs={}), 'humpty-dumpty/dataset-title-attr': RuleConfig(severity=2, args=(), kwargs={}), 'humpty-dumpty/var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}, settings=None), file_path='', messages=[Message(message='Missing metadata, attributes are empty.', node_path='dataset.attrs', rule_id='humpty-dumpty/no-empty-attrs', severity=1, fatal=None, fix=None, suggestions=[Suggestion(desc='Make sure to add appropriate metadata attributes to dataset elements.', data=None, fix=None)]), Message(message=\"Missing 'title' attribute in dataset.\", node_path='dataset', rule_id='humpty-dumpty/dataset-title-attr', severity=2, fatal=None, fix=None, suggestions=None), Message(message=\"Invalid 'units' attribute in variable 'sst'.\", node_path=\"dataset.data_vars['sst']\", rule_id='humpty-dumpty/var-units-attr', severity=1, fatal=None, fix=None, suggestions=None)], fixable_error_count=0, fixable_warning_count=0, error_count=1, fatal_error_count=0, warning_count=2)" ] }, - "execution_count": 26, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -1159,7 +1158,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -1168,7 +1167,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -1177,7 +1176,7 @@ "True" ] }, - "execution_count": 28, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -1188,7 +1187,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -1197,7 +1196,7 @@ "True" ] }, - "execution_count": 29, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -1215,7 +1214,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -1224,7 +1223,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -1233,7 +1232,7 @@ "['__core__']" ] }, - "execution_count": 31, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -1244,7 +1243,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -1260,7 +1259,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -1269,7 +1268,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -1278,7 +1277,7 @@ "['__core__']" ] }, - "execution_count": 34, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -1289,7 +1288,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 20, "metadata": { "scrolled": true }, @@ -1304,7 +1303,7 @@ " 'var-units-attr': RuleConfig(severity=1, args=(), kwargs={})}" ] }, - "execution_count": 35, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -1344,7 +1343,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.11" + "version": "3.13.1" } }, "nbformat": 4, diff --git a/pyproject.toml b/pyproject.toml index 2d20549..8981b1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,19 +53,25 @@ exclude = [ [tool.flake8] max-line-length = 88 +[tool.isort] +profile = "black" + +[tool.ruff] +# There is a problem with ruff when linting imports +exclude = ["**/*.ipynb"] + [project.scripts] xrlint = "xrlint.cli.main:main" [project.optional-dependencies] dev = [ # Development tools - "black", - "flake8", + "build", + "hatch", "pytest", "pytest-cov", + "ruff", "twine", - "build", - "hatch", # Dataset testing "netcdf4", "numpy", diff --git a/tests/_linter/test_rulectx.py b/tests/_linter/test_rulectx.py index df7128e..a74600b 100644 --- a/tests/_linter/test_rulectx.py +++ b/tests/_linter/test_rulectx.py @@ -5,7 +5,7 @@ # noinspection PyProtectedMember from xrlint._linter.rulectx import RuleContextImpl from xrlint.config import Config -from xrlint.result import Suggestion, Message +from xrlint.result import Message, Suggestion class RuleContextImplTest(TestCase): diff --git a/tests/cli/helpers.py b/tests/cli/helpers.py index a083971..502deae 100644 --- a/tests/cli/helpers.py +++ b/tests/cli/helpers.py @@ -1,5 +1,5 @@ -from contextlib import contextmanager import os +from contextlib import contextmanager @contextmanager diff --git a/tests/cli/test_config.py b/tests/cli/test_config.py index 881500b..ce70c6b 100644 --- a/tests/cli/test_config.py +++ b/tests/cli/test_config.py @@ -5,11 +5,10 @@ import pytest -from xrlint.cli.config import ConfigError -from xrlint.cli.config import read_config_list -from xrlint.config import Config -from xrlint.config import ConfigList +from xrlint.cli.config import ConfigError, read_config_list +from xrlint.config import Config, ConfigList from xrlint.rule import RuleConfig + from .helpers import text_file yaml_text = """ @@ -92,8 +91,7 @@ def assert_config_ok(self, config: Any, name: str): def test_read_config_invalid_arg(self): with pytest.raises( TypeError, - match="configuration file must be of type str|Path|PathLike," - " but got None", + match="configuration file must be of type str|Path|PathLike, but got None", ): # noinspection PyTypeChecker read_config_list(None) diff --git a/tests/cli/test_main.py b/tests/cli/test_main.py index c9d29cd..bf0fb53 100644 --- a/tests/cli/test_main.py +++ b/tests/cli/test_main.py @@ -1,15 +1,16 @@ import os -import tempfile import shutil +import tempfile from unittest import TestCase import click.testing -from click.testing import CliRunner import xarray as xr +from click.testing import CliRunner -from xrlint.cli.main import main from xrlint.cli.constants import DEFAULT_CONFIG_FILE_YAML +from xrlint.cli.main import main from xrlint.version import version + from .helpers import text_file no_match_config_yaml = """ @@ -239,7 +240,6 @@ def test_init_exists(self): class CliMainMetaTest(TestCase): - def test_help(self): runner = CliRunner() result = runner.invoke(main, ["--help"]) diff --git a/tests/formatters/helpers.py b/tests/formatters/helpers.py index 58497a7..4e26506 100644 --- a/tests/formatters/helpers.py +++ b/tests/formatters/helpers.py @@ -1,13 +1,11 @@ from xrlint.config import Config from xrlint.formatter import FormatterContext from xrlint.plugin import new_plugin -from xrlint.result import Message, ResultStats -from xrlint.result import Result +from xrlint.result import Message, Result, ResultStats from xrlint.rule import RuleOp class FormatterContextImpl(FormatterContext): - def __init__(self, max_warnings: int = -1): self._max_warnings = max_warnings self._result_stats = ResultStats() @@ -26,7 +24,6 @@ def get_context(max_warnings: int = -1) -> FormatterContext: def get_test_results(): - plugin = new_plugin(name="test") @plugin.define_rule( diff --git a/tests/formatters/test_html.py b/tests/formatters/test_html.py index 3834edf..41c99da 100644 --- a/tests/formatters/test_html.py +++ b/tests/formatters/test_html.py @@ -1,7 +1,8 @@ from unittest import TestCase from xrlint.formatters.html import Html -from .helpers import get_test_results, get_context + +from .helpers import get_context, get_test_results class HtmlTest(TestCase): diff --git a/tests/formatters/test_json.py b/tests/formatters/test_json.py index 9477249..c108be5 100644 --- a/tests/formatters/test_json.py +++ b/tests/formatters/test_json.py @@ -1,7 +1,8 @@ from unittest import TestCase from xrlint.formatters.json import Json -from .helpers import get_test_results, get_context + +from .helpers import get_context, get_test_results class JsonTest(TestCase): diff --git a/tests/formatters/test_simple.py b/tests/formatters/test_simple.py index f5206b7..87a2bb9 100644 --- a/tests/formatters/test_simple.py +++ b/tests/formatters/test_simple.py @@ -3,8 +3,7 @@ from tests.formatters.helpers import get_context from xrlint.config import Config from xrlint.formatters.simple import Simple -from xrlint.result import Message -from xrlint.result import Result +from xrlint.result import Message, Result class SimpleTest(TestCase): diff --git a/tests/plugins/core/rules/test_coords_for_dims.py b/tests/plugins/core/rules/test_coords_for_dims.py index 8fcf408..4a9d26a 100644 --- a/tests/plugins/core/rules/test_coords_for_dims.py +++ b/tests/plugins/core/rules/test_coords_for_dims.py @@ -1,7 +1,6 @@ -from xrlint.plugins.core.rules.coords_for_dims import CoordsForDims - import xarray as xr +from xrlint.plugins.core.rules.coords_for_dims import CoordsForDims from xrlint.testing import RuleTest, RuleTester valid_dataset_1 = xr.Dataset(attrs=dict(title="empty")) diff --git a/tests/plugins/core/rules/test_dataset_title_attr.py b/tests/plugins/core/rules/test_dataset_title_attr.py index b927639..f128eda 100644 --- a/tests/plugins/core/rules/test_dataset_title_attr.py +++ b/tests/plugins/core/rules/test_dataset_title_attr.py @@ -1,6 +1,6 @@ -from xrlint.plugins.core.rules.dataset_title_attr import DatasetTitleAttr import xarray as xr +from xrlint.plugins.core.rules.dataset_title_attr import DatasetTitleAttr from xrlint.testing import RuleTest, RuleTester valid_dataset_1 = xr.Dataset(attrs=dict(title="OC-Climatology")) diff --git a/tests/plugins/core/rules/test_grid_mappings.py b/tests/plugins/core/rules/test_grid_mappings.py index 5126a94..70b5277 100644 --- a/tests/plugins/core/rules/test_grid_mappings.py +++ b/tests/plugins/core/rules/test_grid_mappings.py @@ -1,8 +1,7 @@ -from xrlint.plugins.core.rules.grid_mappings import GridMappings - import numpy as np import xarray as xr +from xrlint.plugins.core.rules.grid_mappings import GridMappings from xrlint.testing import RuleTest, RuleTester diff --git a/tests/plugins/core/rules/test_no_empty_attrs.py b/tests/plugins/core/rules/test_no_empty_attrs.py index 4e1bc6a..26f98fa 100644 --- a/tests/plugins/core/rules/test_no_empty_attrs.py +++ b/tests/plugins/core/rules/test_no_empty_attrs.py @@ -1,7 +1,6 @@ -from xrlint.plugins.core.rules.no_empty_attrs import NoEmptyAttrs - import xarray as xr +from xrlint.plugins.core.rules.no_empty_attrs import NoEmptyAttrs from xrlint.testing import RuleTest, RuleTester valid_dataset_1 = xr.Dataset(attrs=dict(title="empty")) diff --git a/tests/plugins/core/rules/test_var_units_attr.py b/tests/plugins/core/rules/test_var_units_attr.py index 342ab7b..99bd715 100644 --- a/tests/plugins/core/rules/test_var_units_attr.py +++ b/tests/plugins/core/rules/test_var_units_attr.py @@ -1,7 +1,7 @@ -from xrlint.plugins.core.rules.var_units_attr import VarUnitsAttr import xarray as xr -from xrlint.testing import RuleTester, RuleTest +from xrlint.plugins.core.rules.var_units_attr import VarUnitsAttr +from xrlint.testing import RuleTest, RuleTester valid_dataset_1 = xr.Dataset() valid_dataset_2 = xr.Dataset( diff --git a/tests/plugins/core/test_plugin.py b/tests/plugins/core/test_plugin.py index 6346032..022e9c9 100644 --- a/tests/plugins/core/test_plugin.py +++ b/tests/plugins/core/test_plugin.py @@ -4,7 +4,6 @@ class ExportPluginTest(TestCase): - def test_configs_complete(self): _plugin = export_plugin() self.assertEqual( diff --git a/tests/plugins/xcube/rules/test_any_spatial_data_var.py b/tests/plugins/xcube/rules/test_any_spatial_data_var.py index 5db259f..a9f27e6 100644 --- a/tests/plugins/xcube/rules/test_any_spatial_data_var.py +++ b/tests/plugins/xcube/rules/test_any_spatial_data_var.py @@ -1,8 +1,7 @@ from xrlint.plugins.xcube.rules.any_spatial_data_var import AnySpatialDataVar -from .test_grid_mapping_naming import make_dataset - -from xrlint.testing import RuleTester, RuleTest +from xrlint.testing import RuleTest, RuleTester +from .test_grid_mapping_naming import make_dataset valid_dataset = make_dataset() invalid_dataset = valid_dataset.drop_vars(["chl", "tsm"]) diff --git a/tests/plugins/xcube/rules/test_cube_dims_order.py b/tests/plugins/xcube/rules/test_cube_dims_order.py index a98ad6e..aacbcb4 100644 --- a/tests/plugins/xcube/rules/test_cube_dims_order.py +++ b/tests/plugins/xcube/rules/test_cube_dims_order.py @@ -1,10 +1,8 @@ import numpy as np - -from xrlint.plugins.xcube.rules.cube_dims_order import CubeDimsOrder - import xarray as xr -from xrlint.testing import RuleTester, RuleTest +from xrlint.plugins.xcube.rules.cube_dims_order import CubeDimsOrder +from xrlint.testing import RuleTest, RuleTester def make_dataset(dims: tuple[str, str, str]): diff --git a/tests/plugins/xcube/rules/test_data_var_colors.py b/tests/plugins/xcube/rules/test_data_var_colors.py index 0f74e53..051fbfb 100644 --- a/tests/plugins/xcube/rules/test_data_var_colors.py +++ b/tests/plugins/xcube/rules/test_data_var_colors.py @@ -1,10 +1,8 @@ import numpy as np - -from xrlint.plugins.xcube.rules.data_var_colors import DataVarColors - import xarray as xr -from xrlint.testing import RuleTester, RuleTest +from xrlint.plugins.xcube.rules.data_var_colors import DataVarColors +from xrlint.testing import RuleTest, RuleTester def make_dataset(): diff --git a/tests/plugins/xcube/rules/test_grid_mapping_naming.py b/tests/plugins/xcube/rules/test_grid_mapping_naming.py index a233781..f4b0800 100644 --- a/tests/plugins/xcube/rules/test_grid_mapping_naming.py +++ b/tests/plugins/xcube/rules/test_grid_mapping_naming.py @@ -1,10 +1,8 @@ import numpy as np - -from xrlint.plugins.xcube.rules.grid_mapping_naming import GridMappingNaming - import xarray as xr -from xrlint.testing import RuleTester, RuleTest +from xrlint.plugins.xcube.rules.grid_mapping_naming import GridMappingNaming +from xrlint.testing import RuleTest, RuleTester def make_dataset(): diff --git a/tests/plugins/xcube/rules/test_increasing_time.py b/tests/plugins/xcube/rules/test_increasing_time.py index 27ee6fe..6444007 100644 --- a/tests/plugins/xcube/rules/test_increasing_time.py +++ b/tests/plugins/xcube/rules/test_increasing_time.py @@ -1,10 +1,8 @@ import numpy as np - -from xrlint.plugins.xcube.rules.increasing_time import IncreasingTime - import xarray as xr -from xrlint.testing import RuleTester, RuleTest +from xrlint.plugins.xcube.rules.increasing_time import IncreasingTime +from xrlint.testing import RuleTest, RuleTester def make_dataset(): diff --git a/tests/plugins/xcube/rules/test_lat_lon_naming.py b/tests/plugins/xcube/rules/test_lat_lon_naming.py index 06029f8..2a986cd 100644 --- a/tests/plugins/xcube/rules/test_lat_lon_naming.py +++ b/tests/plugins/xcube/rules/test_lat_lon_naming.py @@ -1,10 +1,8 @@ import numpy as np - -from xrlint.plugins.xcube.rules.lat_lon_naming import LatLonNaming - import xarray as xr -from xrlint.testing import RuleTester, RuleTest +from xrlint.plugins.xcube.rules.lat_lon_naming import LatLonNaming +from xrlint.testing import RuleTest, RuleTester def make_dataset(lat_dim: str, lon_dim: str): diff --git a/tests/plugins/xcube/rules/test_single_grid_mapping.py b/tests/plugins/xcube/rules/test_single_grid_mapping.py index d66cd99..551776f 100644 --- a/tests/plugins/xcube/rules/test_single_grid_mapping.py +++ b/tests/plugins/xcube/rules/test_single_grid_mapping.py @@ -1,9 +1,8 @@ -from xrlint.plugins.xcube.rules.single_grid_mapping import SingleGridMapping - import numpy as np import xarray as xr -from xrlint.testing import RuleTester, RuleTest +from xrlint.plugins.xcube.rules.single_grid_mapping import SingleGridMapping +from xrlint.testing import RuleTest, RuleTester def make_dataset(): diff --git a/tests/plugins/xcube/test_plugin.py b/tests/plugins/xcube/test_plugin.py index 424f7d1..c486bf9 100644 --- a/tests/plugins/xcube/test_plugin.py +++ b/tests/plugins/xcube/test_plugin.py @@ -4,7 +4,6 @@ class ExportPluginTest(TestCase): - def test_configs_complete(self): _plugin = export_plugin() self.assertEqual( diff --git a/tests/test_all.py b/tests/test_all.py index 2ddc267..df637ca 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -4,8 +4,6 @@ class AllTest(TestCase): def test_api_is_complete(self): import xrlint.all as xrl - - # noinspection PyProtectedMember from xrlint.all import __all__ # noinspection PyUnresolvedReferences diff --git a/tests/test_config.py b/tests/test_config.py index 5d542cf..dcdfd5b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -152,7 +152,6 @@ def test_from_value_fail(self): ConfigList.from_value({}) def test_compute_config(self): - config_list = ConfigList([Config()]) file_path = "s3://wq-services/datacubes/chl-2.zarr" self.assertEqual( diff --git a/tests/test_constants.py b/tests/test_constants.py index b1b2194..0b7bfb4 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -1,8 +1,6 @@ from unittest import TestCase -from xrlint.constants import SEVERITY_CODE_TO_NAME -from xrlint.constants import SEVERITY_ENUM -from xrlint.constants import SEVERITY_ENUM_TEXT +from xrlint.constants import SEVERITY_CODE_TO_NAME, SEVERITY_ENUM, SEVERITY_ENUM_TEXT class ConstantsTest(TestCase): diff --git a/tests/test_formatter.py b/tests/test_formatter.py index 7f71da0..0275c30 100644 --- a/tests/test_formatter.py +++ b/tests/test_formatter.py @@ -1,13 +1,10 @@ from unittest import TestCase -from xrlint.formatter import Formatter -from xrlint.formatter import FormatterOp -from xrlint.formatter import FormatterRegistry +from xrlint.formatter import Formatter, FormatterOp, FormatterRegistry class FormatterRegistryTest(TestCase): def test_decorator_sets_meta(self): - registry = FormatterRegistry() @registry.define_formatter() @@ -22,7 +19,6 @@ def format(self, *args, **kwargs) -> str: self.assertEqual(None, my_rule.meta.schema) def test_decorator_registrations(self): - registry = FormatterRegistry() @registry.define_formatter("my-fmt-a") diff --git a/tests/test_linter.py b/tests/test_linter.py index f0e6939..1dfd196 100644 --- a/tests/test_linter.py +++ b/tests/test_linter.py @@ -5,21 +5,12 @@ from xrlint.config import Config from xrlint.constants import CORE_PLUGIN_NAME -from xrlint.linter import Linter -from xrlint.linter import new_linter +from xrlint.linter import Linter, new_linter +from xrlint.node import AttrNode, AttrsNode, DataArrayNode, DatasetNode from xrlint.plugin import new_plugin -from xrlint.node import ( - AttrsNode, - AttrNode, - DataArrayNode, - DatasetNode, -) from xrlint.processor import ProcessorOp -from xrlint.result import Message -from xrlint.result import Result -from xrlint.rule import RuleContext -from xrlint.rule import RuleExit -from xrlint.rule import RuleOp +from xrlint.result import Message, Result +from xrlint.rule import RuleContext, RuleExit, RuleOp class LinterTest(TestCase): @@ -58,9 +49,7 @@ def test_new_linter(self): class LinterVerifyTest(TestCase): - def setUp(self): - plugin = new_plugin(name="test") @plugin.define_rule("no-space-in-attr-name") @@ -274,7 +263,6 @@ def test_linter_real_life_scenario(self): ) def test_processor(self): - result = self.linter.verify_dataset( "test.levels", config=Config.from_value( diff --git a/tests/test_operation.py b/tests/test_operation.py index 0b286a5..f0abc79 100644 --- a/tests/test_operation.py +++ b/tests/test_operation.py @@ -92,7 +92,6 @@ class MyThingOp3(ThingOp): self.assertIs(MyThingOp3, thing3.op_class) def test_from_value_ok_str(self): - thing1_ = Thing.from_value("tests.test_operation:thing1") self.assertIs(thing1, thing1_) self.assertEqual("tests.test_operation:thing1", thing1_.meta.ref) @@ -161,9 +160,7 @@ class MyThingOp3(ThingOp): class OpMixinDefineTest(TestCase): - def test_define_op(self): - class MyThingOp3(ThingOp): """This is my 3rd thing.""" @@ -179,7 +176,6 @@ class MyThingOp3(ThingOp): self.assertIs(value.meta, MyThingOp3.meta) def test_define_op_fail(self): - class MyThingOp3(ThingOp): """This is my 3rd thing.""" @@ -215,8 +211,7 @@ def test_decorator_fail(self): with pytest.raises( TypeError, match=( - "decorated thing component must be a subclass of ThingOp," - " but got Thing" + "decorated thing component must be a subclass of ThingOp, but got Thing" ), ): closure(Thing) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index a59dd95..3c3bafb 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -3,8 +3,8 @@ import xarray as xr -from xrlint.plugin import new_plugin, Plugin, PluginMeta -from xrlint.processor import ProcessorOp, Processor +from xrlint.plugin import Plugin, PluginMeta, new_plugin +from xrlint.processor import Processor, ProcessorOp from xrlint.result import Message from xrlint.rule import Rule, RuleOp, define_rule @@ -59,10 +59,8 @@ def test_from_value(self): class PluginDefineRuleDecoratorTest(TestCase): - # noinspection PyUnusedLocal def test_decorator(self): - plugin = Plugin(meta=PluginMeta(name="test")) @plugin.define_rule("my-rule-1") @@ -97,10 +95,8 @@ class MyRule3(RuleOp): class PluginDefineProcessorDecoratorTest(TestCase): - # noinspection PyUnusedLocal def test_decorator(self): - plugin = Plugin(meta=PluginMeta(name="test")) @plugin.define_processor("my-processor-1") diff --git a/tests/test_processor.py b/tests/test_processor.py index 927471a..eab125d 100644 --- a/tests/test_processor.py +++ b/tests/test_processor.py @@ -5,15 +5,11 @@ import xarray as xr from xrlint.plugin import new_plugin -from xrlint.processor import Processor -from xrlint.processor import ProcessorMeta -from xrlint.processor import ProcessorOp -from xrlint.processor import define_processor +from xrlint.processor import Processor, ProcessorMeta, ProcessorOp, define_processor from xrlint.result import Message class ProcessorTest(TestCase): - def test_define_processor(self): registry = {} @@ -64,7 +60,6 @@ def postprocess( # noinspection PyMethodMayBeStatic,PyUnusedLocal def test_define_processor_as_decorator_fail(self): - with pytest.raises( TypeError, match=( diff --git a/tests/test_result.py b/tests/test_result.py index 4b92830..6d553e8 100644 --- a/tests/test_result.py +++ b/tests/test_result.py @@ -3,20 +3,18 @@ from xrlint.config import Config from xrlint.plugin import new_plugin from xrlint.result import ( - get_rules_meta_for_results, - Result, Message, - Suggestion, + Result, ResultStats, + Suggestion, + get_rules_meta_for_results, ) -from xrlint.rule import RuleOp, RuleMeta +from xrlint.rule import RuleMeta, RuleOp class ResultTest(TestCase): - # noinspection PyUnusedLocal def test_get_rules_meta_for_results(self): - plugin = new_plugin(name="test") @plugin.define_rule("my-rule-1") @@ -85,7 +83,6 @@ def test_repr_html(self): class SuggestionTest(TestCase): - # noinspection PyUnusedLocal def test_from_value(self): self.assertEqual( diff --git a/tests/test_rule.py b/tests/test_rule.py index b1d402a..fc20fee 100644 --- a/tests/test_rule.py +++ b/tests/test_rule.py @@ -3,10 +3,7 @@ import pytest -from xrlint.rule import Rule, define_rule -from xrlint.rule import RuleConfig -from xrlint.rule import RuleMeta -from xrlint.rule import RuleOp +from xrlint.rule import Rule, RuleConfig, RuleMeta, RuleOp, define_rule class MyRule1(RuleOp): @@ -109,7 +106,6 @@ def test_to_json(self): class DefineRuleTest(unittest.TestCase): - def test_decorator(self): deco = define_rule() self.assertTrue(callable(deco)) diff --git a/tests/test_testing.py b/tests/test_testing.py index d475a5d..6d1d7ab 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -3,12 +3,9 @@ import pytest import xarray as xr - -from xrlint.testing import RuleTest -from xrlint.testing import RuleTester from xrlint.node import DatasetNode -from xrlint.rule import RuleContext -from xrlint.rule import RuleOp +from xrlint.rule import RuleContext, RuleOp +from xrlint.testing import RuleTest, RuleTester class ForceTitle(RuleOp): diff --git a/tests/util/test_codec.py b/tests/util/test_codec.py index 49ab08f..a0cdec1 100644 --- a/tests/util/test_codec.py +++ b/tests/util/test_codec.py @@ -1,23 +1,23 @@ from dataclasses import dataclass, field from types import NoneType, UnionType from typing import ( + TYPE_CHECKING, Any, - Union, + Literal, + Mapping, Optional, TypeAlias, + Union, get_args, get_origin, - Mapping, - TYPE_CHECKING, - Literal, ) from unittest import TestCase import pytest from xrlint.util.constructible import ( - ValueConstructible, MappingConstructible, + ValueConstructible, get_class_parameters, ) from xrlint.util.serializable import JsonSerializable @@ -70,8 +70,8 @@ class UnionTypesContainer(MappingConstructible, JsonSerializable): if TYPE_CHECKING: # make IDEs and flake8 happy - from xrlint.rule import RuleConfig from xrlint.plugin import Plugin + from xrlint.rule import RuleConfig @dataclass() @@ -81,8 +81,8 @@ class UnresolvedTypesContainer(ComplexTypesContainer, SimpleTypesContainer): @classmethod def forward_refs(cls) -> Optional[Mapping[str, type]]: - from xrlint.rule import RuleConfig from xrlint.plugin import Plugin + from xrlint.rule import RuleConfig return { "RuleConfig": RuleConfig, @@ -323,7 +323,6 @@ def test_no_types_ok(self): class MappingConstructibleTest(TestCase): - def test_simple_ok(self): kwargs = dict(a="?", b=True, c=12, d=34.56, e="uvw", f=bytes, g="on") container = SimpleTypesContainer(**kwargs) @@ -387,7 +386,6 @@ def test_union_ok(self): # noinspection PyMethodMayBeStatic def test_simple_fail(self): - with pytest.raises( TypeError, match=( @@ -488,7 +486,6 @@ def test_get_class_parameters_is_cached(self): class GetClassParametersTest(TestCase): - def test_resolves_types(self): ctc_params = get_class_parameters( UnresolvedTypesContainer, diff --git a/tests/util/test_formatting.py b/tests/util/test_formatting.py index 9afe194..44d6fcb 100644 --- a/tests/util/test_formatting.py +++ b/tests/util/test_formatting.py @@ -1,10 +1,12 @@ from unittest import TestCase -from xrlint.util.formatting import format_case -from xrlint.util.formatting import format_count -from xrlint.util.formatting import format_problems -from xrlint.util.formatting import format_seq -from xrlint.util.formatting import format_styled +from xrlint.util.formatting import ( + format_case, + format_count, + format_problems, + format_seq, + format_styled, +) class FormattingTest(TestCase): diff --git a/tests/util/test_importutil.py b/tests/util/test_importutil.py index beb3323..e5894e5 100644 --- a/tests/util/test_importutil.py +++ b/tests/util/test_importutil.py @@ -1,8 +1,7 @@ from unittest import TestCase from xrlint.plugin import Plugin -from xrlint.util.importutil import import_submodules -from xrlint.util.importutil import import_value +from xrlint.util.importutil import import_submodules, import_value class ImportSubmodulesTest(TestCase): diff --git a/tests/util/test_merge.py b/tests/util/test_merge.py index 4af365d..53ca8cc 100644 --- a/tests/util/test_merge.py +++ b/tests/util/test_merge.py @@ -1,9 +1,6 @@ from unittest import TestCase -from xrlint.util.merge import merge_arrays -from xrlint.util.merge import merge_dicts -from xrlint.util.merge import merge_set_lists -from xrlint.util.merge import merge_values +from xrlint.util.merge import merge_arrays, merge_dicts, merge_set_lists, merge_values class NamingTest(TestCase): diff --git a/tests/util/test_naming.py b/tests/util/test_naming.py index e22cd92..cfcd6b3 100644 --- a/tests/util/test_naming.py +++ b/tests/util/test_naming.py @@ -1,7 +1,6 @@ from unittest import TestCase -from xrlint.util.naming import to_kebab_case -from xrlint.util.naming import to_snake_case +from xrlint.util.naming import to_kebab_case, to_snake_case class NamingTest(TestCase): diff --git a/xrlint/_linter/apply.py b/xrlint/_linter/apply.py index 4fa743c..7c0993c 100644 --- a/xrlint/_linter/apply.py +++ b/xrlint/_linter/apply.py @@ -1,10 +1,6 @@ -from xrlint.node import AttrNode -from xrlint.node import AttrsNode -from xrlint.node import DataArrayNode -from xrlint.node import DatasetNode -from xrlint.rule import RuleConfig -from xrlint.rule import RuleExit -from xrlint.rule import RuleOp +from xrlint.node import AttrNode, AttrsNode, DataArrayNode, DatasetNode +from xrlint.rule import RuleConfig, RuleExit, RuleOp + from .rulectx import RuleContextImpl diff --git a/xrlint/_linter/rulectx.py b/xrlint/_linter/rulectx.py index e93c807..d86e8e2 100644 --- a/xrlint/_linter/rulectx.py +++ b/xrlint/_linter/rulectx.py @@ -6,8 +6,7 @@ from xrlint.config import Config from xrlint.constants import SEVERITY_ERROR from xrlint.node import Node -from xrlint.result import Message -from xrlint.result import Suggestion +from xrlint.result import Message, Suggestion from xrlint.rule import RuleContext diff --git a/xrlint/_linter/verify.py b/xrlint/_linter/verify.py index 1239359..fa95148 100644 --- a/xrlint/_linter/verify.py +++ b/xrlint/_linter/verify.py @@ -4,12 +4,12 @@ import xarray as xr -from xrlint.constants import MISSING_DATASET_FILE_PATH from xrlint.config import Config -from xrlint.result import Message -from xrlint.result import Result -from .rulectx import RuleContextImpl +from xrlint.constants import MISSING_DATASET_FILE_PATH +from xrlint.result import Message, Result + from .apply import apply_rule +from .rulectx import RuleContextImpl def verify_dataset(config: Config, dataset: Any, file_path: str | None): diff --git a/xrlint/all.py b/xrlint/all.py index d170ab4..9b96110 100644 --- a/xrlint/all.py +++ b/xrlint/all.py @@ -1,39 +1,33 @@ from xrlint.cli.engine import XRLint -from xrlint.config import Config -from xrlint.config import ConfigList -from xrlint.formatter import Formatter -from xrlint.formatter import FormatterMeta -from xrlint.formatter import FormatterContext -from xrlint.formatter import FormatterOp -from xrlint.formatter import FormatterRegistry -from xrlint.linter import Linter -from xrlint.linter import new_linter -from xrlint.result import Message -from xrlint.result import Suggestion -from xrlint.result import EditInfo -from xrlint.result import Result -from xrlint.result import get_rules_meta_for_results -from xrlint.node import AttrsNode -from xrlint.node import AttrNode -from xrlint.node import DatasetNode -from xrlint.node import DataArrayNode -from xrlint.node import Node -from xrlint.plugin import Plugin -from xrlint.plugin import PluginMeta -from xrlint.plugin import new_plugin -from xrlint.processor import Processor -from xrlint.processor import ProcessorMeta -from xrlint.processor import ProcessorOp -from xrlint.processor import define_processor -from xrlint.rule import Rule -from xrlint.rule import RuleConfig -from xrlint.rule import RuleContext -from xrlint.rule import RuleExit -from xrlint.rule import RuleMeta -from xrlint.rule import RuleOp -from xrlint.rule import define_rule -from xrlint.testing import RuleTest -from xrlint.testing import RuleTester +from xrlint.config import Config, ConfigList +from xrlint.formatter import ( + Formatter, + FormatterContext, + FormatterMeta, + FormatterOp, + FormatterRegistry, +) +from xrlint.linter import Linter, new_linter +from xrlint.node import AttrNode, AttrsNode, DataArrayNode, DatasetNode, Node +from xrlint.plugin import Plugin, PluginMeta, new_plugin +from xrlint.processor import Processor, ProcessorMeta, ProcessorOp, define_processor +from xrlint.result import ( + EditInfo, + Message, + Result, + Suggestion, + get_rules_meta_for_results, +) +from xrlint.rule import ( + Rule, + RuleConfig, + RuleContext, + RuleExit, + RuleMeta, + RuleOp, + define_rule, +) +from xrlint.testing import RuleTest, RuleTester from xrlint.version import version __all__ = [ diff --git a/xrlint/cli/config.py b/xrlint/cli/config.py index 95dd6b0..25c2aaf 100644 --- a/xrlint/cli/config.py +++ b/xrlint/cli/config.py @@ -7,7 +7,7 @@ from xrlint.config import ConfigList from xrlint.util.formatting import format_message_type_of -from xrlint.util.importutil import import_value, ValueImportError +from xrlint.util.importutil import ValueImportError, import_value def read_config_list(config_path: str | Path | PathLike[str]) -> ConfigList: diff --git a/xrlint/cli/engine.py b/xrlint/cli/engine.py index 26e2a14..71d44bd 100644 --- a/xrlint/cli/engine.py +++ b/xrlint/cli/engine.py @@ -1,32 +1,29 @@ -from collections.abc import Iterable, Iterator import json import os +from collections.abc import Iterable, Iterator import click import fsspec import yaml -from xrlint.cli.config import read_config_list, ConfigError -from xrlint.cli.constants import DEFAULT_CONFIG_FILES -from xrlint.cli.constants import DEFAULT_CONFIG_FILE_YAML -from xrlint.cli.constants import DEFAULT_GLOBAL_FILES -from xrlint.cli.constants import DEFAULT_GLOBAL_IGNORES -from xrlint.cli.constants import DEFAULT_OUTPUT_FORMAT -from xrlint.cli.constants import DEFAULT_MAX_WARNINGS -from xrlint.cli.constants import INIT_CONFIG_YAML -from xrlint.config import Config -from xrlint.config import ConfigList -from xrlint.config import get_core_config +from xrlint.cli.config import ConfigError, read_config_list +from xrlint.cli.constants import ( + DEFAULT_CONFIG_FILE_YAML, + DEFAULT_CONFIG_FILES, + DEFAULT_GLOBAL_FILES, + DEFAULT_GLOBAL_IGNORES, + DEFAULT_MAX_WARNINGS, + DEFAULT_OUTPUT_FORMAT, + INIT_CONFIG_YAML, +) +from xrlint.config import Config, ConfigList, get_core_config from xrlint.formatter import FormatterContext from xrlint.formatters import export_formatters from xrlint.linter import Linter from xrlint.plugin import Plugin -from xrlint.result import Message -from xrlint.result import Result -from xrlint.result import ResultStats +from xrlint.result import Message, Result, ResultStats from xrlint.util.filefilter import FileFilter - DEFAULT_GLOBAL_FILTER = FileFilter.from_patterns( DEFAULT_GLOBAL_FILES, DEFAULT_GLOBAL_IGNORES ) diff --git a/xrlint/cli/main.py b/xrlint/cli/main.py index d5bc730..7e908f8 100644 --- a/xrlint/cli/main.py +++ b/xrlint/cli/main.py @@ -2,15 +2,14 @@ import click - # Warning: do not import heavy stuff here, it can # slow down commands like "xrlint --help" otherwise. -from xrlint.version import version from xrlint.cli.constants import ( + DEFAULT_CONFIG_BASENAME, DEFAULT_MAX_WARNINGS, DEFAULT_OUTPUT_FORMAT, - DEFAULT_CONFIG_BASENAME, ) +from xrlint.version import version @click.command(name="xrlint") diff --git a/xrlint/config.py b/xrlint/config.py index c23c934..6d26d72 100644 --- a/xrlint/config.py +++ b/xrlint/config.py @@ -1,28 +1,18 @@ from dataclasses import dataclass, field from functools import cached_property -from typing import Any, TYPE_CHECKING, Union, Literal, Sequence +from typing import TYPE_CHECKING, Any, Literal, Sequence, Union from xrlint.constants import CORE_PLUGIN_NAME -from xrlint.util.constructible import ( - MappingConstructible, - ValueConstructible, -) -from xrlint.util.serializable import JsonSerializable, JsonValue +from xrlint.util.constructible import MappingConstructible, ValueConstructible from xrlint.util.filefilter import FileFilter -from xrlint.util.merge import ( - merge_arrays, - merge_set_lists, - merge_dicts, - merge_values, -) - +from xrlint.util.merge import merge_arrays, merge_dicts, merge_set_lists, merge_values +from xrlint.util.serializable import JsonSerializable, JsonValue if TYPE_CHECKING: # pragma: no cover # make IDEs and flake8 happy - from xrlint.rule import Rule - from xrlint.rule import RuleConfig from xrlint.plugin import Plugin from xrlint.processor import ProcessorOp + from xrlint.rule import Rule, RuleConfig def get_core_plugin() -> "Plugin": @@ -33,7 +23,7 @@ def get_core_plugin() -> "Plugin": def get_core_config( - config_name: Literal["all", "recommended"] | None = None + config_name: Literal["all", "recommended"] | None = None, ) -> "Config": """Create a base configuration for the built-in plugins. @@ -204,8 +194,7 @@ def get_processor_op( """Get the processor operation for the given processor identifier `processor_spec`. """ - from xrlint.processor import Processor - from xrlint.processor import ProcessorOp + from xrlint.processor import Processor, ProcessorOp if isinstance(processor_spec, ProcessorOp): return processor_spec @@ -284,10 +273,9 @@ def _from_none(cls, value_name: str) -> "Config": @classmethod def forward_refs(cls) -> dict[str, type]: - from xrlint.processor import ProcessorOp from xrlint.plugin import Plugin - from xrlint.rule import Rule - from xrlint.rule import RuleConfig + from xrlint.processor import ProcessorOp + from xrlint.rule import Rule, RuleConfig return { "ProcessorOp": ProcessorOp, diff --git a/xrlint/formatter.py b/xrlint/formatter.py index eddf6b7..da67ea7 100644 --- a/xrlint/formatter.py +++ b/xrlint/formatter.py @@ -1,11 +1,10 @@ -from abc import abstractmethod, ABC -from collections.abc import Mapping, Iterable +from abc import ABC, abstractmethod +from collections.abc import Iterable, Mapping from dataclasses import dataclass from typing import Any, Callable, Type from xrlint.operation import Operation, OperationMeta -from xrlint.result import Result -from xrlint.result import ResultStats +from xrlint.result import Result, ResultStats class FormatterContext(ABC): @@ -86,7 +85,6 @@ def op_name(cls) -> str: class FormatterRegistry(Mapping[str, Formatter]): - def __init__(self): self._registrations = {} diff --git a/xrlint/formatters/html.py b/xrlint/formatters/html.py index 02110d1..965533a 100644 --- a/xrlint/formatters/html.py +++ b/xrlint/formatters/html.py @@ -1,6 +1,6 @@ from collections.abc import Iterable -from xrlint.formatter import FormatterOp, FormatterContext +from xrlint.formatter import FormatterContext, FormatterOp from xrlint.formatters import registry from xrlint.result import Result, get_rules_meta_for_results from xrlint.util.schema import schema @@ -17,7 +17,6 @@ ), ) class Html(FormatterOp): - def __init__(self, with_meta: bool = False): self.with_meta = with_meta diff --git a/xrlint/formatters/json.py b/xrlint/formatters/json.py index 23aed8a..ecdfc4c 100644 --- a/xrlint/formatters/json.py +++ b/xrlint/formatters/json.py @@ -1,9 +1,9 @@ import json from collections.abc import Iterable -from xrlint.formatter import FormatterOp, FormatterContext -from xrlint.result import Result, get_rules_meta_for_results +from xrlint.formatter import FormatterContext, FormatterOp from xrlint.formatters import registry +from xrlint.result import Result, get_rules_meta_for_results from xrlint.util.schema import schema @@ -19,7 +19,6 @@ ), ) class Json(FormatterOp): - def __init__(self, indent: int = 2, with_meta: bool = False): super().__init__() self.indent = indent diff --git a/xrlint/formatters/simple.py b/xrlint/formatters/simple.py index 7c59461..fba63f4 100644 --- a/xrlint/formatters/simple.py +++ b/xrlint/formatters/simple.py @@ -1,13 +1,12 @@ from collections.abc import Iterable +from tabulate import tabulate + from xrlint.constants import SEVERITY_CODE_TO_NAME -from xrlint.formatter import FormatterOp, FormatterContext +from xrlint.formatter import FormatterContext, FormatterOp from xrlint.formatters import registry from xrlint.result import Result from xrlint.util.formatting import format_problems, format_styled - -from tabulate import tabulate - from xrlint.util.schema import schema SEVERITY_CODE_TO_COLOR = {2: "red", 1: "blue", 0: "green", None: ""} diff --git a/xrlint/linter.py b/xrlint/linter.py index 6f94759..337fadf 100644 --- a/xrlint/linter.py +++ b/xrlint/linter.py @@ -1,9 +1,8 @@ from typing import Any, Literal -from xrlint.config import Config -from xrlint.config import get_core_config -from xrlint.config import merge_configs +from xrlint.config import Config, get_core_config, merge_configs from xrlint.result import Result + from ._linter.verify import verify_dataset diff --git a/xrlint/operation.py b/xrlint/operation.py index 3894fb6..9f1c35f 100644 --- a/xrlint/operation.py +++ b/xrlint/operation.py @@ -1,12 +1,12 @@ from collections.abc import MutableMapping from dataclasses import dataclass -from inspect import isclass, getdoc -from typing import Any, Type, Callable +from inspect import getdoc, isclass +from typing import Any, Callable, Type from xrlint.util.constructible import MappingConstructible -from xrlint.util.serializable import JsonSerializable, JsonValue from xrlint.util.importutil import import_value from xrlint.util.naming import to_kebab_case +from xrlint.util.serializable import JsonSerializable, JsonValue @dataclass(kw_only=True) diff --git a/xrlint/plugin.py b/xrlint/plugin.py index 4b46b48..1900d79 100644 --- a/xrlint/plugin.py +++ b/xrlint/plugin.py @@ -1,12 +1,12 @@ from dataclasses import dataclass, field -from typing import Any, Type, Callable, Literal +from typing import Any, Callable, Literal, Type from xrlint.config import Config from xrlint.processor import Processor, ProcessorOp, define_processor from xrlint.rule import Rule, RuleOp, define_rule from xrlint.util.constructible import MappingConstructible -from xrlint.util.serializable import JsonSerializable, JsonValue from xrlint.util.importutil import import_value +from xrlint.util.serializable import JsonSerializable, JsonValue @dataclass(kw_only=True) diff --git a/xrlint/plugins/core/rules/__init__.py b/xrlint/plugins/core/rules/__init__.py index 934d6cf..ab64c5d 100644 --- a/xrlint/plugins/core/rules/__init__.py +++ b/xrlint/plugins/core/rules/__init__.py @@ -2,7 +2,6 @@ from xrlint.plugin import new_plugin from xrlint.version import version - plugin = new_plugin( name=CORE_PLUGIN_NAME, version=version, diff --git a/xrlint/plugins/core/rules/grid_mappings.py b/xrlint/plugins/core/rules/grid_mappings.py index e83f9bd..0f85c76 100644 --- a/xrlint/plugins/core/rules/grid_mappings.py +++ b/xrlint/plugins/core/rules/grid_mappings.py @@ -1,6 +1,6 @@ from xrlint.node import DatasetNode from xrlint.plugins.core.rules import plugin -from xrlint.rule import RuleOp, RuleContext +from xrlint.rule import RuleContext, RuleOp @plugin.define_rule( diff --git a/xrlint/plugins/core/rules/no_empty_attrs.py b/xrlint/plugins/core/rules/no_empty_attrs.py index 683dc0c..22c3ad3 100644 --- a/xrlint/plugins/core/rules/no_empty_attrs.py +++ b/xrlint/plugins/core/rules/no_empty_attrs.py @@ -1,6 +1,6 @@ -from xrlint.result import Suggestion from xrlint.node import AttrsNode from xrlint.plugins.core.rules import plugin +from xrlint.result import Suggestion from xrlint.rule import RuleContext, RuleOp diff --git a/xrlint/plugins/core/rules/var_units_attr.py b/xrlint/plugins/core/rules/var_units_attr.py index e9ccbeb..cec977f 100644 --- a/xrlint/plugins/core/rules/var_units_attr.py +++ b/xrlint/plugins/core/rules/var_units_attr.py @@ -1,6 +1,6 @@ from xrlint.node import DataArrayNode from xrlint.plugins.core.rules import plugin -from xrlint.rule import RuleOp, RuleContext +from xrlint.rule import RuleContext, RuleOp @plugin.define_rule( diff --git a/xrlint/plugins/xcube/rules/__init__.py b/xrlint/plugins/xcube/rules/__init__.py index 323ea84..1de8247 100644 --- a/xrlint/plugins/xcube/rules/__init__.py +++ b/xrlint/plugins/xcube/rules/__init__.py @@ -1,7 +1,6 @@ from xrlint.plugin import new_plugin from xrlint.version import version - plugin = new_plugin( name="xcube", version=version, diff --git a/xrlint/plugins/xcube/rules/any_spatial_data_var.py b/xrlint/plugins/xcube/rules/any_spatial_data_var.py index 2d52e5b..697d2df 100644 --- a/xrlint/plugins/xcube/rules/any_spatial_data_var.py +++ b/xrlint/plugins/xcube/rules/any_spatial_data_var.py @@ -1,7 +1,7 @@ from xrlint.node import DatasetNode from xrlint.plugins.xcube.rules import plugin from xrlint.plugins.xcube.util import is_spatial_var -from xrlint.rule import RuleOp, RuleContext +from xrlint.rule import RuleContext, RuleOp @plugin.define_rule( diff --git a/xrlint/plugins/xcube/rules/cube_dims_order.py b/xrlint/plugins/xcube/rules/cube_dims_order.py index 0f33300..f4fdc62 100644 --- a/xrlint/plugins/xcube/rules/cube_dims_order.py +++ b/xrlint/plugins/xcube/rules/cube_dims_order.py @@ -1,7 +1,7 @@ from xrlint.node import DataArrayNode -from xrlint.plugins.xcube.constants import LAT_NAME, LON_NAME, X_NAME, Y_NAME, T_NAME +from xrlint.plugins.xcube.constants import LAT_NAME, LON_NAME, T_NAME, X_NAME, Y_NAME from xrlint.plugins.xcube.rules import plugin -from xrlint.rule import RuleOp, RuleContext +from xrlint.rule import RuleContext, RuleOp @plugin.define_rule( diff --git a/xrlint/plugins/xcube/rules/data_var_colors.py b/xrlint/plugins/xcube/rules/data_var_colors.py index 5de1c75..39225d2 100644 --- a/xrlint/plugins/xcube/rules/data_var_colors.py +++ b/xrlint/plugins/xcube/rules/data_var_colors.py @@ -1,8 +1,7 @@ from xrlint.node import DataArrayNode from xrlint.plugins.xcube.rules import plugin from xrlint.plugins.xcube.util import is_spatial_var -from xrlint.rule import RuleContext -from xrlint.rule import RuleOp +from xrlint.rule import RuleContext, RuleOp @plugin.define_rule( @@ -10,8 +9,7 @@ version="1.0.0", type="suggestion", description=( - "Spatial data variables should encode" - " xcube color mappings in their metadata." + "Spatial data variables should encode xcube color mappings in their metadata." ), docs_url=( "https://xcube.readthedocs.io/en/latest/cubespec.html#encoding-of-colors" diff --git a/xrlint/plugins/xcube/rules/grid_mapping_naming.py b/xrlint/plugins/xcube/rules/grid_mapping_naming.py index 80a611e..dc079a3 100644 --- a/xrlint/plugins/xcube/rules/grid_mapping_naming.py +++ b/xrlint/plugins/xcube/rules/grid_mapping_naming.py @@ -1,7 +1,7 @@ from xrlint.node import DatasetNode from xrlint.plugins.xcube.constants import GM_NAMES, GM_NAMES_TEXT from xrlint.plugins.xcube.rules import plugin -from xrlint.rule import RuleOp, RuleContext +from xrlint.rule import RuleContext, RuleOp @plugin.define_rule( diff --git a/xrlint/plugins/xcube/rules/increasing_time.py b/xrlint/plugins/xcube/rules/increasing_time.py index 0280d31..331fb05 100644 --- a/xrlint/plugins/xcube/rules/increasing_time.py +++ b/xrlint/plugins/xcube/rules/increasing_time.py @@ -2,11 +2,8 @@ from xrlint.node import DataArrayNode from xrlint.plugins.xcube.rules import plugin -from xrlint.rule import RuleContext -from xrlint.rule import RuleExit -from xrlint.rule import RuleOp -from xrlint.util.formatting import format_count -from xrlint.util.formatting import format_seq +from xrlint.rule import RuleContext, RuleExit, RuleOp +from xrlint.util.formatting import format_count, format_seq @plugin.define_rule( diff --git a/xrlint/plugins/xcube/rules/lat_lon_naming.py b/xrlint/plugins/xcube/rules/lat_lon_naming.py index add58de..9ae2c98 100644 --- a/xrlint/plugins/xcube/rules/lat_lon_naming.py +++ b/xrlint/plugins/xcube/rules/lat_lon_naming.py @@ -1,7 +1,7 @@ from xrlint.node import DatasetNode from xrlint.plugins.xcube.constants import LAT_NAME, LON_NAME from xrlint.plugins.xcube.rules import plugin -from xrlint.rule import RuleOp, RuleContext +from xrlint.rule import RuleContext, RuleOp INVALID_LAT_NAMES = {"ltd", "latitude"} INVALID_LON_NAMES = {"lng", "long", "longitude"} @@ -13,7 +13,7 @@ type="problem", description=( f"Latitude and longitude coordinates and dimensions" - f" should be called {LAT_NAME !r} and {LON_NAME !r}." + f" should be called {LAT_NAME!r} and {LON_NAME!r}." ), docs_url="https://xcube.readthedocs.io/en/latest/cubespec.html#spatial-reference", ) diff --git a/xrlint/plugins/xcube/rules/single_grid_mapping.py b/xrlint/plugins/xcube/rules/single_grid_mapping.py index ccef80d..0e75e5c 100644 --- a/xrlint/plugins/xcube/rules/single_grid_mapping.py +++ b/xrlint/plugins/xcube/rules/single_grid_mapping.py @@ -1,8 +1,8 @@ from xrlint.node import DatasetNode -from xrlint.plugins.xcube.constants import LAT_NAME, LON_NAME, GM_NAMES_TEXT +from xrlint.plugins.xcube.constants import GM_NAMES_TEXT, LAT_NAME, LON_NAME from xrlint.plugins.xcube.rules import plugin from xrlint.plugins.xcube.util import is_spatial_var -from xrlint.rule import RuleOp, RuleContext +from xrlint.rule import RuleContext, RuleOp @plugin.define_rule( diff --git a/xrlint/processor.py b/xrlint/processor.py index dea1633..f45f554 100644 --- a/xrlint/processor.py +++ b/xrlint/processor.py @@ -1,10 +1,10 @@ -from abc import abstractmethod, ABC +from abc import ABC, abstractmethod from dataclasses import dataclass -from typing import Type, Any, Callable +from typing import Any, Callable, Type import xarray as xr -from xrlint.operation import OperationMeta, Operation +from xrlint.operation import Operation, OperationMeta from xrlint.result import Message diff --git a/xrlint/result.py b/xrlint/result.py index 1309c05..943fa5a 100644 --- a/xrlint/result.py +++ b/xrlint/result.py @@ -1,16 +1,18 @@ +import html from collections.abc import Iterable from dataclasses import dataclass, field -from typing import Literal, TYPE_CHECKING, Any, Union -import html +from typing import TYPE_CHECKING, Any, Literal, Union from tabulate import tabulate -from xrlint.constants import SEVERITY_CODE_TO_NAME, MISSING_DATASET_FILE_PATH -from xrlint.constants import SEVERITY_ERROR -from xrlint.constants import SEVERITY_WARN +from xrlint.constants import ( + MISSING_DATASET_FILE_PATH, + SEVERITY_CODE_TO_NAME, + SEVERITY_ERROR, + SEVERITY_WARN, +) +from xrlint.util.formatting import format_message_type_of, format_problems from xrlint.util.serializable import JsonSerializable -from xrlint.util.formatting import format_problems -from xrlint.util.formatting import format_message_type_of if TYPE_CHECKING: # pragma: no cover from xrlint.config import Config diff --git a/xrlint/rule.py b/xrlint/rule.py index 4258154..56894ef 100644 --- a/xrlint/rule.py +++ b/xrlint/rule.py @@ -1,22 +1,17 @@ -from abc import abstractmethod, ABC +from abc import ABC, abstractmethod from collections.abc import MutableMapping, Sequence from dataclasses import dataclass, field -from typing import Type, Literal, Any, Callable +from typing import Any, Callable, Literal, Type import xarray as xr -from xrlint.constants import ( - SEVERITY_ENUM, - SEVERITY_ENUM_TEXT, -) -from xrlint.node import DatasetNode, DataArrayNode, AttrsNode, AttrNode -from xrlint.operation import OperationMeta, Operation +from xrlint.constants import SEVERITY_ENUM, SEVERITY_ENUM_TEXT +from xrlint.node import AttrNode, AttrsNode, DataArrayNode, DatasetNode +from xrlint.operation import Operation, OperationMeta from xrlint.result import Suggestion -from xrlint.util.constructible import ( - ValueConstructible, -) -from xrlint.util.serializable import JsonSerializable +from xrlint.util.constructible import ValueConstructible from xrlint.util.formatting import format_message_one_of +from xrlint.util.serializable import JsonSerializable class RuleContext(ABC): diff --git a/xrlint/testing.py b/xrlint/testing.py index 40243e1..f8fec2e 100644 --- a/xrlint/testing.py +++ b/xrlint/testing.py @@ -7,13 +7,10 @@ from xrlint.constants import SEVERITY_ERROR from xrlint.linter import Linter from xrlint.plugin import new_plugin -from xrlint.result import Message -from xrlint.result import Result -from xrlint.rule import Rule -from xrlint.rule import RuleMeta -from xrlint.rule import RuleOp -from xrlint.util.naming import to_snake_case +from xrlint.result import Message, Result +from xrlint.rule import Rule, RuleMeta, RuleOp from xrlint.util.formatting import format_problems +from xrlint.util.naming import to_snake_case _PLUGIN_NAME: Final = "testing" diff --git a/xrlint/util/constructible.py b/xrlint/util/constructible.py index 1955d24..37e33d9 100644 --- a/xrlint/util/constructible.py +++ b/xrlint/util/constructible.py @@ -1,22 +1,22 @@ import sys from collections.abc import Mapping, Sequence from functools import lru_cache -from inspect import formatannotation, isclass, signature, Parameter +from inspect import Parameter, formatannotation, isclass, signature from types import NoneType, UnionType from typing import ( Any, Generic, - TypeVar, + Literal, + Optional, Type, + TypeVar, Union, - get_origin, get_args, + get_origin, get_type_hints, - Optional, - Literal, ) -from xrlint.util.formatting import format_message_type_of, format_message_one_of +from xrlint.util.formatting import format_message_one_of, format_message_type_of T = TypeVar("T") diff --git a/xrlint/util/filepattern.py b/xrlint/util/filepattern.py index 0e4202a..6cbe608 100644 --- a/xrlint/util/filepattern.py +++ b/xrlint/util/filepattern.py @@ -1,7 +1,7 @@ +import platform import re from functools import cached_property from typing import Literal -import platform _WIN_OS = platform.system() == "Windows" diff --git a/xrlint/util/formatting.py b/xrlint/util/formatting.py index ff60d8d..c51c64c 100644 --- a/xrlint/util/formatting.py +++ b/xrlint/util/formatting.py @@ -1,5 +1,4 @@ from collections.abc import Sequence - from typing import Any diff --git a/xrlint/util/importutil.py b/xrlint/util/importutil.py index ee4e847..7430ab0 100644 --- a/xrlint/util/importutil.py +++ b/xrlint/util/importutil.py @@ -1,12 +1,11 @@ import importlib import pathlib -from typing import TypeVar, Callable, Any, Type +from typing import Any, Callable, Type, TypeVar from xrlint.util.formatting import format_message_type_of def import_submodules(package_name: str, dry_run: bool = False) -> list[str]: - package = importlib.import_module(package_name) if not hasattr(package, "__path__"): return [] @@ -95,7 +94,7 @@ def import_value( attr_value = getattr(attr_value, attr_name) except AttributeError: raise ValueImportError( - f"attribute {'.'.join(attr_names[:i+1])!r}" + f"attribute {'.'.join(attr_names[: i + 1])!r}" f" not found in module {module_name!r}" ) diff --git a/xrlint/util/schema.py b/xrlint/util/schema.py index 6025dba..872fd1c 100644 --- a/xrlint/util/schema.py +++ b/xrlint/util/schema.py @@ -1,9 +1,6 @@ from typing import Any, Literal -from .formatting import ( - format_message_type_of, - format_message_one_of, -) +from .formatting import format_message_one_of, format_message_type_of TYPE_NAMES = ( "null", diff --git a/xrlint/util/serializable.py b/xrlint/util/serializable.py index ef26ee7..b8b7415 100644 --- a/xrlint/util/serializable.py +++ b/xrlint/util/serializable.py @@ -2,7 +2,6 @@ from xrlint.util.formatting import format_message_type_of - JSON_VALUE_TYPE_NAME: Final = "None | bool | int | float | str | dict | list" JsonValue: TypeAlias = (