Flake8 plugin to validate annotations complexity

Overview

flake8-annotations-complexity

Build Status Maintainability Test Coverage PyPI - Python Version

An extension for flake8 to report on too complex type annotations.

Complex type annotations often means bad annotations usage, wrong code decomposition or improper data structure choice. They are also hard to read and make code look java-like.

Annotation complexity is maximum annotation nesting level. So List[int] complexity is 2 and Tuple[List[Optional[str]], int] is 4.

Default max annotation complexity is 3 and can be configured via --max-annotations-complexity option.

Installation

pip install flake8-annotations-complexity

Example

Sample file:

# test.py

def foo() -> List[int]:
    return [1]

Usage:

$ flake8 --max-annotations-complexity=1 test.py
test.py:4:14: TAE002 too complex annotation (2 > 1)

Contributing

We would love you to contribute to our project. It's simple:

  1. Create an issue with bug you found or proposal you have. Wait for approve from maintainer.
  2. Create a pull request. Make sure all checks are green.
  3. Fix review comments if any.
  4. Be awesome.

Here are useful tips:

Comments
  • 0.0.6 release

    0.0.6 release

    Hi, thank you for this awesome plugin! Please consider creatng a new release that contains the changes introduced in the PR #14. Otherwise, the latest release on PyPI (0.0.5) raises on Python 3.9, forcing us to resort to install from source repo.

    Привет, ребята! Спасибо за отличный плагин к flake8! К сожалению, текущая версия на PyPI (0.0.5) кидает исключение в связке с Python 3.9 - фикс уже есть в основной ветке (PR #14). Прошу выпустить новую версию 0.0.6, содержащую этот фикс - пока приходится устанавливать плагин с мастер-ветки репозитория.

    enhancement 
    opened by hoefling 10
  • Variable annotations are ignored

    Variable annotations are ignored

    Here you only check function annotations: https://github.com/best-doctor/flake8-annotations-complexity/blob/master/flake8_annotations_complexity/ast_helpres.py#L17-L20

    However, it is still possible to use really complex variable annotations:

    some_value: List[Union[List[str], Dict[str, Dict[str, str]]]] = ...
    
    opened by sobolevn 2
  • Exception with Literal[

    Exception with Literal[""]

    An exception is thrown if running flake8 and it finds an empty string in a Literal definition.

    To Reproduce I wrote the test for it, so see that: https://github.com/best-doctor/flake8-annotations-complexity/pull/11

    Output:

    Traceback (most recent call last):
      File "/home/ubuntu/.local/bin/flake8", line 8, in <module>
        sys.exit(main())
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8/main/cli.py", line 22, in main
        app.run(argv)
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8/main/application.py", line 360, in run
        self._run(argv)
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8/main/application.py", line 348, in _run
        self.run_checks()
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8/main/application.py", line 262, in run_checks
        self.file_checker_manager.run()
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8/checker.py", line 334, in run
        self.run_serial()
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8/checker.py", line 318, in run_serial
        checker.run_checks()
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8/checker.py", line 598, in run_checks
        self.run_ast_checks()
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8/checker.py", line 505, in run_ast_checks
        for (line_number, offset, text, _) in runner:
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8_annotations_complexity/checker.py", line 23, in run
        too_difficult_annotations = validate_annotations_in_ast_node(
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8_annotations_complexity/ast_helpres.py", line 31, in validate_annotations_in_ast_node
        complexity = get_annotation_complexity(annotation)
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8_annotations_complexity/ast_helpres.py", line 12, in get_annotation_complexity
        return 1 + get_annotation_complexity(annotation_node.slice.value)  # type: ignore
      File "/home/ubuntu/.local/lib/python3.8/site-packages/flake8_annotations_complexity/ast_helpres.py", line 8, in get_annotation_complexity
        annotation_node = ast.parse(annotation_node.s).body[0].value  # type: ignore
    IndexError: list index out of range
    
    bug 
    opened by Dreamsorcerer 1
  • String expression in annotations

    String expression in annotations

    According PEP 3107 we can use any expression in annotations. Annotations like this:

    def foo() -> 'String Annontation':
        pass
    

    raises syntax error due to impossibility AST parsing this string:

    Traceback (most recent call last):
      File "/Users/andrey/.envs/test/bin/flake8", line 8, in <module>
        sys.exit(main())
      File "/Users/andrey/.envs/test/lib/python3.8/site-packages/flake8/main/cli.py", line 18, in main
        app.run(argv)
      File "/Users/andrey/.envs/test/lib/python3.8/site-packages/flake8/main/application.py", line 393, in run
        self._run(argv)
      File "/Users/andrey/.envs/test/lib/python3.8/site-packages/flake8/main/application.py", line 381, in _run
        self.run_checks()
      File "/Users/andrey/.envs/test/lib/python3.8/site-packages/flake8/main/application.py", line 300, in run_checks
        self.file_checker_manager.run()
      File "/Users/andrey/.envs/test/lib/python3.8/site-packages/flake8/checker.py", line 331, in run
        self.run_serial()
      File "/Users/andrey/.envs/test/lib/python3.8/site-packages/flake8/checker.py", line 315, in run_serial
        checker.run_checks()
      File "/Users/andrey/.envs/test/lib/python3.8/site-packages/flake8/checker.py", line 598, in run_checks
        self.run_ast_checks()
      File "/Users/andrey/.envs/test/lib/python3.8/site-packages/flake8/checker.py", line 502, in run_ast_checks
        for (line_number, offset, text, check) in runner:
      File "/Users/andrey/.envs/test/lib/python3.8/site-packages/flake8_annotations_complexity/checker.py", line 23, in run
        too_difficult_annotations = validate_annotations_in_ast_node(
      File "/Users/andrey/.envs/test/lib/python3.8/site-packages/flake8_annotations_complexity/ast_helpres.py", line 28, in validate_annotations_in_ast_node
        complexity = get_annotation_compexity(annotation)
      File "/Users/andrey/.envs/test/lib/python3.8/site-packages/flake8_annotations_complexity/ast_helpres.py", line 7, in get_annotation_compexity
        annotation_node = ast.parse(annotation_node.s).body[0].value  # type: ignore
      File "/Users/andrey/.pyenv/versions/3.8.0/lib/python3.8/ast.py", line 47, in parse
        return compile(source, filename, mode, flags,
      File "<unknown>", line 1
        String Annontation
               ^
    SyntaxError: invalid syntax
    
    opened by leksuss 1
  • fails with AttributeError: 'Slice' object has no attribute 'value'

    fails with AttributeError: 'Slice' object has no attribute 'value'

      File "/Users/ilebedev/.virtualenvs/amy/bin/flake8", line 8, in <module>
        sys.exit(main())
      File "/Users/ilebedev/.virtualenvs/amy/lib/python3.8/site-packages/flake8/main/cli.py", line 18, in main
        app.run(argv)
      File "/Users/ilebedev/.virtualenvs/amy/lib/python3.8/site-packages/flake8/main/application.py", line 393, in run
        self._run(argv)
      File "/Users/ilebedev/.virtualenvs/amy/lib/python3.8/site-packages/flake8/main/application.py", line 381, in _run
        self.run_checks()
      File "/Users/ilebedev/.virtualenvs/amy/lib/python3.8/site-packages/flake8/main/application.py", line 300, in run_checks
        self.file_checker_manager.run()
      File "/Users/ilebedev/.virtualenvs/amy/lib/python3.8/site-packages/flake8/checker.py", line 331, in run
        self.run_serial()
      File "/Users/ilebedev/.virtualenvs/amy/lib/python3.8/site-packages/flake8/checker.py", line 315, in run_serial
        checker.run_checks()
      File "/Users/ilebedev/.virtualenvs/amy/lib/python3.8/site-packages/flake8/checker.py", line 598, in run_checks
        self.run_ast_checks()
      File "/Users/ilebedev/.virtualenvs/amy/lib/python3.8/site-packages/flake8/checker.py", line 502, in run_ast_checks
        for (line_number, offset, text, check) in runner:
      File "/Users/ilebedev/.virtualenvs/amy/lib/python3.8/site-packages/flake8_annotations_complexity/checker.py", line 23, in run
        too_difficult_annotations = validate_annotations_in_ast_node(
      File "/Users/ilebedev/.virtualenvs/amy/lib/python3.8/site-packages/flake8_annotations_complexity/ast_helpres.py", line 28, in validate_annotations_in_ast_node
        complexity = get_annotation_compexity(annotation)
      File "/Users/ilebedev/.virtualenvs/amy/lib/python3.8/site-packages/flake8_annotations_complexity/ast_helpres.py", line 9, in get_annotation_compexity
        return 1 + get_annotation_compexity(annotation_node.slice.value)  # type: ignore
    AttributeError: 'Slice' object has no attribute 'value'
    

    Code:

    
    PROJECTS_INFO: Final[List[Tuple[int, str, str, str]]] = [
    ]
    
    opened by Melevir 1
  • Empty tuple annotation causes crash

    Empty tuple annotation causes crash

    I have some functions that deal with unions where one variant is the empty tuple. Per https://github.com/python/mypy/issues/4211, the way to represent the empty tuple type is typing.Tuple[()]. Using this annotation crashes flake8-annotations-complexity with the following traceback:

    Traceback (most recent call last):
      File "/Users/maxchase/.pyenv/versions/structured-data/bin/flake8", line 10, in <module>
        sys.exit(main())
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8/main/cli.py", line 18, in main
        app.run(argv)
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8/main/application.py", line 393, in run
        self._run(argv)
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8/main/application.py", line 381, in _run
        self.run_checks()
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8/main/application.py", line 300, in run_checks
        self.file_checker_manager.run()
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8/checker.py", line 331, in run
        self.run_serial()
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8/checker.py", line 315, in run_serial
        checker.run_checks()
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8/checker.py", line 598, in run_checks
        self.run_ast_checks()
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8/checker.py", line 502, in run_ast_checks
        for (line_number, offset, text, check) in runner:
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8_annotations_complexity/checker.py", line 25, in run
        self.max_annotations_complexity,
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8_annotations_complexity/ast_helpres.py", line 28, in validate_annotations_in_ast_node
        complexity = get_annotation_compexity(annotation)
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8_annotations_complexity/ast_helpres.py", line 9, in get_annotation_compexity
        return 1 + get_annotation_compexity(annotation_node.slice.value)  # type: ignore
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8_annotations_complexity/ast_helpres.py", line 11, in get_annotation_compexity
        return max(get_annotation_compexity(n) for n in annotation_node.elts)
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8_annotations_complexity/ast_helpres.py", line 11, in <genexpr>
        return max(get_annotation_compexity(n) for n in annotation_node.elts)
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8_annotations_complexity/ast_helpres.py", line 9, in get_annotation_compexity
        return 1 + get_annotation_compexity(annotation_node.slice.value)  # type: ignore
      File "/Users/maxchase/.pyenv/versions/3.7.4/envs/structured-data/lib/python3.7/site-packages/flake8_annotations_complexity/ast_helpres.py", line 11, in get_annotation_compexity
        return max(get_annotation_compexity(n) for n in annotation_node.elts)
    ValueError: max() arg is an empty sequence
    

    I can put in a PR for this.

    opened by mwchase 1
  • `if annotation_len > 7:`

    `if annotation_len > 7:`

    annotation_len compares with constant 7 and not max_annotation_len :( But max_annotation_len is used in error message formatting.

    https://github.com/best-doctor/flake8-annotations-complexity/blob/master/flake8_annotations_complexity/ast_helpers.py#L65

    bug 
    opened by abcdenis 0
  • Add max complexity configuration from file

    Add max complexity configuration from file

    Is your feature request related to a problem? Please describe. The only documented method for configuring the max complexity is a command line switch. It is difficult to enforce a common standard in a shared codebase, or ensure CI is using the correct settings this way.

    Describe the solution you'd like A configuration option I can put in pyproject.toml to ensure the setting I desire is respected for all callers of flake8 in my repo.

    Describe alternatives you've considered Do nothing. Easiest, but it doesn't address the issue.

    Additional context N/A

    enhancement 
    opened by rpdelaney 0
Releases(v0.0.6)
Owner
BestDoctor
Check out our tech stack: https://stackshare.io/bestdoctor/bestdoctor
BestDoctor
Easy saving and switching between multiple KDE configurations.

Konfsave Konfsave is a config manager. That is, it allows you to save, back up, and easily switch between different (per-user) system configurations.

42 Sep 25, 2022
MonkeyType as a pytest plugin.

MonkeyType as a pytest plugin.

Marius van Niekerk 36 Nov 24, 2022
Tools for improving Python imports

imptools Tools for improving Python imports. Installation pip3 install imptools Overview Detailed docs import_path Import a module from any path on th

Danijar Hafner 7 Aug 07, 2022
Mypy stubs for the PyQt5 framework

Mypy stubs for the PyQt5 framework This repository holds the stubs of the PyQt5 framework. It uses the stub files that are produced during compilation

62 Nov 22, 2022
Typed interface stubs for Pythonista iOS

Pythonista Stubs Stubs for the Pythonista iOS API. This allows for better error detection and IDE / editor autocomplete. Installation and Usage pip in

Harold Martin 12 Jul 14, 2020
It's not just a linter that annoys you!

README for Pylint - https://pylint.pycqa.org/ Professional support for pylint is available as part of the Tidelift Subscription. Tidelift gives softwa

Python Code Quality Authority 4.4k Jan 04, 2023
Mypy stubs, i.e., type information, for numpy, pandas and matplotlib

Mypy type stubs for NumPy, pandas, and Matplotlib This is a PEP-561-compliant stub-only package which provides type information for matplotlib, numpy

Predictive Analytics Lab 194 Dec 19, 2022
Stubs with type annotations for ordered-set Python library

ordered-set-stubs - stubs with type annotations for ordered-set Python library Archived - now type annotations are the part of the ordered-set library

Roman Inflianskas 2 Feb 06, 2020
The official GitHub mirror of https://gitlab.com/pycqa/flake8

Flake8 Flake8 is a wrapper around these tools: PyFlakes pycodestyle Ned Batchelder's McCabe script Flake8 runs all the tools by launching the single f

Python Code Quality Authority 2.6k Jan 03, 2023
docstring style checker

pydocstyle - docstring style checker pydocstyle is a static analysis tool for checking compliance with Python docstring conventions. pydocstyle suppor

Python Code Quality Authority 982 Jan 03, 2023
Utilities for pycharm code formatting (flake8 and black)

Pycharm External Tools Extentions to Pycharm code formatting tools. Currently supported are flake8 and black on a selected code block. Usage Flake8 [P

Haim Daniel 13 Nov 03, 2022
An extension for flake8 that forbids some imports statements in some modules.

flake8-obey-import-goat An extension for flake8 that forbids some imports statements in some modules. Important: this project is developed using DDD,

Ilya Lebedev 10 Nov 09, 2022
Rust like Option and Result types in Python

Option Rust-like Option and Result types in Python, slotted and fully typed. An Option type represents an optional value, every Option is either Some

45 Dec 13, 2022
mypy plugin to type check Kubernetes resources

kubernetes-typed mypy plugin to dynamically define types for Kubernetes objects. Features Type checking for Custom Resources Type checking forkubernet

Artem Yarmoliuk 16 Oct 10, 2022
:sparkles: Surface lint errors during code review

✨ Linty Fresh ✨ Keep your codebase sparkly clean with the power of LINT! Linty Fresh parses lint errors and report them back to GitHub as comments on

Lyft 183 Dec 18, 2022
Flake8 extension for enforcing trailing commas in python

Flake8 Extension to enforce better comma placement. Usage If you are using flake8 it's as easy as: pip install flake8-commas Now you can avoid those a

Python Code Quality Authority 127 Sep 03, 2022
Backport Python 3.8+ typing utils & add issubtype & more

typing-utils Backport Python3.8+ typing utils & issubtype & more Install API issubtype get_origin get_args get_type_hints Install pip install typi

10 Nov 09, 2022
Flake8 plugin for managing type-checking imports & forward references

flake8-type-checking Lets you know which imports to put in type-checking blocks. For the imports you've already defined inside type-checking blocks, i

snok 67 Dec 16, 2022
Static Typing for Python

Python static typing home. Contains the source for typing_extensions and the documentation. Also hosts a user help forum.

Python 1.3k Jan 06, 2023
flake8 plugin to run black for checking Python coding style

flake8-black Introduction This is an MIT licensed flake8 plugin for validating Python code style with the command line code formatting tool black. It

Peter Cock 146 Dec 15, 2022