From bf33de8e5053c9be1c1477d4257e2af6ef5ba3ba Mon Sep 17 00:00:00 2001 From: David Hagen Date: Tue, 26 Nov 2024 13:45:21 -0500 Subject: [PATCH 1/2] Run mypy on examples --- examples/url.py | 6 +++--- noxfile.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/url.py b/examples/url.py index dddd0c4..6cb2f4f 100644 --- a/examples/url.py +++ b/examples/url.py @@ -7,9 +7,9 @@ # This covers a typical URL schema, not the crazy one specified by https://tools.ietf.org/html/rfc3986 # In particular, this doesn't handle Unicode -UserInfo = namedtuple("Userinfo", ["username", "password"]) +UserInfo = namedtuple("UserInfo", ["username", "password"]) DomainName = namedtuple("DomainName", ["domains"]) -Url = namedtuple("Uri", ["scheme", "user_info", "host", "port", "path", "query", "fragment"]) +Url = namedtuple("Url", ["scheme", "user_info", "host", "port", "path", "query", "fragment"]) class TypicalUrlParsers(ParserContext): @@ -43,7 +43,7 @@ class TypicalUrlParsers(ParserContext): query_as_is = reg(r"[*-._A-Za-z0-9]+") query_space = lit("+") > constant(" ") query_string = rep1(query_as_is | query_space | encoded) > "".join - query = repsep(query_string << "=" & query_string, "&") > OrderedDict + query = repsep(query_string << "=" & query_string, "&") > OrderedDict # type: ignore fragment = reg(r"[-._~/?A-Za-z0-9]*") diff --git a/noxfile.py b/noxfile.py index ed5fed8..e00e204 100644 --- a/noxfile.py +++ b/noxfile.py @@ -28,7 +28,7 @@ def lint(s: Session, command: list[str]): @session(venv_backend="none") def type_check(s: Session): - s.run("mypy", "src") + s.run("mypy", "src", "examples") @session(venv_backend="none") From 32600029d415136cf93fe7a7baa071893f8b4b7d Mon Sep 17 00:00:00 2001 From: David Hagen Date: Tue, 26 Nov 2024 13:57:56 -0500 Subject: [PATCH 2/2] Re-export all public members for mypy --- pyproject.toml | 5 +-- src/parsita/__init__.py | 57 ++++++++++++++++--------------- src/parsita/parsers/__init__.py | 59 +++++++++++++++++++++++---------- src/parsita/state/__init__.py | 19 ++++++++--- 4 files changed, 90 insertions(+), 50 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 932aa05..14beb5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,6 +83,7 @@ extend-select = [ # F821: undefined-name; Parsita triggers this, but code coverage will catch it # N805: invalid-first-argument-name-for-method; Parsita tests and examples define functions in class bodies extend-ignore = ["F821", "N805"] +isort.combine-as-imports = true [tool.ruff.lint.per-file-ignores] # F401: unused-import; Allow unused imports in __init__.py files @@ -91,8 +92,8 @@ extend-ignore = ["F821", "N805"] [tool.mypy] strict = true -implicit_reexport = true - +# name-defined; Parsita triggers this, but code coverage will catch it +disable_error_code = ["name-defined"] [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/src/parsita/__init__.py b/src/parsita/__init__.py index d59a255..a61cc73 100644 --- a/src/parsita/__init__.py +++ b/src/parsita/__init__.py @@ -1,30 +1,35 @@ -from .metaclasses import ParserContext, fwd +# Use `as` to mark names as re-exports from submodules for mypy. +# Remove this from all re-exports whenever mypy supports a less verbose solution +# (https://github.com/python/mypy/issues/10198) +# or Ruff adds adds an equivalent lint +# (https://github.com/astral-sh/ruff/issues/13507) +from .metaclasses import ParserContext as ParserContext, fwd as fwd from .parsers import ( - Parser, - any1, - debug, - eof, - failure, - first, - lit, - longest, - opt, - pred, - reg, - rep, - rep1, - rep1sep, - repsep, - success, - until, + Parser as Parser, + any1 as any1, + debug as debug, + eof as eof, + failure as failure, + first as first, + lit as lit, + longest as longest, + opt as opt, + pred as pred, + reg as reg, + rep as rep, + rep1 as rep1, + rep1sep as rep1sep, + repsep as repsep, + success as success, + until as until, ) from .state import ( - Failure, - ParseError, - Reader, - RecursionError, - Result, - SequenceReader, - StringReader, - Success, + Failure as Failure, + ParseError as ParseError, + Reader as Reader, + RecursionError as RecursionError, + Result as Result, + SequenceReader as SequenceReader, + StringReader as StringReader, + Success as Success, ) diff --git a/src/parsita/parsers/__init__.py b/src/parsita/parsers/__init__.py index 7b30c0e..2753041 100644 --- a/src/parsita/parsers/__init__.py +++ b/src/parsita/parsers/__init__.py @@ -1,20 +1,43 @@ -from ._alternative import FirstAlternativeParser, LongestAlternativeParser, first, longest -from ._any import AnyParser, any1 -from ._base import Parser, wrap_literal -from ._conversion import ConversionParser, TransformationParser -from ._debug import DebugParser, debug -from ._end_of_source import EndOfSourceParser, eof -from ._literal import LiteralParser, lit -from ._optional import OptionalParser, opt -from ._predicate import PredicateParser, pred -from ._regex import RegexParser, reg -from ._repeated import RepeatedOnceParser, RepeatedParser, rep, rep1 +# Use `as` to mark names as re-exports from submodules for mypy. +from ._alternative import ( + FirstAlternativeParser as FirstAlternativeParser, + LongestAlternativeParser as LongestAlternativeParser, + first as first, + longest as longest, +) +from ._any import AnyParser as AnyParser, any1 as any1 +from ._base import Parser as Parser, wrap_literal as wrap_literal +from ._conversion import ( + ConversionParser as ConversionParser, + TransformationParser as TransformationParser, +) +from ._debug import DebugParser as DebugParser, debug as debug +from ._end_of_source import EndOfSourceParser as EndOfSourceParser, eof as eof +from ._literal import LiteralParser as LiteralParser, lit as lit +from ._optional import OptionalParser as OptionalParser, opt as opt +from ._predicate import PredicateParser as PredicateParser, pred as pred +from ._regex import RegexParser as RegexParser, reg as reg +from ._repeated import ( + RepeatedOnceParser as RepeatedOnceParser, + RepeatedParser as RepeatedParser, + rep as rep, + rep1 as rep1, +) from ._repeated_seperated import ( - RepeatedOnceSeparatedParser, - RepeatedSeparatedParser, - rep1sep, - repsep, + RepeatedOnceSeparatedParser as RepeatedOnceSeparatedParser, + RepeatedSeparatedParser as RepeatedSeparatedParser, + rep1sep as rep1sep, + repsep as repsep, +) +from ._sequential import ( + DiscardLeftParser as DiscardLeftParser, + DiscardRightParser as DiscardRightParser, + SequentialParser as SequentialParser, +) +from ._success import ( + FailureParser as FailureParser, + SuccessParser as SuccessParser, + failure as failure, + success as success, ) -from ._sequential import DiscardLeftParser, DiscardRightParser, SequentialParser -from ._success import FailureParser, SuccessParser, failure, success -from ._until import UntilParser, until +from ._until import UntilParser as UntilParser, until as until diff --git a/src/parsita/state/__init__.py b/src/parsita/state/__init__.py index 69587f1..d23d194 100644 --- a/src/parsita/state/__init__.py +++ b/src/parsita/state/__init__.py @@ -1,4 +1,15 @@ -from ._exceptions import ParseError, RecursionError -from ._reader import Reader, SequenceReader, StringReader -from ._result import Failure, Result, Success -from ._state import Continue, Element, Input, Output, State +# Use `as` to mark names as re-exports from submodules for mypy. +from ._exceptions import ParseError as ParseError, RecursionError as RecursionError +from ._reader import ( + Reader as Reader, + SequenceReader as SequenceReader, + StringReader as StringReader, +) +from ._result import Failure as Failure, Result as Result, Success as Success +from ._state import ( + Continue as Continue, + Element as Element, + Input as Input, + Output as Output, + State as State, +)