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
A simple plugin that allows running mypy from PyCharm and navigate between errors

mypy-PyCharm-plugin The plugin provides a simple terminal to run fast mypy daemon from PyCharm with a single click or hotkey and easily navigate throu

Dropbox 301 Dec 09, 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
Enforce the same configuration across multiple projects

Nitpick Flake8 plugin to enforce the same tool configuration (flake8, isort, mypy, Pylint...) across multiple Python projects. Useful if you maintain

Augusto W. Andreoli 315 Dec 25, 2022
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
❄️ 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
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
Mylint - My really simple rendition of how a linter works.

mylint My really simple rendition of how a linter works. This original version was written for my AST article. Since then I've added tests and turned

Tushar Sadhwani 2 Dec 29, 2021
Flashcards - A flash card application with 2 optional command line arguments

Flashcards A flash card application with 2 optional command line arguments impor

Özgür Yildirim 2 Jul 15, 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
Flake8 plugin that checks import order against various Python Style Guides

flake8-import-order A flake8 and Pylama plugin that checks the ordering of your imports. It does not check anything else about the imports. Merely tha

Python Code Quality Authority 270 Nov 24, 2022
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
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
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
Design by contract for Python. Write bug-free code. Add a few decorators, get static analysis and tests for free.

A Python library for design by contract (DbC) and checking values, exceptions, and side-effects. In a nutshell, deal empowers you to write bug-free co

Life4 473 Dec 28, 2022
Flake8 plugin to validate annotations complexity

flake8-annotations-complexity An extension for flake8 to report on too complex type annotations. Complex type annotations often means bad annotations

BestDoctor 41 Dec 28, 2022
Type annotations builder for boto3 compatible with VSCode, PyCharm, Emacs, Sublime Text, pyright and mypy.

mypy_boto3_builder Type annotations builder for boto3-stubs project. Compatible with VSCode, PyCharm, Emacs, Sublime Text, mypy, pyright and other too

Vlad Emelianov 2 Dec 05, 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
A python documentation linter which checks that the docstring description matches the definition.

Darglint A functional docstring linter which checks whether a docstring's description matches the actual function/method implementation. Darglint expe

Terrence Reilly 463 Dec 31, 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
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