Skip to content

Commit

Permalink
Add flake8-annotations linting and hideous 3.7-compatible type hints
Browse files Browse the repository at this point in the history
  • Loading branch information
AndydeCleyre committed Sep 17, 2024
1 parent 7cfe5de commit 995a7c7
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 53 deletions.
22 changes: 12 additions & 10 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Tasks using Python environments."""

from pathlib import Path
from typing import cast

import nox
from nox.sessions import Session

# TODO: if/when Python 3.7 support is dropped, prefer uv to venv backend
nox.options.default_venv_backend = 'venv'
Expand All @@ -17,7 +19,7 @@


@nox.session(python=ALL_PYTHONS)
def test(session):
def test(session: Session):
"""Run all tests."""
session.install('-U', 'pip')
session.install('-U', '.[test,toml]', 'coverage')
Expand All @@ -26,7 +28,7 @@ def test(session):


@nox.session(python=ALL_PYTHONS)
def test_without_toml(session):
def test_without_toml(session: Session):
"""Run tests without optional TOML support installed."""
session.install('-U', 'pip')
session.install('-U', '.[test-without-toml]', 'coverage')
Expand All @@ -35,15 +37,15 @@ def test_without_toml(session):


@nox.session(python=[DEFAULT_PYTHON])
def combine_coverage(session):
def combine_coverage(session: Session):
"""Prepare a combined coverage report for uploading."""
session.install('-U', 'coverage')
session.run('coverage', 'combine')
session.run('coverage', 'json')


@nox.session(python=[DEFAULT_PYTHON])
def fmt(session):
def fmt(session: Session):
"""Format and lint code and docs."""
session.install('-r', 'fmt-requirements.txt')
session.run('darglint', 'nt2', 'test')
Expand All @@ -52,40 +54,40 @@ def fmt(session):


@nox.session(python=[DEFAULT_PYTHON])
def publish(session):
def publish(session: Session):
"""Package and upload to PyPI."""
session.install('-U', '.[dev]')
session.run('flit', 'publish')


@nox.session(python=[DEFAULT_PYTHON])
def typecheck(session):
def typecheck(session: Session):
"""Check types."""
session.install('-U', '.[dev]')
session.run('sh', '-c', 'pyright --outputjson 2>/dev/null | json2nt', external=True)
session.run('pyright', '--warnings')


@nox.session(python=[DEFAULT_PYTHON])
def render_readme(session):
def render_readme(session: Session):
"""Generate README.md from templates/README.md.wz."""
session.install('-Ue', '.[doc]')
content = session.run('wheezy.template', 'templates/README.md.wz', silent=True)
Path('README.md').write_text(content)
Path('README.md').write_text(cast(str, content))
session.run(
'md_toc', '--in-place', '--skip-lines', '2', 'github', '--header-levels', '4', 'README.md'
)


@nox.session(python=[DEFAULT_PYTHON])
def render_api_docs(session):
def render_api_docs(session: Session):
"""Generate doc/api HTML documentation from docstrings."""
session.install('-r', 'doc/doc-requirements.txt')
session.run('pydoctor', 'nt2')


@nox.session(python=[DEFAULT_PYTHON])
def lock(session):
def lock(session: Session):
"""Generate updated requirements.txt lock files and pyproject.toml."""
session.install('-U', 'uv')
for reqsfile in (
Expand Down
3 changes: 1 addition & 2 deletions nt2/casters.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
from __future__ import annotations

import re
from collections.abc import Sequence
from datetime import date, datetime, time
from typing import cast
from typing import Sequence, cast
from uuid import uuid4

try:
Expand Down
25 changes: 20 additions & 5 deletions nt2/dumpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
import sys
from json import dump as _jdump, dumps as _jdumps, loads as _jloads
from json.decoder import JSONDecodeError
from pathlib import Path
from textwrap import indent
from typing import Any, cast
from typing import Sequence, TextIO, cast

from nestedtext import dump as _ntdump, dumps as _ntdumps, load as _ntload
from plumbum import LocalPath
Expand Down Expand Up @@ -52,7 +53,7 @@ def _syntax_print(content: str, syntax: str, console: RichConsole = RICH):
)


def ntload(file: Any) -> StringyData:
def ntload(file: str | Path | TextIO) -> StringyData:
r"""
Wrap ``nestedtext.load`` with convenient configuration for this module.
Expand Down Expand Up @@ -302,7 +303,11 @@ def dump_toml_to_nestedtext(*input_files: LocalPath):


