From df233260cc1a90cc6e3b23f1d0af6e8862988b01 Mon Sep 17 00:00:00 2001 From: clarasb Date: Thu, 3 Sep 2026 15:26:43 +0200 Subject: [PATCH 01/11] ruff check --fix --- docs/config.md | 12 +++----- docs/mkruleref.py | 2 +- examples/rule_testing.py | 1 - tests/cli/test_config.py | 20 +++++++------ tests/plugins/acdd/rules/test_attributes.py | 2 +- tests/plugins/acdd/rules/test_conventions.py | 2 +- tests/plugins/acdd/rules/test_id_blanks.py | 2 +- tests/plugins/acdd/rules/test_iso_dates.py | 2 +- .../plugins/acdd/rules/test_metadata_link.py | 2 +- tests/plugins/xcube/test_util.py | 3 +- tests/test_operation.py | 9 +++--- tests/util/test_attrs.py | 2 +- tests/util/test_constructible.py | 11 ++++---- tests/util/test_serializable.py | 2 +- xrlint/_linter/rulectx.py | 2 +- xrlint/all.py | 28 +++++++++---------- xrlint/cli/constants.py | 1 - xrlint/cli/main.py | 2 +- xrlint/formatter.py | 12 ++++---- xrlint/node.py | 3 +- xrlint/operation.py | 18 ++++++------ xrlint/plugin.py | 9 +++--- xrlint/plugins/acdd/rules/attributes.py | 2 +- xrlint/plugins/acdd/rules/conventions.py | 2 +- xrlint/plugins/acdd/rules/iso_dates.py | 3 +- xrlint/plugins/acdd/rules/metadata_link.py | 2 +- xrlint/plugins/acdd/rules/no_id_blanks.py | 2 +- xrlint/plugins/core/rules/content_desc.py | 2 +- xrlint/plugins/core/rules/conventions.py | 2 +- xrlint/processor.py | 13 +++++---- xrlint/rule.py | 14 +++++----- xrlint/testing.py | 19 +++++++------ xrlint/util/attrs.py | 2 +- xrlint/util/constructible.py | 6 ++-- xrlint/util/formatting.py | 4 +-- xrlint/util/importutil.py | 5 ++-- xrlint/util/merge.py | 3 +- xrlint/util/schema.py | 8 +++--- xrlint/util/serializable.py | 3 +- 39 files changed, 120 insertions(+), 119 deletions(-) diff --git a/docs/config.md b/docs/config.md index e41fdab..98df3a6 100644 --- a/docs/config.md +++ b/docs/config.md @@ -45,14 +45,10 @@ And as Python module: ```python def export_config(): return [ - {"files": ["**/*.zarr", "**/*.nc"]}, - { - "plugins": { - "xcube": "xrlint.plugins.xcube" - } - }, - "recommended", - "xcube/recommended" + {"files": ["**/*.zarr", "**/*.nc"]}, + {"plugins": {"xcube": "xrlint.plugins.xcube"}}, + "recommended", + "xcube/recommended", ] ``` diff --git a/docs/mkruleref.py b/docs/mkruleref.py index 40bc3db..359e6fc 100644 --- a/docs/mkruleref.py +++ b/docs/mkruleref.py @@ -2,9 +2,9 @@ # This software is distributed under the terms and conditions of the # MIT license (https://mit-license.org/). +from xrlint.config import plugins_from_entry_points from xrlint.plugin import Plugin from xrlint.rule import RuleConfig -from xrlint.config import plugins_from_entry_points # for icons, see # https://squidfunk.github.io/mkdocs-material/reference/icons-emojis/ diff --git a/examples/rule_testing.py b/examples/rule_testing.py index e59bbb2..299981a 100644 --- a/examples/rule_testing.py +++ b/examples/rule_testing.py @@ -12,7 +12,6 @@ from xrlint.rule import RuleContext, RuleOp, define_rule from xrlint.testing import RuleTest, RuleTester - # ---------------------------------------------------- # Place the rule implementation code in its own module # ---------------------------------------------------- diff --git a/tests/cli/test_config.py b/tests/cli/test_config.py index c40dab1..dc17790 100644 --- a/tests/cli/test_config.py +++ b/tests/cli/test_config.py @@ -101,24 +101,28 @@ def test_read_config_invalid_arg(self): read_config(None) def test_read_config_json_with_format_error(self): - with text_file("config.json", "{") as config_path: - with pytest.raises( + with ( + text_file("config.json", "{") as config_path, + pytest.raises( ConfigError, match=( "config.json:" " Expecting property name enclosed in double quotes:" " line 1 column 2 \\(char 1\\)" ), - ): - read_config(config_path) + ), + ): + read_config(config_path) def test_read_config_yaml_with_format_error(self): - with text_file("config.yaml", "}") as config_path: - with pytest.raises( + with ( + text_file("config.yaml", "}") as config_path, + pytest.raises( ConfigError, match="config.yaml: while parsing a block node", - ): - read_config(config_path) + ), + ): + read_config(config_path) def test_read_config_yaml_with_type_error(self): with text_file("config.yaml", "97") as config_path: diff --git a/tests/plugins/acdd/rules/test_attributes.py b/tests/plugins/acdd/rules/test_attributes.py index 4373f37..7b043ff 100644 --- a/tests/plugins/acdd/rules/test_attributes.py +++ b/tests/plugins/acdd/rules/test_attributes.py @@ -1,9 +1,9 @@ import xarray as xr -from xrlint.testing import RuleTest, RuleTester from xrlint.plugins.acdd.rules.attributes import ( Attributes_1_3_Highly_Recommended, ) +from xrlint.testing import RuleTest, RuleTester valid_1_3_highly_rec_dataset = xr.Dataset( attrs={ diff --git a/tests/plugins/acdd/rules/test_conventions.py b/tests/plugins/acdd/rules/test_conventions.py index 7856bc0..a931db8 100644 --- a/tests/plugins/acdd/rules/test_conventions.py +++ b/tests/plugins/acdd/rules/test_conventions.py @@ -1,7 +1,7 @@ import xarray as xr -from xrlint.testing import RuleTest, RuleTester from xrlint.plugins.acdd.rules.conventions import Conventions +from xrlint.testing import RuleTest, RuleTester valid_dataset_0 = xr.Dataset(attrs={"Conventions": "ACDD-1.3"}) diff --git a/tests/plugins/acdd/rules/test_id_blanks.py b/tests/plugins/acdd/rules/test_id_blanks.py index b733a1c..41b1fd3 100644 --- a/tests/plugins/acdd/rules/test_id_blanks.py +++ b/tests/plugins/acdd/rules/test_id_blanks.py @@ -1,7 +1,7 @@ import xarray as xr -from xrlint.testing import RuleTest, RuleTester from xrlint.plugins.acdd.rules.no_id_blanks import NoBlanksInID +from xrlint.testing import RuleTest, RuleTester valid_dataset_0 = xr.Dataset(attrs={"id": "testing_dataset"}) diff --git a/tests/plugins/acdd/rules/test_iso_dates.py b/tests/plugins/acdd/rules/test_iso_dates.py index 577e417..3f8f497 100644 --- a/tests/plugins/acdd/rules/test_iso_dates.py +++ b/tests/plugins/acdd/rules/test_iso_dates.py @@ -1,7 +1,7 @@ import xarray as xr -from xrlint.testing import RuleTest, RuleTester from xrlint.plugins.acdd.rules.iso_dates import IsoDates +from xrlint.testing import RuleTest, RuleTester valid_dataset_0 = xr.Dataset(attrs={"date_created": "2023-10-05T12:34:56Z"}) valid_dataset_1 = xr.Dataset(attrs={"date_modified": "2023-10-05"}) diff --git a/tests/plugins/acdd/rules/test_metadata_link.py b/tests/plugins/acdd/rules/test_metadata_link.py index 16c73ae..5e17fc5 100644 --- a/tests/plugins/acdd/rules/test_metadata_link.py +++ b/tests/plugins/acdd/rules/test_metadata_link.py @@ -1,7 +1,7 @@ import xarray as xr -from xrlint.testing import RuleTest, RuleTester from xrlint.plugins.acdd.rules.metadata_link import MetadataLink +from xrlint.testing import RuleTest, RuleTester valid_dataset_0 = xr.Dataset(attrs={"metadata_link": "http://example.com/metadata"}) valid_dataset_1 = xr.Dataset(attrs={"metadata_link": "https://example.com/metadata"}) diff --git a/tests/plugins/xcube/test_util.py b/tests/plugins/xcube/test_util.py index 2cb4e00..6611765 100644 --- a/tests/plugins/xcube/test_util.py +++ b/tests/plugins/xcube/test_util.py @@ -4,8 +4,7 @@ from unittest import TestCase -from xrlint.plugins.xcube.util import is_absolute_path -from xrlint.plugins.xcube.util import resolve_path +from xrlint.plugins.xcube.util import is_absolute_path, resolve_path class UtilTest(TestCase): diff --git a/tests/test_operation.py b/tests/test_operation.py index 86ef42c..f0a7fc9 100644 --- a/tests/test_operation.py +++ b/tests/test_operation.py @@ -4,7 +4,6 @@ from abc import ABC from dataclasses import dataclass -from typing import Type from unittest import TestCase import pytest @@ -25,14 +24,14 @@ class ThingMeta(OperationMeta): @dataclass(kw_only=True, frozen=True) class Thing(Operation): meta: ThingMeta - op_class: Type[ThingOp] + op_class: type[ThingOp] @classmethod - def meta_class(cls) -> Type: + def meta_class(cls) -> type: return ThingMeta @classmethod - def op_base_class(cls) -> Type[ThingOp]: + def op_base_class(cls) -> type[ThingOp]: return ThingOp @classmethod @@ -40,7 +39,7 @@ def value_name(cls) -> str: return "thing" @classmethod - def define(cls, op_class: Type[ThingOp] | None = None, **kwargs): + def define(cls, op_class: type[ThingOp] | None = None, **kwargs): return cls.define_operation(op_class, **kwargs) diff --git a/tests/util/test_attrs.py b/tests/util/test_attrs.py index 950f251..337c777 100644 --- a/tests/util/test_attrs.py +++ b/tests/util/test_attrs.py @@ -6,7 +6,7 @@ import xarray as xr -from xrlint.node import DataTreeNode, DatasetNode +from xrlint.node import DatasetNode, DataTreeNode from xrlint.util.attrs import hierarchical_attrs diff --git a/tests/util/test_constructible.py b/tests/util/test_constructible.py index 41443e9..747b934 100644 --- a/tests/util/test_constructible.py +++ b/tests/util/test_constructible.py @@ -2,14 +2,13 @@ # This software is distributed under the terms and conditions of the # MIT license (https://mit-license.org/). +from collections.abc import Mapping from dataclasses import dataclass, field from types import NoneType, UnionType from typing import ( TYPE_CHECKING, Any, Literal, - Mapping, - Optional, TypeAlias, Union, get_args, @@ -84,7 +83,7 @@ class UnresolvedTypesContainer(ComplexTypesContainer, SimpleTypesContainer): plugins: dict[str, "Plugin"] = field(default_factory=dict) @classmethod - def forward_refs(cls) -> Optional[Mapping[str, type]]: + def forward_refs(cls) -> Mapping[str, type] | None: from xrlint.plugin import Plugin from xrlint.rule import RuleConfig @@ -94,9 +93,9 @@ def forward_refs(cls) -> Optional[Mapping[str, type]]: } -T1: TypeAlias = int | str | Union[bool, None] | None -T2: TypeAlias = Optional[int] -T3: TypeAlias = Optional[Any] +T1: TypeAlias = int | str | bool | None +T2: TypeAlias = int | None +T3: TypeAlias = Any | None class TypingTest(TestCase): diff --git a/tests/util/test_serializable.py b/tests/util/test_serializable.py index ec950b4..38c1f5f 100644 --- a/tests/util/test_serializable.py +++ b/tests/util/test_serializable.py @@ -39,7 +39,7 @@ def __init__( r: dict[str, PlainSimpleTypesContainer] = None, s: list[int] = None, t: list[PlainSimpleTypesContainer] = None, - u: int | float | None = None, + u: float | None = None, ): self.p = p self.q = q or {} diff --git a/xrlint/_linter/rulectx.py b/xrlint/_linter/rulectx.py index 87c0bb6..b62018e 100644 --- a/xrlint/_linter/rulectx.py +++ b/xrlint/_linter/rulectx.py @@ -101,7 +101,7 @@ def report( @contextlib.contextmanager def use_state(self, **new_state): - old_state = {k: getattr(self, k) for k in new_state.keys()} + old_state = {k: getattr(self, k) for k in new_state} try: for k, v in new_state.items(): setattr(self, k, v) diff --git a/xrlint/all.py b/xrlint/all.py index 4009c16..fcba6cc 100644 --- a/xrlint/all.py +++ b/xrlint/all.py @@ -35,43 +35,43 @@ from xrlint.version import version __all__ = [ - "XRLint", + "AttrNode", + "AttrsNode", "Config", "ConfigLike", "ConfigObject", "ConfigObjectLike", - "Linter", - "new_linter", + "DatasetNode", "EditInfo", - "Message", - "Result", - "Suggestion", - "get_rules_meta_for_results", "Formatter", "FormatterContext", "FormatterMeta", "FormatterOp", "FormatterRegistry", - "AttrNode", - "AttrsNode", - "VariableNode", - "DatasetNode", + "Linter", + "Message", "Node", "Plugin", "PluginMeta", - "new_plugin", "Processor", "ProcessorMeta", "ProcessorOp", - "define_processor", + "Result", "Rule", "RuleConfig", "RuleContext", "RuleExit", "RuleMeta", "RuleOp", - "define_rule", "RuleTest", "RuleTester", + "Suggestion", + "VariableNode", + "XRLint", + "define_processor", + "define_rule", + "get_rules_meta_for_results", + "new_linter", + "new_plugin", "version", ] diff --git a/xrlint/cli/constants.py b/xrlint/cli/constants.py index a8ccba6..8ed38b0 100644 --- a/xrlint/cli/constants.py +++ b/xrlint/cli/constants.py @@ -4,7 +4,6 @@ from typing import Final - _MODULE_BASENAME: Final = "xrlint_config" _REGULAR_BASENAME: Final = "xrlint-config" diff --git a/xrlint/cli/main.py b/xrlint/cli/main.py index 7a381ca..daf476f 100644 --- a/xrlint/cli/main.py +++ b/xrlint/cli/main.py @@ -9,9 +9,9 @@ # Warning: do not import heavy stuff here, it can # slow down commands like "xrlint --help" otherwise. from xrlint.cli.constants import ( + DEFAULT_CONFIG_FILE_YAML, DEFAULT_MAX_WARNINGS, DEFAULT_OUTPUT_FORMAT, - DEFAULT_CONFIG_FILE_YAML, ) from xrlint.version import version diff --git a/xrlint/formatter.py b/xrlint/formatter.py index 1519bd3..fa547cc 100644 --- a/xrlint/formatter.py +++ b/xrlint/formatter.py @@ -3,9 +3,9 @@ # MIT license (https://mit-license.org/). from abc import ABC, abstractmethod -from collections.abc import Iterable, Mapping +from collections.abc import Callable, Iterable, Mapping from dataclasses import dataclass -from typing import Any, Callable, Type +from typing import Any from xrlint.operation import Operation, OperationMeta from xrlint.result import Result, ResultStats @@ -72,15 +72,15 @@ class Formatter(Operation): meta: FormatterMeta """The formatter metadata.""" - op_class: Type[FormatterOp] + op_class: type[FormatterOp] """The class that implements the format operation.""" @classmethod - def meta_class(cls) -> Type: + def meta_class(cls) -> type: return FormatterMeta @classmethod - def op_base_class(cls) -> Type: + def op_base_class(cls) -> type: return FormatterOp @classmethod @@ -97,7 +97,7 @@ def define_formatter( name: str | None = None, version: str | None = None, schema: dict[str, Any] | list[dict[str, Any]] | bool | None = None, - ) -> Callable[[FormatterOp], Type[FormatterOp]] | Formatter: + ) -> Callable[[FormatterOp], type[FormatterOp]] | Formatter: """Decorator function.""" return Formatter.define_operation( None, diff --git a/xrlint/node.py b/xrlint/node.py index 9b190a4..4774a5d 100644 --- a/xrlint/node.py +++ b/xrlint/node.py @@ -3,8 +3,9 @@ # MIT license (https://mit-license.org/). from abc import ABC +from collections.abc import Hashable from dataclasses import dataclass -from typing import Any, Hashable, Union +from typing import Any, Union import xarray as xr diff --git a/xrlint/operation.py b/xrlint/operation.py index b87c004..9561f0f 100644 --- a/xrlint/operation.py +++ b/xrlint/operation.py @@ -2,10 +2,10 @@ # This software is distributed under the terms and conditions of the # MIT license (https://mit-license.org/). -from collections.abc import MutableMapping +from collections.abc import Callable, MutableMapping from dataclasses import dataclass from inspect import getdoc, isclass -from typing import Any, Callable, Type +from typing import Any from xrlint.util.constructible import MappingConstructible from xrlint.util.importutil import import_value @@ -82,7 +82,7 @@ def to_json(self, value_name: str | None = None) -> str: return super().to_json(value_name=value_name) @classmethod - def _from_class(cls, value: Type, value_name: str) -> "Operation": + def _from_class(cls, value: type, value_name: str) -> "Operation": # noinspection PyTypeChecker if issubclass(value, cls.op_base_class()): op_class = value @@ -121,14 +121,14 @@ def op_import_attr_name(cls) -> str: return f"export_{cls.value_name()}" @classmethod - def meta_class(cls) -> Type: + def meta_class(cls) -> type: """Get the class of the instances of the `meta` field. Defaults to [OperationMeta][xrlint.operation.OperationMeta]. """ return OperationMeta @classmethod - def op_base_class(cls) -> Type: + def op_base_class(cls) -> type: """Get the base class from which all instances of the `op_class` must derive from. """ @@ -148,16 +148,16 @@ def value_type_name(cls) -> str: @classmethod def define_operation( cls, - op_class: Type | None, + op_class: type | None, *, registry: MutableMapping[str, "Operation"] | None = None, meta_kwargs: dict[str, Any] | None = None, **kwargs, - ) -> Callable[[Type], Type] | "Operation": + ) -> Callable[[type], type] | "Operation": """Defines an operation.""" meta_kwargs = meta_kwargs or {} - def _define_op(_op_class: Type, decorated=True) -> Type | "Operation": + def _define_op(_op_class: type, decorated=True) -> type | "Operation": cls._assert_op_class_ok( f"decorated {cls.value_name()} component", _op_class ) @@ -206,7 +206,7 @@ def _define_op(_op_class: Type, decorated=True) -> Type | "Operation": return _define_op @classmethod - def _assert_op_class_ok(cls, value_name: str, op_class: Type): + def _assert_op_class_ok(cls, value_name: str, op_class: type): if not isclass(op_class): raise TypeError( f"{value_name} must be a class, but got {type(op_class).__name__}" diff --git a/xrlint/plugin.py b/xrlint/plugin.py index d5b6d11..915d7ec 100644 --- a/xrlint/plugin.py +++ b/xrlint/plugin.py @@ -2,8 +2,9 @@ # This software is distributed under the terms and conditions of the # MIT license (https://mit-license.org/). +from collections.abc import Callable from dataclasses import dataclass, field -from typing import Any, Callable, Literal, Type +from typing import Any, Literal from xrlint.config import Config, ConfigLike, ConfigObject from xrlint.processor import Processor, ProcessorOp, define_processor @@ -72,8 +73,8 @@ def define_rule( type: Literal["problem", "suggestion", "layout"] = "problem", description: str | None = None, docs_url: str | None = None, - op_class: Type[RuleOp] | None = None, - ) -> Callable[[Any], Type[RuleOp]] | None: + op_class: type[RuleOp] | None = None, + ) -> Callable[[Any], type[RuleOp]] | None: """Decorator to define a plugin rule. The method registers a new rule with the plugin. @@ -95,7 +96,7 @@ def define_processor( self, name: str | None = None, version: str = "0.0.0", - op_class: Type[ProcessorOp] | None = None, + op_class: type[ProcessorOp] | None = None, ): """Decorator to define a plugin processor. The method registers a new processor with the plugin. diff --git a/xrlint/plugins/acdd/rules/attributes.py b/xrlint/plugins/acdd/rules/attributes.py index be265fb..7fab2bb 100644 --- a/xrlint/plugins/acdd/rules/attributes.py +++ b/xrlint/plugins/acdd/rules/attributes.py @@ -1,8 +1,8 @@ from typing import Literal from xrlint.node import DatasetNode -from xrlint.rule import RuleContext, RuleOp from xrlint.plugins.acdd.plugin import plugin +from xrlint.rule import RuleContext, RuleOp attrs_1_0_high_rec = { "title": "A short phrase or sentence describing the dataset. In many discovery systems, the title will be displayed in the results list from a search, and therefore should be human readable and reasonable to display in a list of such names. This attribute is also recommended by the NetCDF Users Guide and the CF conventions.", diff --git a/xrlint/plugins/acdd/rules/conventions.py b/xrlint/plugins/acdd/rules/conventions.py index 662339d..52807a2 100644 --- a/xrlint/plugins/acdd/rules/conventions.py +++ b/xrlint/plugins/acdd/rules/conventions.py @@ -1,6 +1,6 @@ from xrlint.node import DatasetNode -from xrlint.rule import RuleContext, RuleOp from xrlint.plugins.acdd.plugin import plugin +from xrlint.rule import RuleContext, RuleOp @plugin.define_rule( diff --git a/xrlint/plugins/acdd/rules/iso_dates.py b/xrlint/plugins/acdd/rules/iso_dates.py index 50e0913..c4a6be9 100644 --- a/xrlint/plugins/acdd/rules/iso_dates.py +++ b/xrlint/plugins/acdd/rules/iso_dates.py @@ -1,7 +1,8 @@ import isodate + from xrlint.node import DatasetNode -from xrlint.rule import RuleContext, RuleOp from xrlint.plugins.acdd.plugin import plugin +from xrlint.rule import RuleContext, RuleOp def datetime_is_iso(date_str): diff --git a/xrlint/plugins/acdd/rules/metadata_link.py b/xrlint/plugins/acdd/rules/metadata_link.py index 299bca0..042eba1 100644 --- a/xrlint/plugins/acdd/rules/metadata_link.py +++ b/xrlint/plugins/acdd/rules/metadata_link.py @@ -1,6 +1,6 @@ from xrlint.node import DatasetNode -from xrlint.rule import RuleContext, RuleOp from xrlint.plugins.acdd.plugin import plugin +from xrlint.rule import RuleContext, RuleOp @plugin.define_rule( diff --git a/xrlint/plugins/acdd/rules/no_id_blanks.py b/xrlint/plugins/acdd/rules/no_id_blanks.py index e0141b7..af9ac9f 100644 --- a/xrlint/plugins/acdd/rules/no_id_blanks.py +++ b/xrlint/plugins/acdd/rules/no_id_blanks.py @@ -1,6 +1,6 @@ from xrlint.node import DatasetNode -from xrlint.rule import RuleContext, RuleOp from xrlint.plugins.acdd.plugin import plugin +from xrlint.rule import RuleContext, RuleOp @plugin.define_rule( diff --git a/xrlint/plugins/core/rules/content_desc.py b/xrlint/plugins/core/rules/content_desc.py index 513b218..60310e5 100644 --- a/xrlint/plugins/core/rules/content_desc.py +++ b/xrlint/plugins/core/rules/content_desc.py @@ -6,8 +6,8 @@ from xrlint.node import DatasetNode, VariableNode from xrlint.plugins.core.plugin import plugin -from xrlint.util.attrs import hierarchical_attrs from xrlint.rule import RuleContext, RuleExit, RuleOp +from xrlint.util.attrs import hierarchical_attrs from xrlint.util.schema import schema DEFAULT_GLOBAL_ATTRS = ["title", "history"] diff --git a/xrlint/plugins/core/rules/conventions.py b/xrlint/plugins/core/rules/conventions.py index 0effdef..17d8879 100644 --- a/xrlint/plugins/core/rules/conventions.py +++ b/xrlint/plugins/core/rules/conventions.py @@ -6,8 +6,8 @@ from xrlint.node import DatasetNode from xrlint.plugins.core.plugin import plugin -from xrlint.util.attrs import hierarchical_attrs from xrlint.rule import RuleContext, RuleExit, RuleOp +from xrlint.util.attrs import hierarchical_attrs from xrlint.util.schema import schema diff --git a/xrlint/processor.py b/xrlint/processor.py index 52759aa..dc87d8c 100644 --- a/xrlint/processor.py +++ b/xrlint/processor.py @@ -3,8 +3,9 @@ # MIT license (https://mit-license.org/). from abc import ABC, abstractmethod +from collections.abc import Callable from dataclasses import dataclass -from typing import Any, Callable, Type +from typing import Any import xarray as xr @@ -87,7 +88,7 @@ class Processor(Operation): meta: ProcessorMeta """Information about the processor.""" - op_class: Type[ProcessorOp] + op_class: type[ProcessorOp] """A class that implements the processor operations.""" # Not yet: @@ -95,11 +96,11 @@ class Processor(Operation): # """`True` if this processor supports auto-fixing of datasets.""" @classmethod - def meta_class(cls) -> Type: + def meta_class(cls) -> type: return ProcessorMeta @classmethod - def op_base_class(cls) -> Type: + def op_base_class(cls) -> type: return ProcessorOp @classmethod @@ -111,8 +112,8 @@ def define_processor( name: str | None = None, version: str = "0.0.0", registry: dict[str, Processor] | None = None, - op_class: Type[ProcessorOp] | None = None, -) -> Callable[[Any], Type[ProcessorOp]] | Processor: + op_class: type[ProcessorOp] | None = None, +) -> Callable[[Any], type[ProcessorOp]] | Processor: """Define a processor. This function can be used to decorate your processor operation class diff --git a/xrlint/rule.py b/xrlint/rule.py index 6b53cc3..eb369bf 100644 --- a/xrlint/rule.py +++ b/xrlint/rule.py @@ -3,9 +3,9 @@ # MIT license (https://mit-license.org/). from abc import ABC, abstractmethod -from collections.abc import MutableMapping, Sequence +from collections.abc import Callable, MutableMapping, Sequence from dataclasses import dataclass, field -from typing import Any, Callable, Literal, Type +from typing import Any, Literal import xarray as xr @@ -223,17 +223,17 @@ class that implements the rule's logic. meta: RuleMeta """Rule metadata of type `RuleMeta`.""" - op_class: Type[RuleOp] + op_class: type[RuleOp] """The class the implements the rule's validation operation. The class must implement the `RuleOp` interface. """ @classmethod - def meta_class(cls) -> Type: + def meta_class(cls) -> type: return RuleMeta @classmethod - def op_base_class(cls) -> Type: + def op_base_class(cls) -> type: return RuleOp @classmethod @@ -337,8 +337,8 @@ def define_rule( docs_url: str | None = None, schema: dict[str, Any] | list[dict[str, Any]] | bool | None = None, registry: MutableMapping[str, Rule] | None = None, - op_class: Type[RuleOp] | None = None, -) -> Callable[[Any], Type[RuleOp]] | Rule: + op_class: type[RuleOp] | None = None, +) -> Callable[[Any], type[RuleOp]] | Rule: """Define a rule. This function can be used to decorate your rule operation class diff --git a/xrlint/testing.py b/xrlint/testing.py index 96dd1e0..bad653c 100644 --- a/xrlint/testing.py +++ b/xrlint/testing.py @@ -3,8 +3,9 @@ # MIT license (https://mit-license.org/). import unittest +from collections.abc import Callable from dataclasses import dataclass -from typing import Any, Callable, Final, Literal, Type +from typing import Any, Final, Literal import xarray as xr @@ -64,7 +65,7 @@ def __init__(self, *, config: ConfigLike = None, **config_props: Any): def run( self, rule_name: str, - rule_op_class: Type[RuleOp], + rule_op_class: type[RuleOp], *, valid: list[RuleTest] | None = None, invalid: list[RuleTest] | None = None, @@ -93,13 +94,13 @@ def run( def define_test( cls, rule_name: str, - rule_op_class: Type[RuleOp], + rule_op_class: type[RuleOp], *, valid: list[RuleTest] | None = None, invalid: list[RuleTest] | None = None, config: ConfigLike = None, **config_props: Any, - ) -> Type[unittest.TestCase]: + ) -> type[unittest.TestCase]: """Create a `unittest.TestCase` class for the given rule and tests. The returned class is derived from `unittest.TestCase` @@ -131,7 +132,7 @@ def define_test( def _create_tests( self, rule_name: str, - rule_op_class: Type[RuleOp], + rule_op_class: type[RuleOp], valid: list[RuleTest] | None, invalid: list[RuleTest] | None, ) -> dict[str, Callable[[unittest.TestCase | None], None]]: @@ -153,12 +154,12 @@ def make_args(checks: list[RuleTest] | None, mode: Literal["valid", "invalid"]): def _create_name_test( self, rule_name: str, - rule_op_class: Type[RuleOp], + rule_op_class: type[RuleOp], ) -> tuple[str, Callable]: test_id = "test_rule_meta" def test_fn(_self: unittest.TestCase): - rule_meta: RuleMeta = getattr(rule_op_class, "meta") + rule_meta: RuleMeta = rule_op_class.meta assert rule_meta.name == rule_name, ( f"rule name expected to be {rule_name!r}, but was {rule_meta.name!r}" ) @@ -169,7 +170,7 @@ def test_fn(_self: unittest.TestCase): def _create_test( self, rule_name: str, - rule_op_class: Type[RuleOp], + rule_op_class: type[RuleOp], test: RuleTest, test_index: int, test_mode: Literal["valid", "invalid"], @@ -189,7 +190,7 @@ def test_fn(_self: unittest.TestCase): def _test_rule( self, rule_name: str, - rule_op_class: Type[RuleOp], + rule_op_class: type[RuleOp], test: RuleTest, test_id: str, test_mode: Literal["valid", "invalid"], diff --git a/xrlint/util/attrs.py b/xrlint/util/attrs.py index 642d0de..cd29d4b 100644 --- a/xrlint/util/attrs.py +++ b/xrlint/util/attrs.py @@ -5,7 +5,7 @@ from collections.abc import Iterator, Mapping from typing import Any -from xrlint.node import DataTreeNode, DatasetNode +from xrlint.node import DatasetNode, DataTreeNode class HierarchicalAttrs(Mapping[str, Any]): diff --git a/xrlint/util/constructible.py b/xrlint/util/constructible.py index e0a7f62..f63bbfb 100644 --- a/xrlint/util/constructible.py +++ b/xrlint/util/constructible.py @@ -11,8 +11,6 @@ Any, Generic, Literal, - Optional, - Type, TypeVar, Union, get_args, @@ -124,7 +122,7 @@ def _from_str(cls, value: str, value_name: str) -> T: raise TypeError(cls._format_type_error(value, value_name)) @classmethod - def _from_class(cls, value: Type, value_name: str) -> T: + def _from_class(cls, value: type, value_name: str) -> T: """Create an instance of this class from a type value. The default implementation raises a `TypeError`. Override to implement a different behaviour. @@ -269,7 +267,7 @@ def class_parameters(cls) -> Mapping[str, Parameter]: return get_class_parameters(cls, forward_refs=cls.forward_refs()) @classmethod - def forward_refs(cls) -> Optional[Mapping[str, type]]: + def forward_refs(cls) -> Mapping[str, type] | None: """Get an extra namespace to be used for resolving parameter type hints. Called from [ValueConstructible._get_class_parameters][]. diff --git a/xrlint/util/formatting.py b/xrlint/util/formatting.py index 87853aa..8a09bc9 100644 --- a/xrlint/util/formatting.py +++ b/xrlint/util/formatting.py @@ -25,7 +25,7 @@ def format_problems(error_count: int, warning_count: int) -> str: def format_count( - count: int | float, + count: float, singular: str, plural: str | None = None, upper: bool | None = None, @@ -41,7 +41,7 @@ def format_count( def format_item( - count: int | float, + count: float, singular: str, plural: str | None = None, upper: bool | None = None, diff --git a/xrlint/util/importutil.py b/xrlint/util/importutil.py index 6506091..b2d4139 100644 --- a/xrlint/util/importutil.py +++ b/xrlint/util/importutil.py @@ -4,7 +4,8 @@ import importlib import pathlib -from typing import Any, Callable, Type, TypeVar +from collections.abc import Callable +from typing import Any, TypeVar from xrlint.util.formatting import format_message_type_of @@ -48,7 +49,7 @@ def import_value( *, constant: bool = False, factory: Callable[[Any], T] | None = None, - expected_type: Type[T] | None = None, + expected_type: type[T] | None = None, ) -> tuple[T, str]: """Import an exported value from given module reference. diff --git a/xrlint/util/merge.py b/xrlint/util/merge.py index 403e59f..7d0cb08 100644 --- a/xrlint/util/merge.py +++ b/xrlint/util/merge.py @@ -2,7 +2,8 @@ # This software is distributed under the terms and conditions of the # MIT license (https://mit-license.org/). -from typing import Any, Callable +from collections.abc import Callable +from typing import Any def merge_values( diff --git a/xrlint/util/schema.py b/xrlint/util/schema.py index bf8c5c7..b5d80ec 100644 --- a/xrlint/util/schema.py +++ b/xrlint/util/schema.py @@ -40,10 +40,10 @@ def schema( title: str | None = None, description: str | None = None, # "integer", "number" - minimum: int | float | None = None, - maximum: int | float | None = None, - exclusiveMinimum: int | float | None = None, - exclusiveMaximum: int | float | None = None, + minimum: float | None = None, + maximum: float | None = None, + exclusiveMinimum: float | None = None, + exclusiveMaximum: float | None = None, # "array" items: list[JsonSchema] | JsonSchema | None = None, # "object" diff --git a/xrlint/util/serializable.py b/xrlint/util/serializable.py index 4f9a4b9..ca10d1c 100644 --- a/xrlint/util/serializable.py +++ b/xrlint/util/serializable.py @@ -2,8 +2,9 @@ # This software is distributed under the terms and conditions of the # MIT license (https://mit-license.org/). +from collections.abc import Mapping, Sequence from dataclasses import fields, is_dataclass -from typing import Any, Final, Mapping, Sequence, TypeAlias +from typing import Any, Final, TypeAlias from xrlint.util.formatting import format_message_type_of From 5245f4b23daf2d52aaff8de6bd77b5e057f76c17 Mon Sep 17 00:00:00 2001 From: clarasb Date: Thu, 3 Sep 2026 17:35:22 +0200 Subject: [PATCH 02/11] ruff check --fix --unsafe-fixes --- examples/rule_testing.py | 4 +- notebooks/mkdataset.py | 18 ++--- tests/cli/test_main.py | 8 +-- tests/plugins/core/rules/test_content_desc.py | 72 +++++++++---------- tests/plugins/core/rules/test_conventions.py | 6 +- .../core/rules/test_coords_for_dims.py | 4 +- .../plugins/core/rules/test_grid_mappings.py | 4 +- .../plugins/core/rules/test_no_empty_attrs.py | 4 +- .../core/rules/test_no_empty_chunks.py | 2 +- tests/plugins/core/rules/test_var_desc.py | 64 ++++++++--------- tests/plugins/core/rules/test_var_flags.py | 20 +++--- .../core/rules/test_var_missing_data.py | 2 +- tests/plugins/core/rules/test_var_units.py | 2 +- tests/plugins/xcube/helpers.py | 68 +++++++++--------- .../xcube/rules/test_cube_dims_order.py | 2 +- .../xcube/rules/test_data_var_colors.py | 2 +- .../plugins/xcube/rules/test_dataset_title.py | 6 +- .../xcube/rules/test_grid_mapping_naming.py | 2 +- .../xcube/rules/test_increasing_time.py | 2 +- .../xcube/rules/test_lat_lon_naming.py | 2 +- .../xcube/rules/test_no_chunked_coords.py | 2 +- .../xcube/rules/test_single_grid_mapping.py | 4 +- tests/plugins/xcube/rules/test_time_naming.py | 2 +- tests/plugins/xcube/test_plugin.py | 2 +- tests/test_all.py | 4 +- tests/test_config.py | 6 +- tests/test_operation.py | 2 +- tests/test_testing.py | 6 +- tests/util/test_constructible.py | 12 +++- tests/util/test_serializable.py | 21 +++--- xrlint/_linter/validate.py | 2 +- xrlint/cli/config.py | 2 +- xrlint/cli/engine.py | 2 +- xrlint/config.py | 2 +- xrlint/constants.py | 2 +- xrlint/formatter.py | 2 +- xrlint/formatters/html.py | 6 +- xrlint/formatters/json.py | 8 +-- xrlint/formatters/simple.py | 8 +-- xrlint/plugins/core/__init__.py | 2 +- xrlint/plugins/core/rules/coords_for_dims.py | 8 ++- xrlint/plugins/core/rules/time_coordinate.py | 6 +- xrlint/plugins/xcube/__init__.py | 4 +- xrlint/plugins/xcube/processors/mldataset.py | 4 +- xrlint/plugins/xcube/rules/increasing_time.py | 2 +- xrlint/plugins/xcube/rules/ml_dataset_meta.py | 12 ++-- .../plugins/xcube/rules/no_chunked_coords.py | 6 +- xrlint/plugins/xcube/rules/time_naming.py | 2 +- xrlint/plugins/xcube/util.py | 7 +- xrlint/processor.py | 2 +- xrlint/rule.py | 16 ++--- xrlint/util/constructible.py | 2 +- xrlint/util/filepattern.py | 4 +- xrlint/util/schema.py | 32 ++++----- xrlint/util/serializable.py | 2 +- 55 files changed, 255 insertions(+), 245 deletions(-) diff --git a/examples/rule_testing.py b/examples/rule_testing.py index 299981a..6af8028 100644 --- a/examples/rule_testing.py +++ b/examples/rule_testing.py @@ -41,8 +41,8 @@ def validate_dataset(self, ctx: RuleContext, node: DatasetNode): tester = RuleTester() -valid_dataset = xr.Dataset(attrs=dict(title="Hello World!")) -invalid_dataset = xr.Dataset(attrs=dict(title="Hello Hamburg!")) +valid_dataset = xr.Dataset(attrs={"title": "Hello World!"}) +invalid_dataset = xr.Dataset(attrs={"title": "Hello Hamburg!"}) # You can use the tester to run a test directly # diff --git a/notebooks/mkdataset.py b/notebooks/mkdataset.py index 63e8b51..4322198 100644 --- a/notebooks/mkdataset.py +++ b/notebooks/mkdataset.py @@ -18,15 +18,15 @@ def make_dataset() -> xr.Dataset: """Create a dataset that passes xrlint core rules.""" return xr.Dataset( - attrs=dict( - Conventions="CF-1.10", - title="SST-Climatology Subset", - history="2025-01-31 17:31:00 - created;", - institution="BC", - source="SST CCI L4", - references="https://climate.esa.int/en/projects/sea-surface-temperature/", - comment="Demo dataset", - ), + attrs={ + "Conventions": "CF-1.10", + "title": "SST-Climatology Subset", + "history": "2025-01-31 17:31:00 - created;", + "institution": "BC", + "source": "SST CCI L4", + "references": "https://climate.esa.int/en/projects/sea-surface-temperature/", + "comment": "Demo dataset", + }, coords={ "x": xr.DataArray( np.linspace(-180, 180, nx), diff --git a/tests/cli/test_main.py b/tests/cli/test_main.py index 814de68..4790549 100644 --- a/tests/cli/test_main.py +++ b/tests/cli/test_main.py @@ -33,13 +33,13 @@ class CliMainTest(TestCase): # noinspection SpellCheckingInspection invalid_config_yaml = "- recommentet\n" - datasets = dict( - dataset1=xr.Dataset(attrs={"title": "Test 1"}), - dataset2=xr.Dataset( + datasets = { + "dataset1": xr.Dataset(attrs={"title": "Test 1"}), + "dataset2": xr.Dataset( attrs={"title": "Test 2"}, data_vars={"v": xr.DataArray([1, 2, 3], attrs={"units": "m/s"})}, ), - ) + } temp_dir: str last_cwd: str diff --git a/tests/plugins/core/rules/test_content_desc.py b/tests/plugins/core/rules/test_content_desc.py index f0d2119..75aed54 100644 --- a/tests/plugins/core/rules/test_content_desc.py +++ b/tests/plugins/core/rules/test_content_desc.py @@ -7,73 +7,73 @@ from xrlint.plugins.core.rules.content_desc import ContentDesc from xrlint.testing import RuleTest, RuleTester -global_attrs = dict( - title="OC-Climatology", - history="2025-01-26: created", -) +global_attrs = { + "title": "OC-Climatology", + "history": "2025-01-26: created", +} -common_attrs = dict( - institution="ESA", - source="a.nc; b.nc", - references="!", - comment="?", -) +common_attrs = { + "institution": "ESA", + "source": "a.nc; b.nc", + "references": "!", + "comment": "?", +} all_attrs = global_attrs | common_attrs time_coord = xr.DataArray( - [1, 2, 3], dims="time", attrs=dict(units="days since 2025-01-01") + [1, 2, 3], dims="time", attrs={"units": "days since 2025-01-01"} ) valid_dataset_0 = xr.Dataset( attrs=all_attrs, - data_vars=dict(chl=xr.DataArray([1, 2, 3], dims="time", attrs=dict())), - coords=dict(time=time_coord), + data_vars={"chl": xr.DataArray([1, 2, 3], dims="time", attrs={})}, + coords={"time": time_coord}, ) valid_dataset_1 = xr.Dataset( attrs=global_attrs, - data_vars=dict(chl=xr.DataArray([1, 2, 3], dims="time", attrs=common_attrs)), - coords=dict(time=time_coord), + data_vars={"chl": xr.DataArray([1, 2, 3], dims="time", attrs=common_attrs)}, + coords={"time": time_coord}, ) valid_dataset_1a = xr.Dataset( attrs=global_attrs, - data_vars=dict( - chl=xr.DataArray([1, 2, 3], dims="time", attrs=common_attrs), - crs=xr.DataArray(0, attrs=dict(grid_mapping_name="...")), - ), - coords=dict(time=time_coord), + data_vars={ + "chl": xr.DataArray([1, 2, 3], dims="time", attrs=common_attrs), + "crs": xr.DataArray(0, attrs={"grid_mapping_name": "..."}), + }, + coords={"time": time_coord}, ) valid_dataset_1b = xr.Dataset( attrs=global_attrs, - data_vars=dict( - chl=xr.DataArray([1, 2, 3], dims="time", attrs=common_attrs), - chl_unc=xr.DataArray(0, attrs=dict(units="...")), - ), - coords=dict(time=time_coord), + data_vars={ + "chl": xr.DataArray([1, 2, 3], dims="time", attrs=common_attrs), + "chl_unc": xr.DataArray(0, attrs={"units": "..."}), + }, + coords={"time": time_coord}, ) valid_dataset_2 = xr.Dataset( attrs=global_attrs, - data_vars=dict(chl=xr.DataArray([1, 2, 3], dims="time", attrs=dict())), - coords=dict(time=time_coord), + data_vars={"chl": xr.DataArray([1, 2, 3], dims="time", attrs={})}, + coords={"time": time_coord}, ) valid_dataset_3 = xr.Dataset( attrs=global_attrs, - data_vars=dict( - chl=xr.DataArray([1, 2, 3], dims="time", attrs=dict(description="Bla!")) - ), - coords=dict(time=time_coord), + data_vars={ + "chl": xr.DataArray([1, 2, 3], dims="time", attrs={"description": "Bla!"}) + }, + coords={"time": time_coord}, ) invalid_dataset_0 = xr.Dataset() invalid_dataset_1 = xr.Dataset( - attrs=dict(), - data_vars=dict(chl=xr.DataArray([1, 2, 3], dims="time", attrs=dict())), - coords=dict(time=time_coord), + attrs={}, + data_vars={"chl": xr.DataArray([1, 2, 3], dims="time", attrs={})}, + coords={"time": time_coord}, ) invalid_dataset_2 = xr.Dataset( attrs=global_attrs, - data_vars=dict(chl=xr.DataArray([1, 2, 3], dims="time", attrs=dict())), - coords=dict(time=time_coord), + data_vars={"chl": xr.DataArray([1, 2, 3], dims="time", attrs={})}, + coords={"time": time_coord}, ) ContentDescTest = RuleTester.define_test( diff --git a/tests/plugins/core/rules/test_conventions.py b/tests/plugins/core/rules/test_conventions.py index 8472eef..3605b9e 100644 --- a/tests/plugins/core/rules/test_conventions.py +++ b/tests/plugins/core/rules/test_conventions.py @@ -7,11 +7,11 @@ from xrlint.plugins.core.rules.conventions import Conventions from xrlint.testing import RuleTest, RuleTester -valid_dataset_0 = xr.Dataset(attrs=dict(Conventions="CF-1.10")) +valid_dataset_0 = xr.Dataset(attrs={"Conventions": "CF-1.10"}) invalid_dataset_0 = xr.Dataset() -invalid_dataset_1 = xr.Dataset(attrs=dict(Conventions=1.12)) -invalid_dataset_2 = xr.Dataset(attrs=dict(Conventions="CF 1.10")) +invalid_dataset_1 = xr.Dataset(attrs={"Conventions": 1.12}) +invalid_dataset_2 = xr.Dataset(attrs={"Conventions": "CF 1.10"}) ConventionsTest = RuleTester.define_test( diff --git a/tests/plugins/core/rules/test_coords_for_dims.py b/tests/plugins/core/rules/test_coords_for_dims.py index 306efba..d8322bb 100644 --- a/tests/plugins/core/rules/test_coords_for_dims.py +++ b/tests/plugins/core/rules/test_coords_for_dims.py @@ -7,9 +7,9 @@ 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")) +valid_dataset_1 = xr.Dataset(attrs={"title": "empty"}) valid_dataset_2 = xr.Dataset( - attrs=dict(title="v-data"), + attrs={"title": "v-data"}, coords={"x": xr.DataArray([0, 0.1, 0.2], dims="x", attrs={"units": "s"})}, data_vars={"v": xr.DataArray([10, 20, 30], dims="x", attrs={"units": "m/s"})}, ) diff --git a/tests/plugins/core/rules/test_grid_mappings.py b/tests/plugins/core/rules/test_grid_mappings.py index 13252ab..b825e82 100644 --- a/tests/plugins/core/rules/test_grid_mappings.py +++ b/tests/plugins/core/rules/test_grid_mappings.py @@ -11,7 +11,7 @@ def make_dataset(): return xr.Dataset( - attrs=dict(title="OC Data"), + attrs={"title": "OC Data"}, coords={ "x": xr.DataArray(np.linspace(0, 1, 4), dims="x", attrs={"units": "m"}), "y": xr.DataArray(np.linspace(0, 1, 3), dims="y", attrs={"units": "m"}), @@ -40,7 +40,7 @@ def make_dataset(): ) -valid_dataset_1 = xr.Dataset(attrs=dict(title="Empty")) +valid_dataset_1 = xr.Dataset(attrs={"title": "Empty"}) valid_dataset_2 = make_dataset() invalid_dataset_1 = make_dataset().drop_vars("crs") diff --git a/tests/plugins/core/rules/test_no_empty_attrs.py b/tests/plugins/core/rules/test_no_empty_attrs.py index 6ea92b0..a67fc0e 100644 --- a/tests/plugins/core/rules/test_no_empty_attrs.py +++ b/tests/plugins/core/rules/test_no_empty_attrs.py @@ -7,9 +7,9 @@ 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")) +valid_dataset_1 = xr.Dataset(attrs={"title": "empty"}) valid_dataset_2 = xr.Dataset( - attrs=dict(title="v-data"), + attrs={"title": "v-data"}, coords={"x": xr.DataArray([0, 0.1, 0.2], dims="x", attrs={"units": "s"})}, data_vars={"v": xr.DataArray([10, 20, 30], dims="x", attrs={"units": "m/s"})}, ) diff --git a/tests/plugins/core/rules/test_no_empty_chunks.py b/tests/plugins/core/rules/test_no_empty_chunks.py index 49a98e9..f6d6f77 100644 --- a/tests/plugins/core/rules/test_no_empty_chunks.py +++ b/tests/plugins/core/rules/test_no_empty_chunks.py @@ -8,7 +8,7 @@ from xrlint.testing import RuleTest, RuleTester # valid, because it is not chunked -valid_dataset_0 = xr.Dataset(attrs=dict(title="OC-Climatology")) +valid_dataset_0 = xr.Dataset(attrs={"title": "OC-Climatology"}) valid_dataset_0.encoding["source"] = "test.zarr" valid_dataset_0["sst"] = xr.DataArray([273, 274, 272], dims="time") valid_dataset_0["sst"].encoding["_FillValue"] = 0 diff --git a/tests/plugins/core/rules/test_var_desc.py b/tests/plugins/core/rules/test_var_desc.py index 69234de..9780a40 100644 --- a/tests/plugins/core/rules/test_var_desc.py +++ b/tests/plugins/core/rules/test_var_desc.py @@ -7,62 +7,62 @@ from xrlint.plugins.core.rules.var_desc import VarDesc from xrlint.testing import RuleTest, RuleTester -pressure_attrs = dict( - long_name="mean sea level pressure", - units="hPa", - standard_name="air_pressure_at_sea_level", -) +pressure_attrs = { + "long_name": "mean sea level pressure", + "units": "hPa", + "standard_name": "air_pressure_at_sea_level", +} time_coord = xr.DataArray( - [1, 2, 3], dims="time", attrs=dict(units="days since 2025-01-01") + [1, 2, 3], dims="time", attrs={"units": "days since 2025-01-01"} ) valid_dataset_0 = xr.Dataset( - coords=dict(time=time_coord), + coords={"time": time_coord}, ) valid_dataset_1 = xr.Dataset( - data_vars=dict(pressure=xr.DataArray([1, 2, 3], dims="time", attrs=pressure_attrs)), - coords=dict(time=time_coord), + data_vars={"pressure": xr.DataArray([1, 2, 3], dims="time", attrs=pressure_attrs)}, + coords={"time": time_coord}, ) valid_dataset_2 = xr.Dataset( - data_vars=dict( - chl=xr.DataArray( - [1, 2, 3], dims="time", attrs=dict(description="It is air pressure") + data_vars={ + "chl": xr.DataArray( + [1, 2, 3], dims="time", attrs={"description": "It is air pressure"} ) - ), - coords=dict(time=time_coord), + }, + coords={"time": time_coord}, ) invalid_dataset_0 = xr.Dataset( - attrs=dict(), - data_vars=dict(chl=xr.DataArray([1, 2, 3], dims="time", attrs=dict())), - coords=dict(time=time_coord), + attrs={}, + data_vars={"chl": xr.DataArray([1, 2, 3], dims="time", attrs={})}, + coords={"time": time_coord}, ) invalid_dataset_1 = xr.Dataset( - attrs=dict(), - data_vars=dict( - chl=xr.DataArray( + attrs={}, + data_vars={ + "chl": xr.DataArray( [1, 2, 3], dims="time", - attrs=dict(standard_name="air_pressure_at_sea_level"), + attrs={"standard_name": "air_pressure_at_sea_level"}, ) - ), - coords=dict(time=time_coord), + }, + coords={"time": time_coord}, ) invalid_dataset_2 = xr.Dataset( - attrs=dict(), - data_vars=dict( - chl=xr.DataArray( - [1, 2, 3], dims="time", attrs=dict(long_name="mean sea level pressure") + attrs={}, + data_vars={ + "chl": xr.DataArray( + [1, 2, 3], dims="time", attrs={"long_name": "mean sea level pressure"} ) - ), - coords=dict(time=time_coord), + }, + coords={"time": time_coord}, ) invalid_dataset_3 = xr.Dataset( - attrs=dict(), - data_vars=dict(chl=xr.DataArray([1, 2, 3], dims="time", attrs=pressure_attrs)), - coords=dict(time=time_coord), + attrs={}, + data_vars={"chl": xr.DataArray([1, 2, 3], dims="time", attrs=pressure_attrs)}, + coords={"time": time_coord}, ) VarDescTest = RuleTester.define_test( diff --git a/tests/plugins/core/rules/test_var_flags.py b/tests/plugins/core/rules/test_var_flags.py index a3b12cc..5525f54 100644 --- a/tests/plugins/core/rules/test_var_flags.py +++ b/tests/plugins/core/rules/test_var_flags.py @@ -10,26 +10,26 @@ valid_dataset_0 = xr.Dataset() valid_dataset_1 = xr.Dataset( - attrs=dict(title="sensor-data"), + attrs={"title": "sensor-data"}, data_vars={ "sensor_status_qc": xr.DataArray( [1, 3, 5, 2, 0, 5], dims="x", - attrs=dict( - long_name="Sensor Status", - standard_name="status_flag", - _FillValue=0, - valid_range=[1, 15], - flag_masks=[1, 2, 12, 12, 12], - flag_values=[1, 2, 4, 8, 12], - flag_meanings=( + attrs={ + "long_name": "Sensor Status", + "standard_name": "status_flag", + "_FillValue": 0, + "valid_range": [1, 15], + "flag_masks": [1, 2, 12, 12, 12], + "flag_values": [1, 2, 4, 8, 12], + "flag_meanings": ( "low_battery" " hardware_fault" " offline_mode" " calibration_mode" " maintenance_mode" ), - ), + }, ) }, ) diff --git a/tests/plugins/core/rules/test_var_missing_data.py b/tests/plugins/core/rules/test_var_missing_data.py index 0099efc..87b206d 100644 --- a/tests/plugins/core/rules/test_var_missing_data.py +++ b/tests/plugins/core/rules/test_var_missing_data.py @@ -9,7 +9,7 @@ valid_dataset_0 = xr.Dataset() valid_dataset_1 = xr.Dataset( - attrs=dict(title="v-data"), + attrs={"title": "v-data"}, coords={"t": xr.DataArray([0, 1, 2], dims="t", attrs={"units": "seconds"})}, data_vars={"v": xr.DataArray([10, 20, 30], dims="t", attrs={"units": "m/s"})}, ) diff --git a/tests/plugins/core/rules/test_var_units.py b/tests/plugins/core/rules/test_var_units.py index bf00bdb..13fb6cb 100644 --- a/tests/plugins/core/rules/test_var_units.py +++ b/tests/plugins/core/rules/test_var_units.py @@ -9,7 +9,7 @@ valid_dataset_0 = xr.Dataset() valid_dataset_1 = xr.Dataset( - attrs=dict(title="v-data"), + attrs={"title": "v-data"}, coords={"t": xr.DataArray([0, 1, 2], dims="t", attrs={"units": "seconds"})}, data_vars={"v": xr.DataArray([10, 20, 30], dims="t", attrs={"units": "m/s"})}, ) diff --git a/tests/plugins/xcube/helpers.py b/tests/plugins/xcube/helpers.py index a817058..c7e5c47 100644 --- a/tests/plugins/xcube/helpers.py +++ b/tests/plugins/xcube/helpers.py @@ -41,61 +41,61 @@ def make_cube(nx: int, ny: int, nt: int | None = None) -> xr.Dataset: an in-memory dataset with one 3-d data variable "chl" with dimensions ["time",] "lat", "lon". """ - x_attrs = dict( - long_name="longitude", - standard_name="longitude", - units="degrees_east", - ) - y_attrs = dict( - long_name="latitude", - standard_name="latitude", - units="degrees_north", - ) + x_attrs = { + "long_name": "longitude", + "standard_name": "longitude", + "units": "degrees_east", + } + y_attrs = { + "long_name": "latitude", + "standard_name": "latitude", + "units": "degrees_north", + } dx = 180.0 / nx dy = 90.0 / ny x_data = np.linspace(-180 + dx, 180 - dx, nx) y_data = np.linspace(-90 + dy, 90 - dy, ny) - chl_attrs = dict( - long_name="chlorophyll concentration", - standard_name="chlorophyll_concentration", - units="mg/m^3", - _FillValue=0, - ) - chl_chunks = dict(lat=min(ny, 90), lon=min(nx, 90)) + chl_attrs = { + "long_name": "chlorophyll concentration", + "standard_name": "chlorophyll_concentration", + "units": "mg/m^3", + "_FillValue": 0, + } + chl_chunks = {"lat": min(ny, 90), "lon": min(nx, 90)} - ds_attrs = dict(title="Chlorophyll") + ds_attrs = {"title": "Chlorophyll"} - coords = dict( - lon=xr.DataArray(x_data, dims="lon", attrs=x_attrs), - lat=xr.DataArray(y_data, dims="lat", attrs=y_attrs), - ) + coords = { + "lon": xr.DataArray(x_data, dims="lon", attrs=x_attrs), + "lat": xr.DataArray(y_data, dims="lat", attrs=y_attrs), + } if nt is None: return xr.Dataset( - data_vars=dict( - chl=xr.DataArray( + data_vars={ + "chl": xr.DataArray( np.zeros((ny, nx)), dims=["lat", "lon"], attrs=chl_attrs ).chunk(**chl_chunks), - ), + }, coords=coords, attrs=ds_attrs, ) else: - time_attrs = dict( - long_name="time", - standard_name="time", - units="days since 2024-06-10:12:00:00 utc", - calendar="gregorian", - ) + time_attrs = { + "long_name": "time", + "standard_name": "time", + "units": "days since 2024-06-10:12:00:00 utc", + "calendar": "gregorian", + } coords.update(time=xr.DataArray(range(nt), dims="time", attrs=time_attrs)) return xr.Dataset( - data_vars=dict( - chl=xr.DataArray( + data_vars={ + "chl": xr.DataArray( np.zeros((nt, ny, nx)), dims=["time", "lat", "lon"], attrs=chl_attrs ).chunk(time=1, **chl_chunks), - ), + }, coords=coords, attrs=ds_attrs, ) diff --git a/tests/plugins/xcube/rules/test_cube_dims_order.py b/tests/plugins/xcube/rules/test_cube_dims_order.py index c3b466a..62637a9 100644 --- a/tests/plugins/xcube/rules/test_cube_dims_order.py +++ b/tests/plugins/xcube/rules/test_cube_dims_order.py @@ -12,7 +12,7 @@ def make_dataset(dims: tuple[str, str, str]): n = 3 return xr.Dataset( - attrs=dict(title="v-data"), + attrs={"title": "v-data"}, coords={ "x": xr.DataArray(np.linspace(0, 1, n), dims="x", attrs={"units": "m"}), "y": xr.DataArray(np.linspace(0, 1, n), dims="y", attrs={"units": "m"}), diff --git a/tests/plugins/xcube/rules/test_data_var_colors.py b/tests/plugins/xcube/rules/test_data_var_colors.py index 8c84551..9f3bee6 100644 --- a/tests/plugins/xcube/rules/test_data_var_colors.py +++ b/tests/plugins/xcube/rules/test_data_var_colors.py @@ -13,7 +13,7 @@ def make_dataset(): dims = ["time", "y", "x"] n = 5 return xr.Dataset( - attrs=dict(title="v-data"), + attrs={"title": "v-data"}, coords={ dims[2]: xr.DataArray( np.linspace(0, 1, n), dims=dims[2], attrs={"units": "m"} diff --git a/tests/plugins/xcube/rules/test_dataset_title.py b/tests/plugins/xcube/rules/test_dataset_title.py index 3f2f68d..6f61211 100644 --- a/tests/plugins/xcube/rules/test_dataset_title.py +++ b/tests/plugins/xcube/rules/test_dataset_title.py @@ -7,11 +7,11 @@ from xrlint.plugins.xcube.rules.dataset_title import DatasetTitle from xrlint.testing import RuleTest, RuleTester -valid_dataset_0 = xr.Dataset(attrs=dict(title="OC-Climatology")) -valid_dataset_1 = xr.Dataset(attrs=dict(title="SST-Climatology")) +valid_dataset_0 = xr.Dataset(attrs={"title": "OC-Climatology"}) +valid_dataset_1 = xr.Dataset(attrs={"title": "SST-Climatology"}) invalid_dataset_0 = xr.Dataset() -invalid_dataset_1 = xr.Dataset(attrs=dict(title="")) +invalid_dataset_1 = xr.Dataset(attrs={"title": ""}) DatasetTitleTest = RuleTester.define_test( diff --git a/tests/plugins/xcube/rules/test_grid_mapping_naming.py b/tests/plugins/xcube/rules/test_grid_mapping_naming.py index 39ca479..686fcd0 100644 --- a/tests/plugins/xcube/rules/test_grid_mapping_naming.py +++ b/tests/plugins/xcube/rules/test_grid_mapping_naming.py @@ -11,7 +11,7 @@ def make_dataset(): return xr.Dataset( - attrs=dict(title="OC Data"), + attrs={"title": "OC Data"}, coords={ "x": xr.DataArray(np.linspace(0, 1, 4), dims="x", attrs={"units": "m"}), "y": xr.DataArray(np.linspace(0, 1, 3), dims="y", attrs={"units": "m"}), diff --git a/tests/plugins/xcube/rules/test_increasing_time.py b/tests/plugins/xcube/rules/test_increasing_time.py index 4965af7..d81ae5e 100644 --- a/tests/plugins/xcube/rules/test_increasing_time.py +++ b/tests/plugins/xcube/rules/test_increasing_time.py @@ -13,7 +13,7 @@ def make_dataset(): dims = ["time", "y", "x"] n = 5 return xr.Dataset( - attrs=dict(title="v-data"), + attrs={"title": "v-data"}, coords={ dims[2]: xr.DataArray( np.linspace(0, 1, n), dims=dims[2], attrs={"units": "m"} diff --git a/tests/plugins/xcube/rules/test_lat_lon_naming.py b/tests/plugins/xcube/rules/test_lat_lon_naming.py index e37b6c2..90d016e 100644 --- a/tests/plugins/xcube/rules/test_lat_lon_naming.py +++ b/tests/plugins/xcube/rules/test_lat_lon_naming.py @@ -13,7 +13,7 @@ def make_dataset(lat_dim: str, lon_dim: str): dims = ["time", lat_dim, lon_dim] n = 3 return xr.Dataset( - attrs=dict(title="v-data"), + attrs={"title": "v-data"}, coords={ lon_dim: xr.DataArray( np.linspace(0, 1, n), dims=lon_dim, attrs={"units": "m"} diff --git a/tests/plugins/xcube/rules/test_no_chunked_coords.py b/tests/plugins/xcube/rules/test_no_chunked_coords.py index eca9124..defb272 100644 --- a/tests/plugins/xcube/rules/test_no_chunked_coords.py +++ b/tests/plugins/xcube/rules/test_no_chunked_coords.py @@ -8,7 +8,7 @@ from xrlint.plugins.xcube.rules.no_chunked_coords import NoChunkedCoords from xrlint.testing import RuleTest, RuleTester -valid_dataset_0 = xr.Dataset(attrs=dict(title="Empty")) +valid_dataset_0 = xr.Dataset(attrs={"title": "Empty"}) valid_dataset_1 = make_cube(360, 180, 3) valid_dataset_2 = make_cube(90, 45, 20) # ok, below default limit 5: ceil(20 / 5) = 4 diff --git a/tests/plugins/xcube/rules/test_single_grid_mapping.py b/tests/plugins/xcube/rules/test_single_grid_mapping.py index 3ed5240..7966d1a 100644 --- a/tests/plugins/xcube/rules/test_single_grid_mapping.py +++ b/tests/plugins/xcube/rules/test_single_grid_mapping.py @@ -11,7 +11,7 @@ def make_dataset(): return xr.Dataset( - attrs=dict(title="OC Data"), + attrs={"title": "OC Data"}, coords={ "x": xr.DataArray(np.linspace(0, 1, 4), dims="x", attrs={"units": "m"}), "y": xr.DataArray(np.linspace(0, 1, 3), dims="y", attrs={"units": "m"}), @@ -40,7 +40,7 @@ def make_dataset(): ) -valid_dataset_1 = xr.Dataset(attrs=dict(title="Empty")) +valid_dataset_1 = xr.Dataset(attrs={"title": "Empty"}) valid_dataset_2 = make_dataset() valid_dataset_3 = make_dataset().rename({"crs": "spatial_ref"}) valid_dataset_4 = make_dataset().drop_vars("crs") diff --git a/tests/plugins/xcube/rules/test_time_naming.py b/tests/plugins/xcube/rules/test_time_naming.py index 5b61840..04031a3 100644 --- a/tests/plugins/xcube/rules/test_time_naming.py +++ b/tests/plugins/xcube/rules/test_time_naming.py @@ -14,7 +14,7 @@ def make_dataset(time_var: str, time_dim: str | None = None): dims = [time_dim, "y", "x"] n = 3 return xr.Dataset( - attrs=dict(title="v-data"), + attrs={"title": "v-data"}, coords={ "x": xr.DataArray(np.linspace(0, 1, n), dims="x", attrs={"units": "m"}), "y": xr.DataArray(np.linspace(0, 1, n), dims="y", attrs={"units": "m"}), diff --git a/tests/plugins/xcube/test_plugin.py b/tests/plugins/xcube/test_plugin.py index 6492078..8dbc7cc 100644 --- a/tests/plugins/xcube/test_plugin.py +++ b/tests/plugins/xcube/test_plugin.py @@ -38,7 +38,7 @@ def test_configs_complete(self): }, set(plugin.configs.keys()), ) - all_rule_names = set(f"xcube/{k}" for k in plugin.rules.keys()) + all_rule_names = {f"xcube/{k}" for k in plugin.rules} self.assertEqual( all_rule_names, set(plugin.configs["all"][-1].rules.keys()), diff --git a/tests/test_all.py b/tests/test_all.py index 1d5c197..55e7ca6 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -11,9 +11,9 @@ def test_api_is_complete(self): from xrlint.all import __all__ # noinspection PyUnresolvedReferences - keys = set( + keys = { k for k, v in xrl.__dict__.items() if isinstance(k, str) and not k.startswith("_") - ) + } self.assertEqual(set(__all__), keys) diff --git a/tests/test_config.py b/tests/test_config.py index 6ee4359..bedf4c9 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -107,9 +107,9 @@ def postprocess( processor = define_processor("myproc", op_class=MyProc) config_obj = ConfigObject( - plugins=dict( - myplugin=new_plugin("myplugin", processors=dict(myproc=processor)) - ) + plugins={ + "myplugin": new_plugin("myplugin", processors={"myproc": processor}) + } ) processor_op = config_obj.get_processor_op(MyProc()) diff --git a/tests/test_operation.py b/tests/test_operation.py index f0a7fc9..c369b80 100644 --- a/tests/test_operation.py +++ b/tests/test_operation.py @@ -161,7 +161,7 @@ def test_define_op(self): class MyThingOp3(ThingOp): """This is my 3rd thing.""" - value = Thing.define_operation(MyThingOp3, meta_kwargs=dict(version="1.0")) + value = Thing.define_operation(MyThingOp3, meta_kwargs={"version": "1.0"}) self.assertIsInstance(value, Thing) self.assertIsInstance(value.meta, ThingMeta) self.assertEqual("my-thing-op-3", value.meta.name) diff --git a/tests/test_testing.py b/tests/test_testing.py index 070748f..91fa803 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -19,10 +19,10 @@ def validate_dataset(self, ctx: RuleContext, node: DatasetNode): ctx.report("Datasets must have a title") -VALID_DATASET_1 = xr.Dataset(attrs=dict(title="OC-Climatology")) -VALID_DATASET_2 = xr.Dataset(attrs=dict(title="SST-Climatology")) +VALID_DATASET_1 = xr.Dataset(attrs={"title": "OC-Climatology"}) +VALID_DATASET_2 = xr.Dataset(attrs={"title": "SST-Climatology"}) INVALID_DATASET_1 = xr.Dataset() -INVALID_DATASET_2 = xr.Dataset(attrs=dict(title="")) +INVALID_DATASET_2 = xr.Dataset(attrs={"title": ""}) # noinspection PyMethodMayBeStatic diff --git a/tests/util/test_constructible.py b/tests/util/test_constructible.py index 747b934..715e0ce 100644 --- a/tests/util/test_constructible.py +++ b/tests/util/test_constructible.py @@ -207,7 +207,7 @@ def test_required_props_fail(self): RequiredPropsContainer.from_value({"x": 12.0, "z": 34.0}, "rpc") def test_no_types_ok(self): - ntc = NoTypesContainer.from_value(dict(u=True, v=654, w="abc")) + ntc = NoTypesContainer.from_value({"u": True, "v": 654, "w": "abc"}) self.assertEqual(True, ntc.u) self.assertEqual(654, ntc.v) self.assertEqual("abc", ntc.w) @@ -215,7 +215,15 @@ 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") + kwargs = { + "a": "?", + "b": True, + "c": 12, + "d": 34.56, + "e": "uvw", + "f": bytes, + "g": "on", + } container = SimpleTypesContainer(**kwargs) self.assertEqual(container, SimpleTypesContainer.from_value(kwargs)) self.assertIs(container, SimpleTypesContainer.from_value(container)) diff --git a/tests/util/test_serializable.py b/tests/util/test_serializable.py index 38c1f5f..fa65a0f 100644 --- a/tests/util/test_serializable.py +++ b/tests/util/test_serializable.py @@ -35,10 +35,10 @@ class PlainComplexTypesContainer(JsonSerializable): def __init__( self, p: PlainSimpleTypesContainer = PlainSimpleTypesContainer(), - q: dict[str, bool] = None, - r: dict[str, PlainSimpleTypesContainer] = None, - s: list[int] = None, - t: list[PlainSimpleTypesContainer] = None, + q: dict[str, bool] | None = None, + r: dict[str, PlainSimpleTypesContainer] | None = None, + s: list[int] | None = None, + t: list[PlainSimpleTypesContainer] | None = None, u: float | None = None, ): self.p = p @@ -104,8 +104,8 @@ def test_plain_simple_ok(self): def test_plain_complex_ok(self): container = PlainComplexTypesContainer( - q=dict(p=True, q=False), - r=dict(u=PlainSimpleTypesContainer(), v=PlainSimpleTypesContainer()), + q={"p": True, "q": False}, + r={"u": PlainSimpleTypesContainer(), "v": PlainSimpleTypesContainer()}, s=[1, 2, 3], t=[ PlainSimpleTypesContainer(c=5, d=6.7), @@ -192,10 +192,11 @@ def test_dataclass_simple_ok(self): def test_dataclass_complex_ok(self): container = DataclassComplexTypesContainer( - q=dict(p=True, q=False), - r=dict( - u=DataclassSimpleTypesContainer(), v=DataclassSimpleTypesContainer() - ), + q={"p": True, "q": False}, + r={ + "u": DataclassSimpleTypesContainer(), + "v": DataclassSimpleTypesContainer(), + }, s=[1, 2, 3], t=[ DataclassSimpleTypesContainer(c=5, d=6.7), diff --git a/xrlint/_linter/validate.py b/xrlint/_linter/validate.py index bf81399..efbfdef 100644 --- a/xrlint/_linter/validate.py +++ b/xrlint/_linter/validate.py @@ -85,7 +85,7 @@ def _open_dataset( ) -> tuple[xr.Dataset | xr.DataTree, float]: """Open a dataset.""" engine = opener_options.pop("engine", None) - if engine is None and (file_path.endswith(".zarr") or file_path.endswith(".zarr/")): + if engine is None and (file_path.endswith((".zarr", ".zarr/"))): engine = "zarr" try: t0 = time.time() diff --git a/xrlint/cli/config.py b/xrlint/cli/config.py index 1a9342c..2816929 100644 --- a/xrlint/cli/config.py +++ b/xrlint/cli/config.py @@ -48,7 +48,7 @@ def read_config(config_path: str | Path | PathLike[str]) -> Config: def _read_config_like(config_path: str) -> Any: - if config_path.endswith(".yml") or config_path.endswith(".yaml"): + if config_path.endswith((".yml", ".yaml")): return _read_config_yaml(config_path) if config_path.endswith(".json"): return _read_config_json(config_path) diff --git a/xrlint/cli/engine.py b/xrlint/cli/engine.py index 63da2a8..0c60fa5 100644 --- a/xrlint/cli/engine.py +++ b/xrlint/cli/engine.py @@ -234,7 +234,7 @@ def format_results(self, results: Iterable[Result]) -> str: raise click.ClickException( f"unknown format {output_format!r}." f" The available formats are" - f" {', '.join(repr(k) for k in formatters.keys())}." + f" {', '.join(repr(k) for k in formatters)}." ) # Here we could pass and validate format-specific args/kwargs # against formatter.meta.schema diff --git a/xrlint/config.py b/xrlint/config.py index c85276a..f04ec56 100644 --- a/xrlint/config.py +++ b/xrlint/config.py @@ -223,7 +223,7 @@ def get_processor_op( raise ValueError(f"unknown processor {processor_spec!r}") return processor.op_class() - def merge(self, config: "ConfigObject", name: str = None) -> "ConfigObject": + def merge(self, config: "ConfigObject", name: str | None = None) -> "ConfigObject": return ConfigObject( name=name, files=self._merge_pattern_lists(self.files, config.files), diff --git a/xrlint/constants.py b/xrlint/constants.py index 1043cea..d2b12a1 100644 --- a/xrlint/constants.py +++ b/xrlint/constants.py @@ -28,4 +28,4 @@ SEVERITY_ENUM: Final[dict[int | str, int]] = ( SEVERITY_NAME_TO_CODE | SEVERITY_CODE_TO_CODE ) -SEVERITY_ENUM_TEXT: Final = ", ".join(f"{k!r}" for k in SEVERITY_ENUM.keys()) +SEVERITY_ENUM_TEXT: Final = ", ".join(f"{k!r}" for k in SEVERITY_ENUM) diff --git a/xrlint/formatter.py b/xrlint/formatter.py index fa547cc..f59cef1 100644 --- a/xrlint/formatter.py +++ b/xrlint/formatter.py @@ -102,7 +102,7 @@ def define_formatter( return Formatter.define_operation( None, registry=self._registrations, - meta_kwargs=dict(name=name, version=version, schema=schema), + meta_kwargs={"name": name, "version": version, "schema": schema}, ) def __getitem__(self, key: str) -> Formatter: diff --git a/xrlint/formatters/html.py b/xrlint/formatters/html.py index aaec003..9039d1f 100644 --- a/xrlint/formatters/html.py +++ b/xrlint/formatters/html.py @@ -18,9 +18,9 @@ version="1.0.0", schema=schema( "object", - properties=dict( - with_meta=schema("boolean", default=False), - ), + properties={ + "with_meta": schema("boolean", default=False), + }, ), ) class Html(FormatterOp): diff --git a/xrlint/formatters/json.py b/xrlint/formatters/json.py index ce10a66..5df3de1 100644 --- a/xrlint/formatters/json.py +++ b/xrlint/formatters/json.py @@ -16,10 +16,10 @@ version="1.0.0", schema=schema( "object", - properties=dict( - indent=schema("integer", minimum=0, maximum=8, default=2), - with_meta=schema("boolean", default=False), - ), + properties={ + "indent": schema("integer", minimum=0, maximum=8, default=2), + "with_meta": schema("boolean", default=False), + }, ), ) class Json(FormatterOp): diff --git a/xrlint/formatters/simple.py b/xrlint/formatters/simple.py index 5516e1e..bd0df95 100644 --- a/xrlint/formatters/simple.py +++ b/xrlint/formatters/simple.py @@ -19,10 +19,10 @@ version="1.0.0", schema=schema( "object", - properties=dict( - styled=schema("boolean", default=True), - output=schema("boolean", default=True), - ), + properties={ + "styled": schema("boolean", default=True), + "output": schema("boolean", default=True), + }, ), ) class Simple(FormatterOp): diff --git a/xrlint/plugins/core/__init__.py b/xrlint/plugins/core/__init__.py index f48004b..320cf5b 100644 --- a/xrlint/plugins/core/__init__.py +++ b/xrlint/plugins/core/__init__.py @@ -38,7 +38,7 @@ def export_plugin() -> Plugin: "all", { "name": "all", - "rules": {rule_id: "error" for rule_id in plugin.rules.keys()}, + "rules": {rule_id: "error" for rule_id in plugin.rules}, }, ) diff --git a/xrlint/plugins/core/rules/coords_for_dims.py b/xrlint/plugins/core/rules/coords_for_dims.py index 30d0e23..c562a34 100644 --- a/xrlint/plugins/core/rules/coords_for_dims.py +++ b/xrlint/plugins/core/rules/coords_for_dims.py @@ -37,8 +37,10 @@ def validate_dataset(self, ctx: RuleContext, node: DatasetNode): f"{format_item(n, 'Data variable dimension')} without" f" coordinates: {', '.join(no_coord_dims)}.", suggestions=[ - f"Add corresponding {format_item(n, 'coordinate variable')}" - f" to dataset:" - f" {', '.join(f'{d}[{dataset.sizes[d]}]' for d in no_coord_dims)}." + ( + f"Add corresponding {format_item(n, 'coordinate variable')}" + f" to dataset:" + f" {', '.join(f'{d}[{dataset.sizes[d]}]' for d in no_coord_dims)}." + ) ], ) diff --git a/xrlint/plugins/core/rules/time_coordinate.py b/xrlint/plugins/core/rules/time_coordinate.py index daeb784..797ca64 100644 --- a/xrlint/plugins/core/rules/time_coordinate.py +++ b/xrlint/plugins/core/rules/time_coordinate.py @@ -166,8 +166,10 @@ def validate_variable(self, ctx: RuleContext, node: VariableNode): f"Missing timezone in {source} 'units': {units!r}.", suggestions=[ _units_format_suggestion(), - f"Append timezone specification, e.g., use" - f" {' '.join(units_parts[:-1] + ['+0:00'])!r}.", + ( + f"Append timezone specification, e.g., use" + f" {' '.join(units_parts[:-1] + ['+0:00'])!r}." + ), ], ) diff --git a/xrlint/plugins/xcube/__init__.py b/xrlint/plugins/xcube/__init__.py index 31ed245..bce9aee 100644 --- a/xrlint/plugins/xcube/__init__.py +++ b/xrlint/plugins/xcube/__init__.py @@ -59,9 +59,7 @@ def export_plugin() -> Plugin: [ *common_configs, { - "rules": { - f"xcube/{rule_id}": "error" for rule_id in plugin.rules.keys() - }, + "rules": {f"xcube/{rule_id}": "error" for rule_id in plugin.rules}, }, ], ) diff --git a/xrlint/plugins/xcube/processors/mldataset.py b/xrlint/plugins/xcube/processors/mldataset.py index f63c020..5cfe794 100644 --- a/xrlint/plugins/xcube/processors/mldataset.py +++ b/xrlint/plugins/xcube/processors/mldataset.py @@ -50,12 +50,12 @@ def preprocess( # with fs.open(f"{fs_path}/.zgroup") as stream: # group_props = json.load(stream) - level_paths, num_levels = parse_levels(fs, file_path, file_names) + level_paths, _num_levels = parse_levels(fs, file_path, file_names) engine = opener_options.pop("engine", "zarr") level_datasets: list[xr.Dataset | None] = [] - for level, level_path in level_paths.items(): + for level_path in level_paths.values(): level_dataset = xr.open_dataset(level_path, engine=engine, **opener_options) level_datasets.append((level_dataset, level_path)) diff --git a/xrlint/plugins/xcube/rules/increasing_time.py b/xrlint/plugins/xcube/rules/increasing_time.py index 8a3a239..dd4bd6a 100644 --- a/xrlint/plugins/xcube/rules/increasing_time.py +++ b/xrlint/plugins/xcube/rules/increasing_time.py @@ -24,7 +24,7 @@ def validate_variable(self, ctx: RuleContext, node: VariableNode): array = node.array if node.in_coords() and node.name == "time" and array.dims == ("time",): diff_array: np.ndarray = array.diff("time").values - if not np.count_nonzero(diff_array > 0) == diff_array.size: + if np.count_nonzero(diff_array > 0) != diff_array.size: check_indexes(ctx, diff_array == 0, "Duplicate") check_indexes(ctx, diff_array < 0, "Backsliding") raise RuleExit # No need to apply rule any further diff --git a/xrlint/plugins/xcube/rules/ml_dataset_meta.py b/xrlint/plugins/xcube/rules/ml_dataset_meta.py index 3e51b0f..acf9a69 100644 --- a/xrlint/plugins/xcube/rules/ml_dataset_meta.py +++ b/xrlint/plugins/xcube/rules/ml_dataset_meta.py @@ -43,10 +43,12 @@ def validate_dataset(self, ctx: RuleContext, node: DatasetNode): ctx.report( f"Missing {ML_META_FILENAME!r} meta-info file.", suggestions=[ - f"Add {ML_META_FILENAME!r} meta-info file." - f" Without the meta-info the dataset cannot be reliably extended" - f" as the aggregation method used for each variable must be" - f" specified." + ( + f"Add {ML_META_FILENAME!r} meta-info file." + f" Without the meta-info the dataset cannot be reliably extended" + f" as the aggregation method used for each variable must be" + f" specified." + ) ], ) return @@ -82,7 +84,7 @@ def validate_dataset(self, ctx: RuleContext, node: DatasetNode): f"Missing value for variable {var_name!r}" f" in 'agg_methods' of {ML_META_FILENAME!r} meta-info." ) - for var_name in meta.agg_methods.keys(): + for var_name in meta.agg_methods: if var_name not in node.dataset: ctx.report( f"Variable {var_name!r} not found in dataset, but specified" diff --git a/xrlint/plugins/xcube/rules/no_chunked_coords.py b/xrlint/plugins/xcube/rules/no_chunked_coords.py index 8b68135..aea4db2 100644 --- a/xrlint/plugins/xcube/rules/no_chunked_coords.py +++ b/xrlint/plugins/xcube/rules/no_chunked_coords.py @@ -26,14 +26,14 @@ ), schema=schema( "object", - properties=dict( - limit=schema( + properties={ + "limit": schema( "integer", minimum=0, default=DEFAULT_LIMIT, title="Acceptable number of chunks", ) - ), + }, ), ) class NoChunkedCoords(RuleOp): diff --git a/xrlint/plugins/xcube/rules/time_naming.py b/xrlint/plugins/xcube/rules/time_naming.py index e9f6a4b..935c67a 100644 --- a/xrlint/plugins/xcube/rules/time_naming.py +++ b/xrlint/plugins/xcube/rules/time_naming.py @@ -27,7 +27,7 @@ def validate_dataset(self, ctx: RuleContext, node: DatasetNode): for var_name, var in node.dataset.coords.items() if var_name != TIME_NAME and _is_time_coord(var_name, var) } - for var_name, var in time_vars.items(): + for var_name in time_vars: ctx.report( f"The coordinate {var_name!r} should be named {TIME_NAME!r}.", suggestions=[f"Rename {var_name!r} to {TIME_NAME!r}."], diff --git a/xrlint/plugins/xcube/util.py b/xrlint/plugins/xcube/util.py index b574af6..ca04514 100644 --- a/xrlint/plugins/xcube/util.py +++ b/xrlint/plugins/xcube/util.py @@ -85,7 +85,7 @@ def get_spatial_size( dataset: xr.Dataset, ) -> tuple[tuple[Hashable, int], tuple[Hashable, int]] | None: """Return (x_size, y_size) for given dataset.""" - for k, v in dataset.data_vars.items(): + for v in dataset.data_vars.values(): if is_spatial_var(v): y_name, x_name = v.dims[-2:] x_size = dataset.sizes[x_name] @@ -110,11 +110,8 @@ def resolve_path(path: str, root_path: str | None = None) -> str: def is_absolute_path(path: str) -> bool: return ( # Unix abs path - path.startswith("/") - # URL + path.startswith(("/", "\\\\")) or "://" in path - # Windows abs paths - or path.startswith("\\\\") or path.find(":\\", 1) == 1 or path.find(":/", 1) == 1 ) diff --git a/xrlint/processor.py b/xrlint/processor.py index dc87d8c..d0263cf 100644 --- a/xrlint/processor.py +++ b/xrlint/processor.py @@ -142,5 +142,5 @@ def define_processor( a class derived from [ProcessorOp][xrlint.processor.ProcessorOp]. """ return Processor.define_operation( - op_class, registry=registry, meta_kwargs=dict(name=name, version=version) + op_class, registry=registry, meta_kwargs={"name": name, "version": version} ) diff --git a/xrlint/rule.py b/xrlint/rule.py index eb369bf..ff48cc9 100644 --- a/xrlint/rule.py +++ b/xrlint/rule.py @@ -373,12 +373,12 @@ def define_rule( return Rule.define_operation( op_class, registry=registry, - meta_kwargs=dict( - name=name, - version=version, - description=description, - docs_url=docs_url, - type=type if type else "problem", - schema=schema, - ), + meta_kwargs={ + "name": name, + "version": version, + "description": description, + "docs_url": docs_url, + "type": type if type else "problem", + "schema": schema, + }, ) diff --git a/xrlint/util/constructible.py b/xrlint/util/constructible.py index f63bbfb..60d5ab5 100644 --- a/xrlint/util/constructible.py +++ b/xrlint/util/constructible.py @@ -329,7 +329,7 @@ def _format_type_error(cls, value: Any, value_name: str) -> str: return format_message_type_of(value_name, value, cls.value_type_name()) -class MappingConstructible(Generic[T], ValueConstructible[T]): +class MappingConstructible(ValueConstructible[T], Generic[T]): """A mixin that makes your classes constructible from mappings, such as a `dict`. diff --git a/xrlint/util/filepattern.py b/xrlint/util/filepattern.py index ebdd39d..e7085ec 100644 --- a/xrlint/util/filepattern.py +++ b/xrlint/util/filepattern.py @@ -40,7 +40,7 @@ def __init__(self, pattern: str, flip_negate: bool = False): self._empty = False self._comment = False self._negate = False - self._dir: Literal[True, None] = None # we cannot know + self._dir: Literal[True] | None = None # we cannot know if not pattern: self._empty = True @@ -80,7 +80,7 @@ def negate(self) -> bool: return self._negate @property - def dir(self) -> Literal[True, None]: + def dir(self) -> Literal[True] | None: """`True` if this matcher's pattern denotes a directory.""" return self._dir diff --git a/xrlint/util/schema.py b/xrlint/util/schema.py index b5d80ec..bf37edd 100644 --- a/xrlint/util/schema.py +++ b/xrlint/util/schema.py @@ -54,22 +54,22 @@ def schema( """Helper function so you have keyword-arguments for creating schemas.""" return { k: v - for k, v in dict( - type=_parse_type(type), - default=default, - const=const, - enum=enum, - minimum=minimum, - maximum=maximum, - exclusiveMinimum=exclusiveMinimum, - exclusiveMaximum=exclusiveMaximum, - items=items, - properties=properties, - additionalProperties=False if additionalProperties is False else None, - required=required, - title=title, - description=description, - ).items() + for k, v in { + "type": _parse_type(type), + "default": default, + "const": const, + "enum": enum, + "minimum": minimum, + "maximum": maximum, + "exclusiveMinimum": exclusiveMinimum, + "exclusiveMaximum": exclusiveMaximum, + "items": items, + "properties": properties, + "additionalProperties": False if additionalProperties is False else None, + "required": required, + "title": title, + "description": description, + }.items() if v is not None } diff --git a/xrlint/util/serializable.py b/xrlint/util/serializable.py index ca10d1c..f6f172c 100644 --- a/xrlint/util/serializable.py +++ b/xrlint/util/serializable.py @@ -99,5 +99,5 @@ def _is_non_protected_property_name(cls, key: Any) -> bool: isinstance(key, str) and key.isidentifier() and not key[0].isupper() - and not key[0] == "_" + and key[0] != "_" ) From 08b2ed89180d8820a70fc1de6f2a2a801abb2e59 Mon Sep 17 00:00:00 2001 From: clarasb Date: Thu, 3 Sep 2026 17:58:12 +0200 Subject: [PATCH 03/11] =?UTF-8?q?ruff=20SIM117=20=E2=80=93=20nested=20with?= =?UTF-8?q?=20statements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/cli/test_config.py | 50 ++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/tests/cli/test_config.py b/tests/cli/test_config.py index dc17790..160943b 100644 --- a/tests/cli/test_config.py +++ b/tests/cli/test_config.py @@ -125,16 +125,18 @@ def test_read_config_yaml_with_format_error(self): read_config(config_path) def test_read_config_yaml_with_type_error(self): - with text_file("config.yaml", "97") as config_path: - with pytest.raises( + with ( + text_file("config.yaml", "97") as config_path, + pytest.raises( ConfigError, match=( r"config\.yaml\: config must be of type" r" Config \| ConfigObjectLike \| str \| Sequence\[ConfigObjectLike \| str\]," r" but got int" ), - ): - read_config(config_path) + ), + ): + read_config(config_path) def test_read_config_with_unknown_format(self): with pytest.raises( @@ -145,38 +147,45 @@ def test_read_config_with_unknown_format(self): def test_read_config_py_no_export(self): py_code = "x = 42\n" - with text_file(self.new_config_py(), py_code) as config_path: - with pytest.raises( + with ( + text_file(self.new_config_py(), py_code) as config_path, + pytest.raises( ConfigError, match=( "config_1002.py: attribute 'export_config'" " not found in module 'config_1002'" ), - ): - read_config(config_path) + ), + ): + read_config(config_path) def test_read_config_py_with_value_error(self): py_code = "def export_config():\n raise ValueError('value is useless!')\n" - with text_file(self.new_config_py(), py_code) as config_path: - with pytest.raises( + with ( + text_file(self.new_config_py(), py_code) as config_path, + pytest.raises( ValueError, match="value is useless!", - ): - read_config(config_path) + ), + ): + read_config(config_path) def test_read_config_py_with_os_error(self): py_code = "def export_config():\n raise OSError('where is my hat?')\n" - with text_file(self.new_config_py(), py_code) as config_path: - with pytest.raises( + with ( + text_file(self.new_config_py(), py_code) as config_path, + pytest.raises( ConfigError, match="where is my hat?", - ): - read_config(config_path) + ), + ): + read_config(config_path) def test_read_config_py_with_invalid_config_list(self): py_code = "def export_config():\n return 42\n" - with text_file(self.new_config_py(), py_code) as config_path: - with pytest.raises( + with ( + text_file(self.new_config_py(), py_code) as config_path, + pytest.raises( ConfigError, match=( r"\.py: failed converting value of 'config_1003:export_config':" @@ -184,8 +193,9 @@ def test_read_config_py_with_invalid_config_list(self): r" Config \| ConfigObjectLike \| str \| Sequence\[ConfigObjectLike \| str\]," r" but got int" ), - ): - read_config(config_path) + ), + ): + read_config(config_path) class CliConfigResolveTest(unittest.TestCase): From 8734a9c1adc0ae33b3157756834420003ca1f2ed Mon Sep 17 00:00:00 2001 From: clarasb Date: Thu, 3 Sep 2026 17:58:29 +0200 Subject: [PATCH 04/11] ruff TC010 - invalid string inside `|` union type --- xrlint/operation.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xrlint/operation.py b/xrlint/operation.py index 9561f0f..dcaa5d2 100644 --- a/xrlint/operation.py +++ b/xrlint/operation.py @@ -2,6 +2,8 @@ # This software is distributed under the terms and conditions of the # MIT license (https://mit-license.org/). +from __future__ import annotations + from collections.abc import Callable, MutableMapping from dataclasses import dataclass from inspect import getdoc, isclass @@ -153,11 +155,11 @@ def define_operation( registry: MutableMapping[str, "Operation"] | None = None, meta_kwargs: dict[str, Any] | None = None, **kwargs, - ) -> Callable[[type], type] | "Operation": + ) -> Callable[[type], type] | Operation: """Defines an operation.""" meta_kwargs = meta_kwargs or {} - def _define_op(_op_class: type, decorated=True) -> type | "Operation": + def _define_op(_op_class: type, decorated=True) -> type | Operation: cls._assert_op_class_ok( f"decorated {cls.value_name()} component", _op_class ) From 3d0e796dcab07d7d88c0a36752b114147ca1873a Mon Sep 17 00:00:00 2001 From: clarasb Date: Tue, 8 Sep 2026 17:03:20 +0200 Subject: [PATCH 05/11] =?UTF-8?q?ruff=20B021=20=E2=80=93=20f-string=20used?= =?UTF-8?q?=20as=20docstring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xrlint/plugins/xcube/processors/mldataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xrlint/plugins/xcube/processors/mldataset.py b/xrlint/plugins/xcube/processors/mldataset.py index 5cfe794..92847c6 100644 --- a/xrlint/plugins/xcube/processors/mldataset.py +++ b/xrlint/plugins/xcube/processors/mldataset.py @@ -26,7 +26,7 @@ @plugin.define_processor("multi-level-dataset") class MultiLevelDatasetProcessor(ProcessorOp): - f"""This processor should be used with `files: [{ML_FILE_PATTERN}"]`.""" + f"""This processor should be used with `files: ["{ML_FILE_PATTERN}"]`.""" # noqa: B021 def preprocess( self, file_path: str, opener_options: dict[str, Any] From 090579ab68e2846dc9c150dca8e287c33ed758ba Mon Sep 17 00:00:00 2001 From: clarasb Date: Tue, 8 Sep 2026 17:09:40 +0200 Subject: [PATCH 06/11] =?UTF-8?q?ruff=20PLR0124=20=E2=80=93=20comparison?= =?UTF-8?q?=20with=20itself?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/util/test_filepattern.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/util/test_filepattern.py b/tests/util/test_filepattern.py index 800ece7..61e5006 100644 --- a/tests/util/test_filepattern.py +++ b/tests/util/test_filepattern.py @@ -12,7 +12,6 @@ def test_basics(self): matcher = FilePattern("**/*.h5") self.assertEqual("**/*.h5", str(matcher)) self.assertEqual("FilePattern('**/*.h5')", repr(matcher)) - self.assertTrue(matcher == matcher) self.assertFalse(matcher == 5) self.assertTrue(matcher == FilePattern("**/*.h5")) self.assertFalse(matcher == FilePattern("**/*.nc")) From 4ba2ce72c6535dfe2aefc0daddd468386d9a1705 Mon Sep 17 00:00:00 2001 From: clarasb Date: Tue, 8 Sep 2026 17:45:24 +0200 Subject: [PATCH 07/11] =?UTF-8?q?ruff=20B008=20=E2=80=93=20function=20call?= =?UTF-8?q?=20in=20default=20argument?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/util/test_serializable.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/util/test_serializable.py b/tests/util/test_serializable.py index fa65a0f..f785dbb 100644 --- a/tests/util/test_serializable.py +++ b/tests/util/test_serializable.py @@ -34,14 +34,14 @@ def __init__( class PlainComplexTypesContainer(JsonSerializable): def __init__( self, - p: PlainSimpleTypesContainer = PlainSimpleTypesContainer(), + p: PlainSimpleTypesContainer | None = None, q: dict[str, bool] | None = None, r: dict[str, PlainSimpleTypesContainer] | None = None, s: list[int] | None = None, t: list[PlainSimpleTypesContainer] | None = None, u: float | None = None, ): - self.p = p + self.p = p if p is not None else PlainSimpleTypesContainer() self.q = q or {} self.r = r or {} self.s = s or [] From afb21fceacbf7fe0651a95856198857d26a3b75f Mon Sep 17 00:00:00 2001 From: clarasb Date: Tue, 8 Sep 2026 17:47:43 +0200 Subject: [PATCH 08/11] =?UTF-8?q?ruff=20RUF012=20=E2=80=93=20mutable=20cla?= =?UTF-8?q?ss=20attributes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/cli/test_main.py | 4 ++-- tests/formatters/test_simple.py | 4 ++-- tests/plugins/xcube/processors/test_mldataset.py | 2 +- xrlint/plugins/acdd/rules/attributes.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/cli/test_main.py b/tests/cli/test_main.py index 4790549..0c73222 100644 --- a/tests/cli/test_main.py +++ b/tests/cli/test_main.py @@ -26,14 +26,14 @@ # noinspection PyTypeChecker class CliMainTest(TestCase): - files = ["dataset1.zarr", "dataset1.nc", "dataset2.zarr", "dataset2.nc"] + files = ["dataset1.zarr", "dataset1.nc", "dataset2.zarr", "dataset2.nc"] # noqa: RUF012 ok_config_yaml = "- rules:\n var-units: error\n" fail_config_yaml = "- rules:\n conventions: error\n" # noinspection SpellCheckingInspection invalid_config_yaml = "- recommentet\n" - datasets = { + datasets = { # noqa: RUF012 "dataset1": xr.Dataset(attrs={"title": "Test 1"}), "dataset2": xr.Dataset( attrs={"title": "Test 2"}, diff --git a/tests/formatters/test_simple.py b/tests/formatters/test_simple.py index 1bf6d15..b22be13 100644 --- a/tests/formatters/test_simple.py +++ b/tests/formatters/test_simple.py @@ -11,7 +11,7 @@ class SimpleTest(TestCase): - errors_and_warnings = [ + errors_and_warnings = [ # noqa: RUF012 Result( file_path="test1.nc", config_object=ConfigObject(), @@ -23,7 +23,7 @@ class SimpleTest(TestCase): ) ] - warnings_only = [ + warnings_only = [ # noqa: RUF012 Result( file_path="test2.nc", config_object=ConfigObject(), diff --git a/tests/plugins/xcube/processors/test_mldataset.py b/tests/plugins/xcube/processors/test_mldataset.py index 825bef4..9b306e8 100644 --- a/tests/plugins/xcube/processors/test_mldataset.py +++ b/tests/plugins/xcube/processors/test_mldataset.py @@ -27,7 +27,7 @@ class MultiLevelDatasetProcessorTest(TestCase): num_levels = 4 meta_path = f"{levels_dir}/.zlevels" - meta_content = { + meta_content = { # noqa: RUF012 "version": "1.0", "num_levels": num_levels, "use_saved_levels": False, diff --git a/xrlint/plugins/acdd/rules/attributes.py b/xrlint/plugins/acdd/rules/attributes.py index 7fab2bb..cf3aa3e 100644 --- a/xrlint/plugins/acdd/rules/attributes.py +++ b/xrlint/plugins/acdd/rules/attributes.py @@ -102,7 +102,7 @@ class AttributesRule(RuleOp): - attrs = {} + attrs = {} # noqa: RUF012 level: Literal["highly recommended", "recommended", "suggested"] = "recommended" def validate_dataset(self, ctx: RuleContext, node: DatasetNode): From 7f6517465a97906b4750aa555000990df8da229c Mon Sep 17 00:00:00 2001 From: clarasb Date: Tue, 8 Sep 2026 18:02:04 +0200 Subject: [PATCH 09/11] ruff check --fix --- xrlint/operation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xrlint/operation.py b/xrlint/operation.py index dcaa5d2..f05dfb1 100644 --- a/xrlint/operation.py +++ b/xrlint/operation.py @@ -84,7 +84,7 @@ def to_json(self, value_name: str | None = None) -> str: return super().to_json(value_name=value_name) @classmethod - def _from_class(cls, value: type, value_name: str) -> "Operation": + def _from_class(cls, value: type, value_name: str) -> Operation: # noinspection PyTypeChecker if issubclass(value, cls.op_base_class()): op_class = value @@ -104,7 +104,7 @@ def _from_class(cls, value: type, value_name: str) -> "Operation": return super()._from_class(value, value_name) @classmethod - def _from_str(cls, value: str, value_name: str) -> "Operation": + def _from_str(cls, value: str, value_name: str) -> Operation: # noinspection PyTypeChecker operator, operator_ref = import_value( value, @@ -152,7 +152,7 @@ def define_operation( cls, op_class: type | None, *, - registry: MutableMapping[str, "Operation"] | None = None, + registry: MutableMapping[str, Operation] | None = None, meta_kwargs: dict[str, Any] | None = None, **kwargs, ) -> Callable[[type], type] | Operation: From 0bb1c3b422da27f325a83eb428396488a240a452 Mon Sep 17 00:00:00 2001 From: clarasb Date: Wed, 9 Sep 2026 16:29:49 +0200 Subject: [PATCH 10/11] address ruff lint findings and update typing test accordingly --- tests/util/test_constructible.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/util/test_constructible.py b/tests/util/test_constructible.py index 715e0ce..13074c4 100644 --- a/tests/util/test_constructible.py +++ b/tests/util/test_constructible.py @@ -10,7 +10,6 @@ Any, Literal, TypeAlias, - Union, get_args, get_origin, ) @@ -102,9 +101,6 @@ class TypingTest(TestCase): def test_assumptions(self): # self.assertTrue(isinstance(Any, type)) self.assertTrue(isinstance(UnionType, type)) - self.assertTrue(not isinstance(Union, type)) - self.assertTrue(not isinstance(Union, UnionType)) - self.assertTrue(Union != UnionType) self.assertEqual(None, get_origin("NoTypesContainer")) self.assertEqual(None, get_origin("dict")) @@ -113,13 +109,13 @@ def test_assumptions(self): (str, "NoTypesContainer"), get_args(dict[str, "NoTypesContainer"]) ) - self.assertEqual(Union, get_origin(T1)) + self.assertEqual(UnionType, get_origin(T1)) self.assertEqual({bool, int, str, NoneType}, set(get_args(T1))) - self.assertEqual(Union, get_origin(T2)) + self.assertEqual(UnionType, get_origin(T2)) self.assertEqual({int, NoneType}, set(get_args(T2))) - self.assertEqual(Union, get_origin(T3)) + self.assertEqual(UnionType, get_origin(T3)) self.assertEqual({Any, NoneType}, set(get_args(T3))) From 8b1afc5db402b17e9a88afe9198b58feedf102dd Mon Sep 17 00:00:00 2001 From: clarasb Date: Wed, 9 Sep 2026 16:41:59 +0200 Subject: [PATCH 11/11] address ruff lint findings and update typing test accordingly --- tests/util/test_constructible.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/util/test_constructible.py b/tests/util/test_constructible.py index 13074c4..1caad85 100644 --- a/tests/util/test_constructible.py +++ b/tests/util/test_constructible.py @@ -10,6 +10,7 @@ Any, Literal, TypeAlias, + Union, get_args, get_origin, ) @@ -115,7 +116,8 @@ def test_assumptions(self): self.assertEqual(UnionType, get_origin(T2)) self.assertEqual({int, NoneType}, set(get_args(T2))) - self.assertEqual(UnionType, get_origin(T3)) + # Python 3.10 may report Any | None as typing.Union. + self.assertIn(get_origin(T3), (Union, UnionType)) self.assertEqual({Any, NoneType}, set(get_args(T3)))