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
Naming Convention checker for Python

PEP 8 Naming Conventions Check your code against PEP 8 naming conventions. This module provides a plugin for flake8, the Python code checker. (It repl

Python Code Quality Authority 411 Dec 23, 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
flake8 plugin which checks that typing imports are properly guarded

flake8-typing-imports flake8 plugin which checks that typing imports are properly guarded installation pip install flake8-typing-imports flake8 codes

Anthony Sottile 50 Nov 01, 2022
An enhanced version of the Python typing library.

typingplus An enhanced version of the Python typing library that always uses the latest version of typing available, regardless of which version of Py

Contains 6 Mar 26, 2021
Plugin for mypy to support zope.interface

Plugin for mypy to support zope.interface The goal is to be able to make zope interfaces to be treated as types in mypy sense. Usage Install both mypy

Shoobx 36 Oct 29, 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
MonkeyType as a pytest plugin.

MonkeyType as a pytest plugin.

Marius van Niekerk 36 Nov 24, 2022
Flake8 Type Annotation Checking

flake8-annotations flake8-annotations is a plugin for Flake8 that detects the absence of PEP 3107-style function annotations and PEP 484-style type co

S. Co1 118 Jan 05, 2023
Automated security testing using bandit and flake8.

flake8-bandit Automated security testing built right into your workflow! You already use flake8 to lint all your code for errors, ensure docstrings ar

Tyler Wince 96 Jan 01, 2023
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
coala provides a unified command-line interface for linting and fixing all your code, regardless of the programming languages you use.

"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live." ― John F. Woods coala provides a

coala development group 3.4k Dec 29, 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
Tool to automatically fix some issues reported by flake8 (forked from autoflake).

autoflake8 Introduction autoflake8 removes unused imports and unused variables from Python code. It makes use of pyflakes to do this. autoflake8 also

francisco souza 27 Sep 08, 2022
A plugin for flake8 integrating Mypy.

flake8-mypy NOTE: THIS PROJECT IS DEAD It was created in early 2017 when Mypy performance was often insufficient for in-editor linting. The Flake8 plu

Łukasz Langa 103 Jun 23, 2022
A plugin for Flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle.

flake8-bugbear A plugin for Flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycode

Python Code Quality Authority 869 Dec 30, 2022
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
Silence mypy by adding or removing code comments

mypy-silent Automatically add or remove # type: ignore commends to silence mypy. Inspired by pylint-silent Why? Imagine you want to add type check for

Wu Haotian 8 Nov 30, 2022
Optional static typing for Python 3 and 2 (PEP 484)

Mypy: Optional Static Typing for Python Got a question? Join us on Gitter! We don't have a mailing list; but we are always happy to answer questions o

Python 14.4k Jan 08, 2023
❄️ A flake8 plugin to help you write better list/set/dict comprehensions.

flake8-comprehensions A flake8 plugin that helps you write better list/set/dict comprehensions. Requirements Python 3.6 to 3.9 supported. Installation

Adam Johnson 398 Dec 23, 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