def dump_nestedtext_to_yaml(
*input_files: LocalPath, bool_paths=(), null_paths=(), num_paths=(), date_paths=()
*input_files: LocalPath,
bool_paths: Sequence[str] = (),
null_paths: Sequence[str] = (),
num_paths: Sequence[str] = (),
date_paths: Sequence[str] = (),
):
r"""
Read NestedText from stdin or ``input_files``, and send up-typed YAML to stdout.
Expand All @@ -327,7 +332,12 @@ def dump_nestedtext_to_yaml(
ydump(data)


def dump_nestedtext_to_toml(*input_files: LocalPath, bool_paths=(), num_paths=(), date_paths=()):
def dump_nestedtext_to_toml(
*input_files: LocalPath,
bool_paths: Sequence[str] = (),
num_paths: Sequence[str] = (),
date_paths: Sequence[str] = (),
):
r"""
Read NestedText from stdin or ``input_files``, and send up-typed TOML to stdout.
Expand All @@ -353,7 +363,12 @@ def dump_nestedtext_to_toml(*input_files: LocalPath, bool_paths=(), num_paths=()
tdump(data)


def dump_nestedtext_to_json(*input_files: LocalPath, bool_paths=(), null_paths=(), num_paths=()):
def dump_nestedtext_to_json(
*input_files: LocalPath,
bool_paths: Sequence[str] = (),
null_paths: Sequence[str] = (),
num_paths: Sequence[str] = (),
):
r"""
Read NestedText from stdin or ``input_files``, and send up-typed JSON to stdout.
Expand Down
12 changes: 6 additions & 6 deletions nt2/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,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
def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101
try:
for schema_file in cast(list, self.schema_files):
schema = cast(dict, ntload(schema_file))
Expand Down Expand Up @@ -176,7 +176,7 @@ class NestedTextToYAML(
nt2yaml --int People.age --boolean 'People."is a wizard"' example.nt
"""

def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102
def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101
try:
for schema_file in cast(list, self.schema_files):
schema = cast(dict, ntload(schema_file))
Expand Down Expand Up @@ -213,7 +213,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
def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101
try:
for schema_file in cast(list, self.schema_files):
schema = cast(dict, ntload(schema_file))
Expand Down Expand Up @@ -242,7 +242,7 @@ class JSONToNestedText(_TypedFormatToSchema):
cat example.json | json2nt
"""

def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102
def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101
try:
if not self.to_schema:
dump_json_to_nestedtext(*input_files)
Expand All @@ -263,7 +263,7 @@ class YAMLToNestedText(_TypedFormatToSchema):
cat example.yml | yaml2nt
"""

def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102
def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101
try:
if not self.to_schema:
dump_yaml_to_nestedtext(*input_files)
Expand All @@ -284,7 +284,7 @@ class TOMLToNestedText(_TypedFormatToSchema):
cat example.yml | toml2nt
"""

def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102
def main(self, *input_files: ExistingFile): # type: ignore # noqa: D102,ANN201,ANN101
try:
if not self.to_schema:
dump_toml_to_nestedtext(*input_files)
Expand Down
4 changes: 2 additions & 2 deletions nt2/yamlpath_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

import sys
from collections import defaultdict
from collections.abc import Iterable
from datetime import date, datetime, time
from types import SimpleNamespace
from typing import Iterable

try:
from types import NoneType
Expand Down Expand Up @@ -73,7 +73,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):
def _schema_entry_type(obj: int | float | bool | None | datetime | date | time) -> str:
# -> Literal['number', 'boolean', 'null', 'date']
if isinstance(obj, bool):
return 'boolean'
Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ quote-style = "preserve"
skip-magic-trailing-comma = true

[tool.ruff.lint]
select = ["F", "E", "W", "C90", "I", "N", "D", "UP", "YTT", "SIM"]
select = ["F", "E", "W", "C90", "I", "N", "D", "UP", "YTT", "ANN", "SIM"]

[tool.ruff.lint.flake8-annotations]
suppress-none-returning = true

[tool.ruff.lint.isort]
combine-as-imports = true
Expand Down
21 changes: 13 additions & 8 deletions test/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@

import io
import sys
from typing import cast
from typing import Sequence, Union, cast

try:
from typing import TypeAlias
except ImportError:
from typing import Any as TypeAlias

from plumbum import LocalPath
from plumbum.cli import Application as _Application

from nt2.ui import (
Expand All @@ -39,7 +40,11 @@
YAMLToNestedText = cast(Application, _YAMLToNestedText)


def _run_app(app_class: Application, *cli_args, **cli_kwargs) -> str:
def _run_app(
app_class: Application,
*cli_args: LocalPath,
**cli_kwargs: Union[str, LocalPath, Sequence[str], bool],
) -> str:
"""
Invoke `app_class` with given flags, and return stdout content.
Expand Down Expand Up @@ -69,7 +74,7 @@ def _run_app(app_class: Application, *cli_args, **cli_kwargs) -> str:
return output


def json2nt(*cli_args, **cli_kwargs) -> str:
def json2nt(*cli_args: LocalPath, **cli_kwargs: Union[str, bool]) -> str:
"""
Invoke `JSONToNestedText` in a test-friendly way.
Expand All @@ -83,7 +88,7 @@ def json2nt(*cli_args, **cli_kwargs) -> str:
return _run_app(JSONToNestedText, *cli_args, **cli_kwargs)


def nt2json(*cli_args, **cli_kwargs) -> str:
def nt2json(*cli_args: LocalPath, **cli_kwargs: Union[str, LocalPath, Sequence[str]]) -> str:
"""
Invoke `NestedTextToJSON` in a test-friendly way.
Expand All @@ -97,7 +102,7 @@ def nt2json(*cli_args, **cli_kwargs) -> str:
return _run_app(NestedTextToJSON, *cli_args, **cli_kwargs)


def nt2yaml(*cli_args, **cli_kwargs) -> str:
def nt2yaml(*cli_args: LocalPath, **cli_kwargs: Union[str, LocalPath, Sequence[str]]) -> str:
"""
Invoke `NestedTextToYAML` in a test-friendly way.
Expand All @@ -111,7 +116,7 @@ def nt2yaml(*cli_args, **cli_kwargs) -> str:
return _run_app(NestedTextToYAML, *cli_args, **cli_kwargs)


def yaml2nt(*cli_args, **cli_kwargs) -> str:
def yaml2nt(*cli_args: LocalPath, **cli_kwargs: Union[str, bool]) -> str:
"""
Invoke `YAMLToNestedText` in a test-friendly way.
Expand All @@ -125,7 +130,7 @@ def yaml2nt(*cli_args, **cli_kwargs) -> str:
return _run_app(YAMLToNestedText, *cli_args, **cli_kwargs)


def nt2toml(*cli_args, **cli_kwargs) -> str:
def nt2toml(*cli_args: LocalPath, **cli_kwargs: Union[str, LocalPath, Sequence[str]]) -> str:
"""
Invoke `NestedTextToTOML` in a test-friendly way.
Expand All @@ -139,7 +144,7 @@ def nt2toml(*cli_args, **cli_kwargs) -> str:
return _run_app(NestedTextToTOML, *cli_args, **cli_kwargs)


def toml2nt(*cli_args, **cli_kwargs) -> str:
def toml2nt(*cli_args: LocalPath, **cli_kwargs: Union[str, bool]) -> str:
"""
Invoke `TOMLToNestedText` in a test-friendly way.
Expand Down
8 changes: 4 additions & 4 deletions test/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .commands import json2nt, nt2json
from .utils import assert_file_content, casting_args_from_schema_file

SAMPLES = local.path(__file__).up() / 'samples' / 'json' # type: ignore
SAMPLES = local.path(__file__).up() / 'samples' / 'json'


@test("JSON Lines -> NestedText")
Expand All @@ -24,7 +24,7 @@ def _():
}.items():

