mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-19 09:53:47 +01:00
143 lines
5.8 KiB
Python
Executable File
143 lines
5.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (c) 2022 The Bitcoin Core developers
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
"""
|
|
Check for specified flake8 and mypy warnings in python files.
|
|
"""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
|
|
from importlib.metadata import metadata, PackageNotFoundError
|
|
|
|
# Customize mypy cache dir via environment variable
|
|
cache_dir = Path(__file__).parent.parent / ".mypy_cache"
|
|
os.environ["MYPY_CACHE_DIR"] = str(cache_dir)
|
|
|
|
DEPS = ['flake8', 'lief', 'mypy', 'pyzmq']
|
|
|
|
# All .py files, except those in src/ (to exclude subtrees there)
|
|
FLAKE_FILES_ARGS = ['git', 'ls-files', '*.py', ':!:src/*.py']
|
|
|
|
# Only .py files in test/functional and contrib/devtools have type annotations
|
|
# enforced.
|
|
MYPY_FILES_ARGS = ['git', 'ls-files', 'test/functional/*.py', 'contrib/devtools/*.py']
|
|
|
|
ENABLED = (
|
|
'E101,' # indentation contains mixed spaces and tabs
|
|
'E112,' # expected an indented block
|
|
'E113,' # unexpected indentation
|
|
'E115,' # expected an indented block (comment)
|
|
'E116,' # unexpected indentation (comment)
|
|
'E125,' # continuation line with same indent as next logical line
|
|
'E129,' # visually indented line with same indent as next logical line
|
|
'E131,' # continuation line unaligned for hanging indent
|
|
'E133,' # closing bracket is missing indentation
|
|
'E223,' # tab before operator
|
|
'E224,' # tab after operator
|
|
'E242,' # tab after ','
|
|
'E266,' # too many leading '#' for block comment
|
|
'E271,' # multiple spaces after keyword
|
|
'E272,' # multiple spaces before keyword
|
|
'E273,' # tab after keyword
|
|
'E274,' # tab before keyword
|
|
'E275,' # missing whitespace after keyword
|
|
'E304,' # blank lines found after function decorator
|
|
'E306,' # expected 1 blank line before a nested definition
|
|
'E401,' # multiple imports on one line
|
|
'E402,' # module level import not at top of file
|
|
'E502,' # the backslash is redundant between brackets
|
|
'E701,' # multiple statements on one line (colon)
|
|
'E702,' # multiple statements on one line (semicolon)
|
|
'E703,' # statement ends with a semicolon
|
|
'E711,' # comparison to None should be 'if cond is None:'
|
|
'E714,' # test for object identity should be "is not"
|
|
'E721,' # do not compare types, use "isinstance()"
|
|
'E722,' # do not use bare 'except'
|
|
'E742,' # do not define classes named "l", "O", or "I"
|
|
'E743,' # do not define functions named "l", "O", or "I"
|
|
'E901,' # SyntaxError: invalid syntax
|
|
'E902,' # TokenError: EOF in multi-line string
|
|
'F401,' # module imported but unused
|
|
'F402,' # import module from line N shadowed by loop variable
|
|
'F403,' # 'from foo_module import *' used; unable to detect undefined names
|
|
'F404,' # future import(s) name after other statements
|
|
'F405,' # foo_function may be undefined, or defined from star imports: bar_module
|
|
'F406,' # "from module import *" only allowed at module level
|
|
'F407,' # an undefined __future__ feature name was imported
|
|
'F601,' # dictionary key name repeated with different values
|
|
'F602,' # dictionary key variable name repeated with different values
|
|
'F621,' # too many expressions in an assignment with star-unpacking
|
|
'F622,' # two or more starred expressions in an assignment (a, *b, *c = d)
|
|
'F631,' # assertion test is a tuple, which are always True
|
|
'F632,' # use ==/!= to compare str, bytes, and int literals
|
|
'F701,' # a break statement outside of a while or for loop
|
|
'F702,' # a continue statement outside of a while or for loop
|
|
'F703,' # a continue statement in a finally block in a loop
|
|
'F704,' # a yield or yield from statement outside of a function
|
|
'F705,' # a return statement with arguments inside a generator
|
|
'F706,' # a return statement outside of a function/method
|
|
'F707,' # an except: block as not the last exception handler
|
|
'F811,' # redefinition of unused name from line N
|
|
'F812,' # list comprehension redefines 'foo' from line N
|
|
'F821,' # undefined name 'Foo'
|
|
'F822,' # undefined name name in __all__
|
|
'F823,' # local variable name … referenced before assignment
|
|
'F831,' # duplicate argument name in function definition
|
|
'F841,' # local variable 'foo' is assigned to but never used
|
|
'W191,' # indentation contains tabs
|
|
'W291,' # trailing whitespace
|
|
'W292,' # no newline at end of file
|
|
'W293,' # blank line contains whitespace
|
|
'W601,' # .has_key() is deprecated, use "in"
|
|
'W602,' # deprecated form of raising exception
|
|
'W603,' # "<>" is deprecated, use "!="
|
|
'W604,' # backticks are deprecated, use "repr()"
|
|
'W605,' # invalid escape sequence "x"
|
|
'W606,' # 'async' and 'await' are reserved keywords starting with Python 3.7
|
|
)
|
|
|
|
|
|
def check_dependencies():
|
|
for dep in DEPS:
|
|
try:
|
|
metadata(dep)
|
|
except PackageNotFoundError:
|
|
print(f"Skipping Python linting since {dep} is not installed.")
|
|
exit(0)
|
|
|
|
|
|
def main():
|
|
check_dependencies()
|
|
|
|
if len(sys.argv) > 1:
|
|
flake8_files = sys.argv[1:]
|
|
else:
|
|
flake8_files = subprocess.check_output(FLAKE_FILES_ARGS).decode("utf-8").splitlines()
|
|
|
|
flake8_args = ['flake8', '--ignore=B,C,E,F,I,N,W', f'--select={ENABLED}'] + flake8_files
|
|
flake8_env = os.environ.copy()
|
|
flake8_env["PYTHONWARNINGS"] = "ignore"
|
|
|
|
try:
|
|
subprocess.check_call(flake8_args, env=flake8_env)
|
|
except subprocess.CalledProcessError:
|
|
exit(1)
|
|
|
|
mypy_files = subprocess.check_output(MYPY_FILES_ARGS).decode("utf-8").splitlines()
|
|
mypy_args = ['mypy', '--show-error-codes'] + mypy_files
|
|
|
|
try:
|
|
subprocess.check_call(mypy_args)
|
|
except subprocess.CalledProcessError:
|
|
exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|