diff --git a/noxfile.py b/noxfile.py index df59202..2a2a9ce 100644 --- a/noxfile.py +++ b/noxfile.py @@ -23,7 +23,6 @@ def test(session: Session): """Run all tests.""" session.install('-U', 'pip') session.install('-U', '.[test,toml]', 'coverage') - session.run('pip', 'list') session.run('coverage', 'run', '-p', '-m', 'ward', *session.posargs) @@ -32,7 +31,6 @@ def test_without_toml(session: Session): """Run tests without optional TOML support installed.""" session.install('-U', 'pip') session.install('-U', '.[test-without-toml]', 'coverage') - session.run('pip', 'list') session.run('coverage', 'run', '-p', '-m', 'ward', *session.posargs) diff --git a/nt2/casters.py b/nt2/casters.py index 2089774..a53cd01 100644 --- a/nt2/casters.py +++ b/nt2/casters.py @@ -67,14 +67,14 @@ def _str_to_num(informal_num: str) -> int | float: num = float(informal_num) except ValueError as e: for prefix, base in {'0x': 16, '0o': 8, '0b': 2}.items(): - if re.match(f"[+-]?{prefix}", informal_num, re.I): + if re.match(f"[+-]?{prefix}", informal_num, re.IGNORECASE): try: num = int(informal_num, base) except Exception: # pragma: no cover raise ValueError(': '.join(e.args)) from None else: return num - raise e # pragma: no cover + raise # pragma: no cover try: inum = int(num) except (ValueError, OverflowError): diff --git a/nt2/dumpers.py b/nt2/dumpers.py index 06095e6..060fe40 100644 --- a/nt2/dumpers.py +++ b/nt2/dumpers.py @@ -6,12 +6,16 @@ import sys from json import dump as _jdump, dumps as _jdumps, loads as _jloads from json.decoder import JSONDecodeError -from pathlib import Path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from pathlib import Path + + from plumbum import LocalPath from textwrap import indent -from typing import Sequence, TextIO, cast +from typing import BinaryIO, Sequence, TextIO, cast from nestedtext import dump as _ntdump, dumps as _ntdumps, load as _ntload -from plumbum import LocalPath from rich.console import Console as RichConsole from rich.syntax import Syntax as RichSyntax from ruamel.yaml.scalarstring import walk_tree as use_multiline_syntax @@ -110,8 +114,8 @@ def ydump(data: dict | list): out_stream = io.StringIO() try: YAML_EDITOR.dump(data, out_stream) - except Exception as e: - raise e + except Exception: + raise else: _syntax_print(out_stream.getvalue(), 'yaml') finally: @@ -140,9 +144,9 @@ def tdump(data: dict): """ _require_toml_support() if sys.stdout.isatty(): - _syntax_print(_tdumps(data, multiline_strings=True), 'toml') # type: ignore + _syntax_print(_tdumps(data, multiline_strings=True), 'toml') # pyright: ignore [reportPossiblyUnboundVariable] else: - print(_tdumps(data, multiline_strings=True), end='') # type: ignore + print(_tdumps(data, multiline_strings=True), end='') # pyright: ignore [reportPossiblyUnboundVariable] def jloads(content: str) -> dict | list: @@ -238,7 +242,7 @@ def dump_yaml_to_schema(*input_files: LocalPath): _dump_typed_data_to_schema(typed_data) else: for f in input_files: - with open(f, encoding='utf-8') as ifile: + with f.open(encoding='utf-8') as ifile: typed_data = yload(ifile) _dump_typed_data_to_schema(typed_data) @@ -252,12 +256,12 @@ def dump_toml_to_schema(*input_files: LocalPath): """ _require_toml_support() if not input_files: - typed_data = tloads(sys.stdin.read()) # type: ignore + typed_data = tloads(sys.stdin.read()) # pyright: ignore [reportPossiblyUnboundVariable] _dump_typed_data_to_schema(typed_data) else: for f in input_files: - with open(f, 'rb') as ifile: - typed_data = tload(ifile) # type: ignore + with f.open('rb') as ifile: + typed_data = tload(cast(BinaryIO, ifile)) # pyright: ignore [reportPossiblyUnboundVariable] _dump_typed_data_to_schema(typed_data) @@ -275,7 +279,7 @@ def dump_yaml_to_nestedtext(*input_files: LocalPath): ntdump(data) else: for f in input_files: - with open(f, encoding='utf-8') as ifile: + with f.open(encoding='utf-8') as ifile: data = yload(ifile) data = converter.unstructure(data) ntdump(data) @@ -291,13 +295,13 @@ def dump_toml_to_nestedtext(*input_files: LocalPath): _require_toml_support() converter = mk_stringy_converter() if not input_files: - data = tloads(sys.stdin.read()) # type: ignore + data = tloads(sys.stdin.read()) # pyright: ignore [reportPossiblyUnboundVariable] data = converter.unstructure(data) ntdump(data) else: for f in input_files: - with open(f, 'rb') as ifile: - data = tload(ifile) # type: ignore + with f.open('rb') as ifile: + data = tload(cast(BinaryIO, ifile)) # pyright: ignore [reportPossiblyUnboundVariable] data = converter.unstructure(data) ntdump(data) diff --git a/nt2/ui.py b/nt2/ui.py old mode 100755 new mode 100644 index 287ecf4..672da1e --- a/nt2/ui.py +++ b/nt2/ui.py @@ -6,11 +6,16 @@ import sys from json import JSONDecodeError -from typing import cast +from typing import ClassVar, cast from nestedtext import NestedTextError, load as ntload from plumbum.cli import Application, ExistingFile, Flag, SwitchAttr -from plumbum.colors import blue, green, magenta, yellow # type: ignore +from plumbum.colors import ( + blue, # pyright: ignore [reportAttributeAccessIssue] + green, # pyright: ignore [reportAttributeAccessIssue] + magenta, # pyright: ignore [reportAttributeAccessIssue] + yellow, # pyright: ignore [reportAttributeAccessIssue] +) from rich import inspect as _rich_inspect from rich.console import Console as RichConsole from ruamel.yaml.parser import ParserError as YAMLParserError @@ -66,7 +71,7 @@ class _ColorApp(Application): PROGNAME = green VERSION = __version__ | blue COLOR_USAGE = green - COLOR_GROUPS = {'Meta-switches': magenta, 'Switches': yellow, 'Subcommands': blue} + COLOR_GROUPS: ClassVar = {'Meta-switches': magenta, 'Switches': yellow, 'Subcommands': blue} ALLOW_ABBREV = True @@ -137,7 +142,7 @@ class NestedTextToJSON(_NestedTextToTypedFormat, _NestedTextToTypedFormatSupport nt2json --int People.age --boolean 'People."is a wizard"' example.nt """ - def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101 + def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201 try: for schema_file in cast(list, self.schema_files): schema = cast(dict, ntload(schema_file)) @@ -176,7 +181,7 @@ class NestedTextToYAML( nt2yaml --int People.age --boolean 'People."is a wizard"' example.nt """ - def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101 + def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201 try: for schema_file in cast(list, self.schema_files): schema = cast(dict, ntload(schema_file)) @@ -213,7 +218,7 @@ class NestedTextToTOML(_NestedTextToTypedFormat, _NestedTextToTypedFormatSupport nt2toml --int People.age --boolean 'People."is a wizard"' example.nt """ - def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101 + def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201 try: for schema_file in cast(list, self.schema_files): schema = cast(dict, ntload(schema_file)) @@ -242,7 +247,7 @@ class JSONToNestedText(_TypedFormatToSchema): cat example.json | json2nt """ - def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101 + def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201 try: if not self.to_schema: dump_json_to_nestedtext(*input_files) @@ -263,7 +268,7 @@ class YAMLToNestedText(_TypedFormatToSchema): cat example.yml | yaml2nt """ - def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101 + def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201 try: if not self.to_schema: dump_yaml_to_nestedtext(*input_files) @@ -284,7 +289,7 @@ class TOMLToNestedText(_TypedFormatToSchema): cat example.yml | toml2nt """ - def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101 + def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201 try: if not self.to_schema: dump_toml_to_nestedtext(*input_files) diff --git a/nt2/yamlpath_tools.py b/nt2/yamlpath_tools.py index 40bfe6b..0bc8e3c 100644 --- a/nt2/yamlpath_tools.py +++ b/nt2/yamlpath_tools.py @@ -6,19 +6,20 @@ from collections import defaultdict from datetime import date, datetime, time from types import SimpleNamespace -from typing import Iterable +from typing import TYPE_CHECKING, Iterable try: from types import NoneType except ImportError: NoneType = type(None) -from ruamel.yaml.main import YAML +if TYPE_CHECKING: + from ruamel.yaml.main import YAML + from yamlpath.wrappers.nodecoords import NodeCoords from yamlpath import Processor, YAMLPath from yamlpath.common import Parsers from yamlpath.exceptions import YAMLPathException from yamlpath.wrappers import ConsolePrinter -from yamlpath.wrappers.nodecoords import NodeCoords def mk_yaml_editor() -> YAML: @@ -73,7 +74,7 @@ def non_null_matches(surgeon: Processor, *query_paths: str) -> Iterable[NodeCoor yield from matches -def _schema_entry_type(obj: int | float | bool | None | datetime | date | time) -> str: +def _schema_entry_type(obj: float | bool | None | datetime | date | time) -> str: # -> Literal['number', 'boolean', 'null', 'date'] if isinstance(obj, bool): return 'boolean' diff --git a/pyproject.toml b/pyproject.toml index 96b7251..823987c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,7 +53,8 @@ quote-style = "preserve" skip-magic-trailing-comma = true [tool.ruff.lint] -select = ["F", "E", "W", "C90", "I", "N", "D", "UP", "YTT", "ANN", "S", "FBT", "B", "SIM"] +select = ["ALL"] +ignore = ["BLE", "COM812", "EM", "FIX", "ISC001", "PERF203", "PGH003", "Q000", "T20", "TD002", "TD003", "TRY003", "TRY302"] [tool.ruff.lint.flake8-annotations] suppress-none-returning = true @@ -65,6 +66,9 @@ split-on-trailing-comma = false [tool.ruff.lint.pydocstyle] convention = "pep257" +[tool.ruff.lint.pylint] +max-args = 6 + [tool.taskipy.tasks] fmt = "nox -s fmt" lock = "nox -s lock" diff --git a/test/commands.py b/test/commands.py index a1331f4..4a58031 100644 --- a/test/commands.py +++ b/test/commands.py @@ -10,16 +10,19 @@ Each version herein returns the stdout as a `str` for convenience in tests. """ +from __future__ import annotations + import io import sys -from typing import Sequence, Union, cast +from typing import TYPE_CHECKING, Sequence, cast try: from typing import TypeAlias except ImportError: from typing import Any as TypeAlias -from plumbum import LocalPath +if TYPE_CHECKING: + from plumbum import LocalPath from plumbum.cli import Application as _Application from nt2.ui import ( @@ -43,7 +46,7 @@ def _run_app( app_class: Application, *cli_args: LocalPath, - **cli_kwargs: Union[str, LocalPath, Sequence[str], bool], + **cli_kwargs: str | LocalPath | Sequence[str] | bool, ) -> str: """ Invoke `app_class` with given flags, and return stdout content. @@ -64,8 +67,8 @@ def _run_app( try: sys.stdout = fake_stdout app, main_result = app_class.invoke(*cli_args, **cli_kwargs) - except Exception as e: # pragma: no cover - raise e + except Exception: # pragma: no cover + raise else: output = fake_stdout.getvalue() finally: @@ -74,7 +77,7 @@ def _run_app( return output -def json2nt(*cli_args: LocalPath, **cli_kwargs: Union[str, bool]) -> str: +def json2nt(*cli_args: LocalPath, **cli_kwargs: str | bool) -> str: """ Invoke `JSONToNestedText` in a test-friendly way. @@ -88,7 +91,7 @@ def json2nt(*cli_args: LocalPath, **cli_kwargs: Union[str, bool]) -> str: return _run_app(JSONToNestedText, *cli_args, **cli_kwargs) -def nt2json(*cli_args: LocalPath, **cli_kwargs: Union[str, LocalPath, Sequence[str]]) -> str: +def nt2json(*cli_args: LocalPath, **cli_kwargs: str | LocalPath | Sequence[str]) -> str: """ Invoke `NestedTextToJSON` in a test-friendly way. @@ -102,7 +105,7 @@ def nt2json(*cli_args: LocalPath, **cli_kwargs: Union[str, LocalPath, Sequence[s return _run_app(NestedTextToJSON, *cli_args, **cli_kwargs) -def nt2yaml(*cli_args: LocalPath, **cli_kwargs: Union[str, LocalPath, Sequence[str]]) -> str: +def nt2yaml(*cli_args: LocalPath, **cli_kwargs: str | LocalPath | Sequence[str]) -> str: """ Invoke `NestedTextToYAML` in a test-friendly way. @@ -116,7 +119,7 @@ def nt2yaml(*cli_args: LocalPath, **cli_kwargs: Union[str, LocalPath, Sequence[s return _run_app(NestedTextToYAML, *cli_args, **cli_kwargs) -def yaml2nt(*cli_args: LocalPath, **cli_kwargs: Union[str, bool]) -> str: +def yaml2nt(*cli_args: LocalPath, **cli_kwargs: str | bool) -> str: """ Invoke `YAMLToNestedText` in a test-friendly way. @@ -130,7 +133,7 @@ def yaml2nt(*cli_args: LocalPath, **cli_kwargs: Union[str, bool]) -> str: return _run_app(YAMLToNestedText, *cli_args, **cli_kwargs) -def nt2toml(*cli_args: LocalPath, **cli_kwargs: Union[str, LocalPath, Sequence[str]]) -> str: +def nt2toml(*cli_args: LocalPath, **cli_kwargs: str | LocalPath | Sequence[str]) -> str: """ Invoke `NestedTextToTOML` in a test-friendly way. @@ -144,7 +147,7 @@ def nt2toml(*cli_args: LocalPath, **cli_kwargs: Union[str, LocalPath, Sequence[s return _run_app(NestedTextToTOML, *cli_args, **cli_kwargs) -def toml2nt(*cli_args: LocalPath, **cli_kwargs: Union[str, bool]) -> str: +def toml2nt(*cli_args: LocalPath, **cli_kwargs: str | bool) -> str: """ Invoke `TOMLToNestedText` in a test-friendly way. diff --git a/test/test_toml.py b/test/test_toml.py index 1f2ab29..36e090b 100644 --- a/test/test_toml.py +++ b/test/test_toml.py @@ -1,6 +1,8 @@ """Test TOML <-> NestedText.""" -from typing import Dict, Sequence, cast +from __future__ import annotations + +from typing import Sequence, cast from plumbum import LocalPath, local from ward import skip, test @@ -114,7 +116,7 @@ def _( def _( input_file: LocalPath = input_file, expected_file: LocalPath = expected_file, - casting_args: Dict[str, Sequence[str]] = casting_args, + casting_args: dict[str, Sequence[str]] = casting_args, ): output = nt2toml(input_file, **casting_args) assert_file_content(expected_file, output) diff --git a/test/utils.py b/test/utils.py index 98c004f..d608041 100644 --- a/test/utils.py +++ b/test/utils.py @@ -2,11 +2,14 @@ from __future__ import annotations -from pathlib import Path -from typing import Sequence, TextIO, cast +from typing import TYPE_CHECKING, Sequence, TextIO, cast + +if TYPE_CHECKING: + from pathlib import Path + + from plumbum import LocalPath from nestedtext import load as ntload -from plumbum import LocalPath from ward.expect import assert_equal