mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 20:42:59 +01:00
ab430bdb81
1ef2138c0db3bd4f9332c777fa3fb2770dc1b08c lint: run mypy over contrib/devtools (fanquake) Pull request description: wumpus mentioned on IRC that we don't currently run `mypy` over the `contrib/devtools` directory, and that it would likely be worthwhile given #20434. This just adds that dir to the linter, as well as some missing annotations to fix existing errors. Note that now we require Python 3.6 we can make use of variable annotations. master (patched to check contrib devtools): ```bash test/lint/lint-python.sh contrib/devtools/symbol-check.py:154: error: Incompatible types in assignment (expression has type "List[str]", variable has type "str") contrib/devtools/circular-dependencies.py:35: error: Need type annotation for 'deps' (hint: "deps: Dict[<type>, <type>] = ...") contrib/devtools/circular-dependencies.py:67: error: Need type annotation for 'closure' (hint: "closure: Dict[<type>, <type>] = ...") Found 4 errors in 3 files (checked 187 source files) ``` I haven't quite gone as far as to add annotations like ```python CHECKS: Dict[str, List[Tuple[str, Callable[[Any], bool]]]] = {... ``` to `symbol-check.py`. ACKs for top commit: laanwj: ACK 1ef2138c0db3bd4f9332c777fa3fb2770dc1b08c Tree-SHA512: a58c2ece588c640289dc1d35dad5b1b8732788272daa0965d6bf44ee8a7f7c8e8585f94d233ac41c84b9ffcfc97841a00fe2c9acba41f58fd164f01de4b6512b
125 lines
3.9 KiB
Python
Executable File
125 lines
3.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2018-2019 The Bitcoin Core developers
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
import sys
|
|
import re
|
|
from multiprocess import Pool
|
|
from typing import Dict, List, Set
|
|
|
|
MAPPING = {
|
|
'core_read.cpp': 'core_io.cpp',
|
|
'core_write.cpp': 'core_io.cpp',
|
|
}
|
|
|
|
# Directories with header-based modules, where the assumption that .cpp files
|
|
# define functions and variables declared in corresponding .h files is
|
|
# incorrect.
|
|
HEADER_MODULE_PATHS = [
|
|
'interfaces/'
|
|
]
|
|
|
|
def module_name(path):
|
|
if path in MAPPING:
|
|
path = MAPPING[path]
|
|
if any(path.startswith(dirpath) for dirpath in HEADER_MODULE_PATHS):
|
|
return path
|
|
if path.endswith(".h"):
|
|
return path[:-2]
|
|
if path.endswith(".c"):
|
|
return path[:-2]
|
|
if path.endswith(".cpp"):
|
|
return path[:-4]
|
|
return None
|
|
|
|
if __name__=="__main__":
|
|
files = dict()
|
|
deps: Dict[str, Set[str]] = dict()
|
|
|
|
RE = re.compile("^#include <(.*)>")
|
|
|
|
def handle_module(arg):
|
|
module = module_name(arg)
|
|
if module is None:
|
|
print("Ignoring file %s (does not constitute module)\n" % arg)
|
|
else:
|
|
files[arg] = module
|
|
deps[module] = set()
|
|
|
|
def handle_module2(module):
|
|
# Build the transitive closure of dependencies of module
|
|
closure: Dict[str, List[str]] = dict()
|
|
for dep in deps[module]:
|
|
closure[dep] = []
|
|
while True:
|
|
old_size = len(closure)
|
|
old_closure_keys = sorted(closure.keys())
|
|
for src in old_closure_keys:
|
|
for dep in deps[src]:
|
|
if dep not in closure:
|
|
closure[dep] = closure[src] + [src]
|
|
if len(closure) == old_size:
|
|
break
|
|
# If module is in its own transitive closure, it's a circular dependency; check if it is the shortest
|
|
if module in closure:
|
|
return [module] + closure[module]
|
|
|
|
return None
|
|
|
|
|
|
# Iterate over files, and create list of modules
|
|
for arg in sys.argv[1:]:
|
|
handle_module(arg)
|
|
|
|
def build_list_direct(arg):
|
|
module = files[arg]
|
|
with open(arg, 'r', encoding="utf8") as f:
|
|
for line in f:
|
|
match = RE.match(line)
|
|
if match:
|
|
include = match.group(1)
|
|
included_module = module_name(include)
|
|
if included_module is not None and included_module in deps and included_module != module:
|
|
deps[module].add(included_module)
|
|
|
|
|
|
# Iterate again, and build list of direct dependencies for each module
|
|
# TODO: implement support for multiple include directories
|
|
for arg in sorted(files.keys()):
|
|
build_list_direct(arg)
|
|
# Loop to find the shortest (remaining) circular dependency
|
|
|
|
def shortest_c_dep():
|
|
have_cycle: bool = False
|
|
|
|
sorted_keys = None
|
|
|
|
while True:
|
|
|
|
shortest_cycles = None
|
|
if sorted_keys is None:
|
|
sorted_keys = sorted(deps.keys())
|
|
|
|
with Pool(8) as pool:
|
|
cycles = pool.map(handle_module2, sorted_keys)
|
|
|
|
for cycle in cycles:
|
|
if cycle is not None and (shortest_cycles is None or len(cycle) < len(shortest_cycles)):
|
|
shortest_cycles = cycle
|
|
|
|
if shortest_cycles is None:
|
|
break
|
|
# We have the shortest circular dependency; report it
|
|
module = shortest_cycles[0]
|
|
print("Circular dependency: %s" % (" -> ".join(shortest_cycles + [module])))
|
|
# And then break the dependency to avoid repeating in other cycles
|
|
deps[shortest_cycles[-1]] -= {module}
|
|
sorted_keys = None
|
|
have_cycle = True
|
|
|
|
if have_cycle:
|
|
return True
|
|
|
|
sys.exit(1 if shortest_c_dep() else 0)
|