ethspecify 0.1.3__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ethspecify might be problematic. Click here for more details.
- ethspecify/cli.py +183 -12
- ethspecify/core.py +30 -11
- {ethspecify-0.1.3.dist-info → ethspecify-0.2.0.dist-info}/METADATA +3 -2
- ethspecify-0.2.0.dist-info/RECORD +9 -0
- {ethspecify-0.1.3.dist-info → ethspecify-0.2.0.dist-info}/WHEEL +1 -1
- ethspecify-0.1.3.dist-info/RECORD +0 -9
- {ethspecify-0.1.3.dist-info → ethspecify-0.2.0.dist-info}/entry_points.txt +0 -0
- {ethspecify-0.1.3.dist-info → ethspecify-0.2.0.dist-info/licenses}/LICENSE +0 -0
- {ethspecify-0.1.3.dist-info → ethspecify-0.2.0.dist-info}/top_level.txt +0 -0
ethspecify/cli.py
CHANGED
|
@@ -1,34 +1,205 @@
|
|
|
1
1
|
import argparse
|
|
2
|
+
import json
|
|
2
3
|
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from .core import grep, replace_spec_tags, get_pyspec, get_latest_fork
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def process(args):
|
|
10
|
+
"""Process all spec tags."""
|
|
11
|
+
project_dir = os.path.abspath(os.path.expanduser(args.path))
|
|
12
|
+
if not os.path.isdir(project_dir):
|
|
13
|
+
print(f"Error: The directory {repr(project_dir)} does not exist.")
|
|
14
|
+
return 1
|
|
15
|
+
|
|
16
|
+
for f in grep(project_dir, r"<spec\b.*?>", args.exclude):
|
|
17
|
+
print(f"Processing file: {f}")
|
|
18
|
+
replace_spec_tags(f)
|
|
19
|
+
|
|
20
|
+
return 0
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def list_tags(args):
|
|
24
|
+
"""List all available tags for a specific fork and preset."""
|
|
25
|
+
# Get the specification data
|
|
26
|
+
pyspec = get_pyspec()
|
|
27
|
+
fork = args.fork
|
|
28
|
+
preset = args.preset
|
|
29
|
+
|
|
30
|
+
# Validate that the fork exists
|
|
31
|
+
if fork not in pyspec[preset]:
|
|
32
|
+
print(f"Error: Fork '{fork}' not found in {preset} preset")
|
|
33
|
+
available_forks = list(pyspec[preset].keys())
|
|
34
|
+
print(f"Available forks: {', '.join(available_forks)}")
|
|
35
|
+
return 1
|
|
36
|
+
|
|
37
|
+
# Format output based on requested format
|
|
38
|
+
if args.format == "json":
|
|
39
|
+
result = {
|
|
40
|
+
"fork": fork,
|
|
41
|
+
"preset": preset,
|
|
42
|
+
"tags": {
|
|
43
|
+
"functions": list(pyspec[preset][fork]['functions'].keys()),
|
|
44
|
+
"constant_vars": list(pyspec[preset][fork]['constant_vars'].keys()),
|
|
45
|
+
"custom_types": list(pyspec[preset][fork]['custom_types'].keys()),
|
|
46
|
+
"ssz_objects": list(pyspec[preset][fork]['ssz_objects'].keys()),
|
|
47
|
+
"dataclasses": list(pyspec[preset][fork]['dataclasses'].keys()),
|
|
48
|
+
"preset_vars": list(pyspec[preset][fork]['preset_vars'].keys()),
|
|
49
|
+
"config_vars": list(pyspec[preset][fork]['config_vars'].keys()),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
print(json.dumps(result, indent=2))
|
|
53
|
+
else:
|
|
54
|
+
# Plain text output
|
|
55
|
+
print(f"Available tags for {fork} fork ({preset} preset):")
|
|
56
|
+
maybe_fork = f' fork="{fork}"' if fork != get_latest_fork() else ""
|
|
57
|
+
|
|
58
|
+
print("\nFunctions:")
|
|
59
|
+
for fn_name in sorted(pyspec[preset][fork]['functions'].keys()):
|
|
60
|
+
if args.search is None or args.search.lower() in fn_name.lower():
|
|
61
|
+
print(f" <spec fn=\"{fn_name}\"{maybe_fork} />")
|
|
62
|
+
|
|
63
|
+
print("\nConstants:")
|
|
64
|
+
for const_name in sorted(pyspec[preset][fork]['constant_vars'].keys()):
|
|
65
|
+
if args.search is None or args.search.lower() in const_name.lower():
|
|
66
|
+
print(f" <spec constant_var=\"{const_name}\"{maybe_fork} />")
|
|
67
|
+
|
|
68
|
+
print("\nCustom Types:")
|
|
69
|
+
for type_name in sorted(pyspec[preset][fork]['custom_types'].keys()):
|
|
70
|
+
if args.search is None or args.search.lower() in type_name.lower():
|
|
71
|
+
print(f" <spec custom_type=\"{type_name}\"{maybe_fork} />")
|
|
72
|
+
|
|
73
|
+
print("\nSSZ Objects:")
|
|
74
|
+
for obj_name in sorted(pyspec[preset][fork]['ssz_objects'].keys()):
|
|
75
|
+
if args.search is None or args.search.lower() in obj_name.lower():
|
|
76
|
+
print(f" <spec ssz_object=\"{obj_name}\"{maybe_fork} />")
|
|
77
|
+
|
|
78
|
+
print("\nDataclasses:")
|
|
79
|
+
for class_name in sorted(pyspec[preset][fork]['dataclasses'].keys()):
|
|
80
|
+
if args.search is None or args.search.lower() in class_name.lower():
|
|
81
|
+
print(f" <spec dataclass=\"{class_name}\"{maybe_fork} />")
|
|
82
|
+
|
|
83
|
+
print("\nPreset Variables:")
|
|
84
|
+
for var_name in sorted(pyspec[preset][fork]['preset_vars'].keys()):
|
|
85
|
+
if args.search is None or args.search.lower() in var_name.lower():
|
|
86
|
+
print(f" <spec preset_var=\"{var_name}\"{maybe_fork} />")
|
|
87
|
+
|
|
88
|
+
print("\nConfig Variables:")
|
|
89
|
+
for var_name in sorted(pyspec[preset][fork]['config_vars'].keys()):
|
|
90
|
+
if args.search is None or args.search.lower() in var_name.lower():
|
|
91
|
+
print(f" <spec config_var=\"{var_name}\"{maybe_fork} />")
|
|
92
|
+
|
|
93
|
+
return 0
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def list_forks(args):
|
|
97
|
+
"""List all available forks."""
|
|
98
|
+
pyspec = get_pyspec()
|
|
99
|
+
preset = args.preset
|
|
100
|
+
|
|
101
|
+
if preset not in pyspec:
|
|
102
|
+
print(f"Error: Preset '{preset}' not found.")
|
|
103
|
+
print(f"Available presets: {', '.join(pyspec.keys())}")
|
|
104
|
+
return 1
|
|
105
|
+
|
|
106
|
+
forks = sorted(
|
|
107
|
+
pyspec[preset].keys(),
|
|
108
|
+
# Put phase0 at the top & EIP feature forks at the bottom
|
|
109
|
+
key=lambda x: (x != "phase0", x.startswith("eip"), x)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if args.format == "json":
|
|
113
|
+
result = {
|
|
114
|
+
"preset": preset,
|
|
115
|
+
"forks": forks
|
|
116
|
+
}
|
|
117
|
+
print(json.dumps(result, indent=2))
|
|
118
|
+
else:
|
|
119
|
+
print(f"Available forks for {preset} preset:")
|
|
120
|
+
for fork in forks:
|
|
121
|
+
print(f" {fork}")
|
|
122
|
+
|
|
123
|
+
return 0
|
|
3
124
|
|
|
4
|
-
from .core import grep, replace_spec_tags
|
|
5
125
|
|
|
6
126
|
def main():
|
|
7
127
|
parser = argparse.ArgumentParser(
|
|
8
128
|
description="Process files containing <spec> tags."
|
|
9
129
|
)
|
|
10
|
-
|
|
130
|
+
|
|
131
|
+
# Create subparsers for different commands
|
|
132
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to execute")
|
|
133
|
+
|
|
134
|
+
# Parser for 'process' command
|
|
135
|
+
process_parser = subparsers.add_parser("process", help="Process spec tags in files")
|
|
136
|
+
process_parser.set_defaults(func=process)
|
|
137
|
+
process_parser.add_argument(
|
|
11
138
|
"--path",
|
|
12
139
|
type=str,
|
|
13
140
|
help="Directory to search for files containing <spec> tags",
|
|
14
141
|
default=".",
|
|
15
142
|
)
|
|
16
|
-
|
|
143
|
+
process_parser.add_argument(
|
|
17
144
|
"--exclude",
|
|
18
145
|
action="append",
|
|
19
146
|
help="Exclude paths matching this regex",
|
|
20
147
|
default=[],
|
|
21
148
|
)
|
|
22
|
-
args = parser.parse_args()
|
|
23
149
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
150
|
+
# Parser for 'list-tags' command
|
|
151
|
+
list_tags_parser = subparsers.add_parser("list-tags", help="List available specification tags")
|
|
152
|
+
list_tags_parser.set_defaults(func=list_tags)
|
|
153
|
+
list_tags_parser.add_argument(
|
|
154
|
+
"--fork",
|
|
155
|
+
type=str,
|
|
156
|
+
help="Fork to list tags for",
|
|
157
|
+
default=get_latest_fork(),
|
|
158
|
+
)
|
|
159
|
+
list_tags_parser.add_argument(
|
|
160
|
+
"--preset",
|
|
161
|
+
type=str,
|
|
162
|
+
help="Preset to use (mainnet or minimal)",
|
|
163
|
+
default="mainnet",
|
|
164
|
+
)
|
|
165
|
+
list_tags_parser.add_argument(
|
|
166
|
+
"--format",
|
|
167
|
+
type=str,
|
|
168
|
+
choices=["text", "json"],
|
|
169
|
+
default="text",
|
|
170
|
+
help="Output format (text or json)",
|
|
171
|
+
)
|
|
172
|
+
list_tags_parser.add_argument(
|
|
173
|
+
"--search",
|
|
174
|
+
type=str,
|
|
175
|
+
help="Filter tags by search term",
|
|
176
|
+
default=None,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Parser for 'list-forks' command
|
|
180
|
+
list_forks_parser = subparsers.add_parser("list-forks", help="List available forks")
|
|
181
|
+
list_forks_parser.set_defaults(func=list_forks)
|
|
182
|
+
list_forks_parser.add_argument(
|
|
183
|
+
"--preset",
|
|
184
|
+
type=str,
|
|
185
|
+
help="Preset to use (mainnet or minimal)",
|
|
186
|
+
default="mainnet",
|
|
187
|
+
)
|
|
188
|
+
list_forks_parser.add_argument(
|
|
189
|
+
"--format",
|
|
190
|
+
type=str,
|
|
191
|
+
choices=["text", "json"],
|
|
192
|
+
default="text",
|
|
193
|
+
help="Output format (text or json)",
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Default to 'process' if no args are provided
|
|
197
|
+
if len(sys.argv) == 1:
|
|
198
|
+
sys.argv.insert(1, "process")
|
|
199
|
+
|
|
200
|
+
args = parser.parse_args()
|
|
201
|
+
exit(args.func(args))
|
|
28
202
|
|
|
29
|
-
for f in grep(project_dir, r"<spec\b.*?>", args.exclude):
|
|
30
|
-
print(f"Processing file: {f}")
|
|
31
|
-
replace_spec_tags(f)
|
|
32
203
|
|
|
33
204
|
if __name__ == "__main__":
|
|
34
|
-
main()
|
|
205
|
+
main()
|
ethspecify/core.py
CHANGED
|
@@ -197,6 +197,16 @@ def get_spec(attributes, preset, fork):
|
|
|
197
197
|
raise Exception("invalid spec tag")
|
|
198
198
|
return spec
|
|
199
199
|
|
|
200
|
+
def get_latest_fork():
|
|
201
|
+
"""A helper function to get the latest non-eip fork."""
|
|
202
|
+
pyspec = get_pyspec()
|
|
203
|
+
forks = sorted(
|
|
204
|
+
pyspec["mainnet"].keys(),
|
|
205
|
+
key=lambda x: (x != "phase0", x.startswith("eip"), x)
|
|
206
|
+
)
|
|
207
|
+
for fork in reversed(forks):
|
|
208
|
+
if not fork.startswith("eip"):
|
|
209
|
+
return fork
|
|
200
210
|
|
|
201
211
|
def parse_common_attributes(attributes):
|
|
202
212
|
try:
|
|
@@ -207,7 +217,7 @@ def parse_common_attributes(attributes):
|
|
|
207
217
|
try:
|
|
208
218
|
fork = attributes["fork"]
|
|
209
219
|
except KeyError:
|
|
210
|
-
|
|
220
|
+
fork = get_latest_fork()
|
|
211
221
|
|
|
212
222
|
try:
|
|
213
223
|
style = attributes["style"]
|
|
@@ -287,12 +297,28 @@ def replace_spec_tags(file_path):
|
|
|
287
297
|
new_opening += f' hash="{hash_value}">'
|
|
288
298
|
return new_opening
|
|
289
299
|
|
|
300
|
+
def rebuild_self_closing_tag(attributes, hash_value):
|
|
301
|
+
# Build a self-closing tag from attributes, forcing a single space before the slash.
|
|
302
|
+
new_tag = "<spec"
|
|
303
|
+
for key, val in attributes.items():
|
|
304
|
+
if key != "hash":
|
|
305
|
+
new_tag += f' {key}="{val}"'
|
|
306
|
+
new_tag += f' hash="{hash_value}" />'
|
|
307
|
+
return new_tag
|
|
308
|
+
|
|
290
309
|
def replacer(match):
|
|
291
310
|
# Always use the tag text from whichever group matched:
|
|
292
311
|
if match.group("self") is not None:
|
|
293
312
|
original_tag_text = match.group("self")
|
|
294
313
|
else:
|
|
295
314
|
original_tag_text = match.group("long")
|
|
315
|
+
# Determine the original opening tag (ignore inner content)
|
|
316
|
+
if match.group("self") is not None:
|
|
317
|
+
original_tag_text = match.group("self")
|
|
318
|
+
else:
|
|
319
|
+
long_tag_text = match.group("long")
|
|
320
|
+
opening_tag_match = re.search(r'<spec\b[^>]*>', long_tag_text)
|
|
321
|
+
original_tag_text = opening_tag_match.group(0) if opening_tag_match else long_tag_text
|
|
296
322
|
|
|
297
323
|
attributes = extract_attributes(original_tag_text)
|
|
298
324
|
print(f"spec tag: {attributes}")
|
|
@@ -301,18 +327,11 @@ def replace_spec_tags(file_path):
|
|
|
301
327
|
hash_value = hashlib.sha256(spec.encode('utf-8')).hexdigest()[:8]
|
|
302
328
|
|
|
303
329
|
if style == "hash":
|
|
304
|
-
#
|
|
305
|
-
|
|
306
|
-
updated_tag = re.sub(
|
|
307
|
-
r'(hash=")[^"]*(")',
|
|
308
|
-
lambda m: f'{m.group(1)}{hash_value}{m.group(2)}',
|
|
309
|
-
original_tag_text
|
|
310
|
-
)
|
|
311
|
-
else:
|
|
312
|
-
updated_tag = re.sub(r'\s*/>$', f' hash="{hash_value}" />', original_tag_text)
|
|
330
|
+
# Rebuild a fresh self-closing tag.
|
|
331
|
+
updated_tag = rebuild_self_closing_tag(attributes, hash_value)
|
|
313
332
|
return updated_tag
|
|
314
333
|
else:
|
|
315
|
-
# For full/diff styles,
|
|
334
|
+
# For full/diff styles, rebuild as a long (paired) tag.
|
|
316
335
|
new_opening = rebuild_opening_tag(attributes, hash_value)
|
|
317
336
|
spec_content = get_spec_item(attributes)
|
|
318
337
|
prefix = content[:match.start()].splitlines()[-1]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: ethspecify
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A utility for processing Ethereum specification tags.
|
|
5
5
|
Home-page: https://github.com/jtraglia/ethspecify
|
|
6
6
|
Author: Justin Traglia
|
|
@@ -18,6 +18,7 @@ Dynamic: classifier
|
|
|
18
18
|
Dynamic: description
|
|
19
19
|
Dynamic: description-content-type
|
|
20
20
|
Dynamic: home-page
|
|
21
|
+
Dynamic: license-file
|
|
21
22
|
Dynamic: requires-dist
|
|
22
23
|
Dynamic: requires-python
|
|
23
24
|
Dynamic: summary
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
ethspecify/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
ethspecify/cli.py,sha256=73NnY6-xvFcxMnpeZ1LqvU02EiNiPx5jOor1KPERenk,6903
|
|
3
|
+
ethspecify/core.py,sha256=tkkuBXXJtjWZOSvrpohaFa89VsiocgdAm9QrB55dtGE,12963
|
|
4
|
+
ethspecify-0.2.0.dist-info/licenses/LICENSE,sha256=Awxsr73mm9YMBVhBYnzeI7bNdRd-bH6RDtO5ItG0DaM,1071
|
|
5
|
+
ethspecify-0.2.0.dist-info/METADATA,sha256=wE94OOQKuoZL8hGliq93NX7LQRLsU5eGbtD380J1kzc,8812
|
|
6
|
+
ethspecify-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
+
ethspecify-0.2.0.dist-info/entry_points.txt,sha256=09viGkCg9J3h0c9BFRN-BKaJUEaIc4JyULNgBP5EL_g,51
|
|
8
|
+
ethspecify-0.2.0.dist-info/top_level.txt,sha256=0klaMvlVyOkXW09fwZTijJpdybITEp2c9zQKV5v30VM,11
|
|
9
|
+
ethspecify-0.2.0.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
ethspecify/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
ethspecify/cli.py,sha256=TTKmSGUkuRdYlYS6t-cpVXpdpSKsYCWvmRDy8DcC10E,882
|
|
3
|
-
ethspecify/core.py,sha256=pUybFZnO8nDDQ-7tQjxbUjQHFTV4PEGFWJTyR3gy0hM,12194
|
|
4
|
-
ethspecify-0.1.3.dist-info/LICENSE,sha256=Awxsr73mm9YMBVhBYnzeI7bNdRd-bH6RDtO5ItG0DaM,1071
|
|
5
|
-
ethspecify-0.1.3.dist-info/METADATA,sha256=JI4igGfL54P1EEmtIG-54A1SV5mrhxIbCCOs2WufJmQ,8790
|
|
6
|
-
ethspecify-0.1.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
7
|
-
ethspecify-0.1.3.dist-info/entry_points.txt,sha256=09viGkCg9J3h0c9BFRN-BKaJUEaIc4JyULNgBP5EL_g,51
|
|
8
|
-
ethspecify-0.1.3.dist-info/top_level.txt,sha256=0klaMvlVyOkXW09fwZTijJpdybITEp2c9zQKV5v30VM,11
|
|
9
|
-
ethspecify-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|