Skip to content

Commit

Permalink
Move parsing into parse subcommand (introduce argparse) (#25)
Browse files Browse the repository at this point in the history
* Split logic from entrypoint

* Break calculation into separate statement

* Isolate calculation from serialization

* Rename produce_errors_report->parse_errors_report

"Parse" is a better reflection of what this function does.

* Require "parse" subcommand when calling

We intend to add more functionality to this tool, so it no longer makes
sense to have the error parsing functionality at the top level when
calling the script.

This change introduces (and requires) a new "parse" subcommand, under
which the existing parsing function is moved.

Because we're using argparse to do this, we also get a new "--help" flag
on the command.

* Use better typing for command handlers

Co-authored-by: Jeppe Fihl-Pearson <[email protected]>

* Update docstring

The summary returned is no longer JSON.

Co-authored-by: Jeppe Fihl-Pearson <[email protected]>

* Let argparse source the CLI arguments

There's no point in passing in the defaults.

Co-authored-by: Jeppe Fihl-Pearson <[email protected]>

* Add --indentation to vary spacing in JSON report

Some environments may want control over this to make working with
existing style-guides easier.

Co-authored-by: Jeppe Fihl-Pearson <[email protected]>

---------

Co-authored-by: Jeppe Fihl-Pearson <[email protected]>
  • Loading branch information
meshy and Tenzer authored Feb 3, 2023
1 parent 9e16478 commit 0328ce0
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 12 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- *Action required:* Move existing behaviour under "parse" subcommand.
Invocations of `mypy-json-report` should now be replaced with `mypy-json-report parse`.
- Add `parse --indentation` flag to grant control over how much indentation is used in the JSON report.
- Use GA version of Python 3.11 in test matrix.

## v0.1.3 [2022-09-07]
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Pipe the output of mypy through the `mypy-json-report` CLI app.
Store the output to a file, and commit it to your git repo.

```
mypy . --strict | mypy-json-report > known-mypy-errors.json
mypy . --strict | mypy-json-report parse > known-mypy-errors.json
git add known-mypy-errors.json
git commit -m "Add mypy errors lockfile"
```
Expand Down Expand Up @@ -80,7 +80,7 @@ jobs:
- name: Run mypy
run: |
mypy . --strict | mypy-json-report > known-mypy-errors.json
mypy . --strict | mypy-json-report parse > known-mypy-errors.json
- name: Check for mypy changes
run: |
Expand Down
56 changes: 52 additions & 4 deletions mypy_json_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,63 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import enum
import json
import sys
from collections import Counter, defaultdict
from dataclasses import dataclass
from typing import Counter as CounterType, Dict, Iterator


class ErrorCodes(enum.IntEnum):
DEPRECATED = 1


def main() -> None:
print(produce_errors_report(sys.stdin))
"""
The primary entrypoint of the program.
Parses the CLI flags, and delegates to other functions as appropriate.
For details of how to invoke the program, call it with `--help`.
"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(title="subcommand")

parser.set_defaults(func=_no_command)

parse_parser = subparsers.add_parser(
"parse", help="Transform Mypy output into JSON."
)
parse_parser.add_argument(
"-i",
"--indentation",
type=int,
default=2,
help="Number of spaces to indent JSON output.",
)

parse_parser.set_defaults(func=_parse_command)

parsed = parser.parse_args()
parsed.func(parsed)


def _parse_command(args: argparse.Namespace) -> None:
"""Handle the `parse` command."""
errors = parse_errors_report(sys.stdin)
error_json = json.dumps(errors, sort_keys=True, indent=args.indentation)
print(error_json)


def _no_command(args: argparse.Namespace) -> None:
"""
Handle the lack of an explicit command.
This will be hit when the program is called without arguments.
"""
print("A subcommand is required. Pass --help for usage info.")
sys.exit(ErrorCodes.DEPRECATED)


@dataclass(frozen=True)
Expand All @@ -29,12 +77,12 @@ class MypyError:
message: str


def produce_errors_report(input_lines: Iterator[str]) -> str:
"""Given lines from mypy's output, return a JSON summary of error frequencies by file."""
def parse_errors_report(input_lines: Iterator[str]) -> Dict[str, Dict[str, int]]:
"""Given lines from mypy's output, return a summary of error frequencies by file."""
errors = _extract_errors(input_lines)
error_frequencies = _count_errors(errors)
structured_errors = _structure_errors(error_frequencies)
return json.dumps(structured_errors, sort_keys=True, indent=2)
return structured_errors


def _extract_errors(lines: Iterator[str]) -> Iterator[MypyError]:
Expand Down
9 changes: 4 additions & 5 deletions tests/test_mypy_json_report.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import json
from io import StringIO

from mypy_json_report import produce_errors_report
from mypy_json_report import parse_errors_report


EXAMPLE_MYPY_STDOUT = """\
Expand All @@ -11,10 +10,10 @@
Found 2 errors in 1 file (checked 3 source files)"""


def test_report() -> None:
report = produce_errors_report(StringIO(EXAMPLE_MYPY_STDOUT))
def test_parse_errors_report() -> None:
report = parse_errors_report(StringIO(EXAMPLE_MYPY_STDOUT))

assert json.loads(report) == {
assert report == {
"mypy_json_report.py": {
'Call to untyped function "main" in typed context': 1,
"Function is missing a return type annotation": 1,
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ allowlist_externals =
commands_pre =
poetry install
commands =
poetry run bash -c "mypy . --strict | mypy-json-report > known-mypy-errors.json"
poetry run bash -c "mypy . --strict | mypy-json-report parse > known-mypy-errors.json"
git diff --exit-code known-mypy-errors.json

0 comments on commit 0328ce0

Please sign in to comment.