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
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 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
A plugin for Flake8 that provides specializations for type hinting stub files

flake8-pyi A plugin for Flake8 that provides specializations for type hinting stub files, especially interesting for linting typeshed. Functionality A

Łukasz Langa 58 Jan 04, 2023
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
Flake8 wrapper to make it nice, legacy-friendly, configurable.

THE PROJECT IS ARCHIVED Forks: https://github.com/orsinium/forks It's a Flake8 wrapper to make it cool. Lint md, rst, ipynb, and more. Shareable and r

Life4 232 Dec 16, 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
The mypy playground. Try mypy with your web browser.

mypy-playground The mypy playground provides Web UI to run mypy in the sandbox: Features Web UI and sandbox for running mypy eas

Yusuke Miyazaki 57 Jan 02, 2023
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
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
Mypy plugin and stubs for SQLAlchemy

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

Dropbox 521 Dec 29, 2022
❄️ 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
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
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
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
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
Custom Python linting through AST expressions

bellybutton bellybutton is a customizable, easy-to-configure linting engine for Python. What is this good for? Tools like pylint and flake8 provide, o

H. Chase Stevens 249 Dec 31, 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
Tool to check the completeness of MANIFEST.in for Python packages

check-manifest Are you a Python developer? Have you uploaded packages to the Python Package Index? Have you accidentally uploaded broken packages with

Marius Gedminas 270 Dec 26, 2022
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
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