@test(f"JSON -> NestedText [{input_json_name}]")
def _(input_json_name=input_json_name, output_nt_name=output_nt_name):
def _(input_json_name: str = input_json_name, output_nt_name: str = output_nt_name):
expected_file = SAMPLES / f"{output_nt_name}.nt"
output = json2nt(SAMPLES / f"{input_json_name}.json")
assert_file_content(expected_file, output)
Expand All @@ -47,13 +47,13 @@ def _():
for schema_file in SAMPLES // 'base.*.types.nt':

@test(f"NestedText -> JSON [schema file: {schema_file.name}]")
def _(schema_file=schema_file):
def _(schema_file: LocalPath = schema_file):
expected_file = SAMPLES / f"typed_{schema_file.name.split('.')[1]}.json"
output = nt2json(SAMPLES / 'base.nt', schema_files=(schema_file,))
assert_file_content(expected_file, output)

@test(f"NestedText -> JSON [casting args from schema: {schema_file.name}]")
def _(schema_file=schema_file):
def _(schema_file: LocalPath = schema_file):
casting_args = casting_args_from_schema_file(schema_file)
expected_file = SAMPLES / f"typed_{schema_file.name.split('.')[1]}.json"
output = nt2json(SAMPLES / 'base.nt', **casting_args)
Expand Down
Loading

0 comments on commit 995a7c7

Please sign in to comment.