-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathpaths.py
More file actions
161 lines (124 loc) · 5.15 KB
/
paths.py
File metadata and controls
161 lines (124 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
"""Helper class for constructing paths and URLs."""
import glob
import json
import os
import re
import subprocess
import sys
from itertools import chain
from os import path
from typing import Any
class Paths:
autonity_dir: str
build_dir: str
output_dir: str
src_dir: str
github_url: str
def __init__(
self,
output_dir: str,
autonity_dir: str,
autonity_config: dict[str, str],
):
self.autonity_dir = path.abspath(path.realpath(autonity_dir))
assert_directory_exists(self.autonity_dir)
assert_git_repository(self.autonity_dir)
self.src_dir = path.join(self.autonity_dir, autonity_config["src_dir"])
assert_directory_exists(self.src_dir)
self.build_dir = path.join(self.autonity_dir, autonity_config["build_dir"])
self.output_dir = output_dir
self.github_url = autonity_config["github_url"]
def load_abi(self, contract_name: str) -> list[dict[str, Any]]:
return load_json(path.join(self.build_dir, f"{contract_name}.abi"))
def load_userdoc(self, contract_name: str) -> dict[str, Any]:
return load_natspec(path.join(self.build_dir, f"{contract_name}.docuser"))
def load_devdoc(self, contract_name: str) -> dict[str, Any]:
return load_natspec(path.join(self.build_dir, f"{contract_name}.docdev"))
def get_output_file_path(self, contract_display_name: str) -> str:
return path.join(
self.output_dir, contract_display_name.replace(" ", "-") + ".md"
)
def get_github_src_url(self, contract_name: str, regexp: re.Pattern) -> str:
src_files = [self.find_src_file(contract_name)]
visited_files = []
for src_file in src_files:
visited_files.append(src_file)
code = read_file(src_file)
if match := regexp.search(code):
lineno = code[: match.span()[0]].count("\n") + 1
return self.construct_github_src_url(src_file, lineno)
# Check in imported files
for rel_import in parse_solidity_imports(src_file):
abs_import = path.abspath(path.join(path.dirname(src_file), rel_import))
if abs_import not in visited_files:
src_files.append(abs_import)
raise RuntimeError(
f"Failed to find event or function definition in {contract_name} source "
f"code using regexp: {regexp}"
)
def find_src_file(self, contract_name: str) -> str:
if paths := glob.glob(
path.join(self.src_dir, "**", f"{contract_name}.sol"), recursive=True
):
return paths[0]
raise RuntimeError(f"Could not find {contract_name}.sol in {self.src_dir}")
def construct_github_src_url(self, src_file: str, lineno: int) -> str:
relpath = path.relpath(src_file, self.autonity_dir)
commit_id = get_commit_id(self.autonity_dir)
return f"{self.github_url}/blob/{commit_id}/{relpath}#L{lineno}"
def get_document_version(self) -> str:
version = []
# Convert 'heads/develop' to 'develop' or
# 'tags/v0.14.0' to 'v0.14.0' or 'tags/v0.14.0-1-gc157e1344' to 'v0.14.0'
version.append(
get_git_object(self.autonity_dir).split("/", 1)[-1].split("-")[0]
)
version.append(get_commit_id(self.autonity_dir)[:8])
if is_repo_dirty(self.src_dir):
version.append("dirty")
return "-".join(version)
def clear_output_dir(self) -> None:
for file in glob.glob(path.join(self.output_dir, "**", "*.md"), recursive=True):
if file != path.join(self.output_dir, "index.md"):
os.remove(file)
def load_json(file: str) -> Any:
with open(file) as f:
return json.load(f)
def load_natspec(file: str) -> dict[str, Any]:
try:
return load_json(file)
except FileNotFoundError:
print(
"Note: For Autonity <= v0.14.1 run `patch-autonity` "
"to patch `make contracts` to build .docuser and .docdev files.",
file=sys.stderr,
)
raise
def get_git_object(repo_dir: str) -> str:
return subprocess.check_output(
["git", "describe", "--all"], cwd=repo_dir, text=True
).strip()
def get_commit_id(repo_dir: str) -> str:
return subprocess.check_output(
["git", "rev-parse", "HEAD"], cwd=repo_dir, text=True
).strip()
def is_repo_dirty(repo_dir: str) -> bool:
process = subprocess.run(
["git", "diff-index", "--quiet", "HEAD", "."], cwd=repo_dir
)
return process.returncode > 0
def read_file(file: str) -> str:
with open(file) as f:
return f.read()
def parse_solidity_imports(file: str) -> list[str]:
quote = r"['\"]"
matches = re.findall(
rf"(import +{quote}(.+?){quote}|import.+from +{quote}(.+?){quote})",
read_file(file),
re.MULTILINE,
)
return [item for item in chain(*[match[1:] for match in matches]) if item != ""]
def assert_directory_exists(dir: str) -> None:
assert path.isdir(dir), f"{dir}: no such directory"
def assert_git_repository(dir: str) -> None:
assert path.isdir(path.join(dir, ".git")), f"{dir}: not a Git repository"