ethspecify 0.3.1__tar.gz → 0.3.3__tar.gz
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.
- {ethspecify-0.3.1 → ethspecify-0.3.3}/PKG-INFO +1 -1
- {ethspecify-0.3.1 → ethspecify-0.3.3}/ethspecify/cli.py +24 -7
- {ethspecify-0.3.1 → ethspecify-0.3.3}/ethspecify/core.py +416 -14
- {ethspecify-0.3.1 → ethspecify-0.3.3}/ethspecify.egg-info/PKG-INFO +1 -1
- {ethspecify-0.3.1 → ethspecify-0.3.3}/setup.py +1 -1
- {ethspecify-0.3.1 → ethspecify-0.3.3}/LICENSE +0 -0
- {ethspecify-0.3.1 → ethspecify-0.3.3}/README.md +0 -0
- {ethspecify-0.3.1 → ethspecify-0.3.3}/ethspecify/__init__.py +0 -0
- {ethspecify-0.3.1 → ethspecify-0.3.3}/ethspecify.egg-info/SOURCES.txt +0 -0
- {ethspecify-0.3.1 → ethspecify-0.3.3}/ethspecify.egg-info/dependency_links.txt +0 -0
- {ethspecify-0.3.1 → ethspecify-0.3.3}/ethspecify.egg-info/entry_points.txt +0 -0
- {ethspecify-0.3.1 → ethspecify-0.3.3}/ethspecify.egg-info/requires.txt +0 -0
- {ethspecify-0.3.1 → ethspecify-0.3.3}/ethspecify.egg-info/top_level.txt +0 -0
- {ethspecify-0.3.1 → ethspecify-0.3.3}/setup.cfg +0 -0
|
@@ -3,7 +3,7 @@ import json
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
|
-
from .core import grep, replace_spec_tags, get_pyspec, get_latest_fork, get_spec_item_history, load_config, run_checks, sort_specref_yaml, generate_specref_files
|
|
6
|
+
from .core import grep, replace_spec_tags, get_pyspec, get_latest_fork, get_spec_item_history, load_config, run_checks, sort_specref_yaml, generate_specref_files, get_yaml_filename_for_spec_attr, get_spec_attr_and_name, add_missing_entries_to_yaml
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def process(args):
|
|
@@ -16,20 +16,37 @@ def process(args):
|
|
|
16
16
|
# Load config once from the project directory
|
|
17
17
|
config = load_config(project_dir)
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
for f in grep(project_dir, r"<spec\b.*?>", args.exclude):
|
|
21
|
-
print(f"Processing file: {f}")
|
|
22
|
-
replace_spec_tags(f, config)
|
|
23
|
-
|
|
24
|
-
# Sort specref YAML files if they exist in config
|
|
19
|
+
# Check if auto_add_missing_entries and auto_standardize_names are enabled
|
|
25
20
|
specrefs_config = config.get('specrefs', {})
|
|
26
21
|
if isinstance(specrefs_config, dict):
|
|
22
|
+
auto_add_missing = specrefs_config.get('auto_add_missing_entries', False)
|
|
23
|
+
auto_standardize_names = specrefs_config.get('auto_standardize_names', False)
|
|
27
24
|
specrefs_files = specrefs_config.get('files', [])
|
|
28
25
|
elif isinstance(specrefs_config, list):
|
|
26
|
+
auto_add_missing = False
|
|
27
|
+
auto_standardize_names = False
|
|
29
28
|
specrefs_files = specrefs_config
|
|
30
29
|
else:
|
|
30
|
+
auto_add_missing = False
|
|
31
|
+
auto_standardize_names = False
|
|
31
32
|
specrefs_files = []
|
|
32
33
|
|
|
34
|
+
# Process spec tags in files
|
|
35
|
+
for f in grep(project_dir, r"<spec\b.*?>", args.exclude):
|
|
36
|
+
print(f"Processing file: {f}")
|
|
37
|
+
replace_spec_tags(f, config)
|
|
38
|
+
|
|
39
|
+
# Add missing spec items to YAML files if enabled
|
|
40
|
+
if auto_add_missing:
|
|
41
|
+
from .core import add_missing_spec_items_to_yaml_files
|
|
42
|
+
add_missing_spec_items_to_yaml_files(project_dir, config, specrefs_files)
|
|
43
|
+
|
|
44
|
+
# Update entry names to <spec_item>#<fork> format if enabled
|
|
45
|
+
if auto_standardize_names:
|
|
46
|
+
from .core import update_entry_names_in_yaml_files
|
|
47
|
+
update_entry_names_in_yaml_files(project_dir, specrefs_files)
|
|
48
|
+
|
|
49
|
+
# Sort specref YAML files if they exist in config
|
|
33
50
|
for yaml_file in specrefs_files:
|
|
34
51
|
yaml_path = os.path.join(project_dir, yaml_file)
|
|
35
52
|
if os.path.exists(yaml_path):
|
|
@@ -89,7 +89,8 @@ def validate_exception_items(exceptions, version):
|
|
|
89
89
|
break
|
|
90
90
|
|
|
91
91
|
if not item_found:
|
|
92
|
-
|
|
92
|
+
fork_suffix = f"#{fork}" if fork else ""
|
|
93
|
+
errors.append(f"invalid key: {exception_key}.{item_name}{fork_suffix}")
|
|
93
94
|
|
|
94
95
|
if errors:
|
|
95
96
|
error_msg = "Invalid exception items in configuration:\n" + "\n".join(f" - {e}" for e in errors)
|
|
@@ -588,9 +589,18 @@ def sort_specref_yaml(yaml_file):
|
|
|
588
589
|
with open(yaml_file, 'r') as f:
|
|
589
590
|
content_str = f.read()
|
|
590
591
|
|
|
591
|
-
#
|
|
592
|
-
# This
|
|
593
|
-
|
|
592
|
+
# Extract search values that originally had single or double quotes before YAML parsing
|
|
593
|
+
# This preserves the original quoting style exactly
|
|
594
|
+
single_quoted_searches = set()
|
|
595
|
+
for match in re.finditer(r"search:\s*'([^']*)'", content_str):
|
|
596
|
+
single_quoted_searches.add(match.group(1))
|
|
597
|
+
double_quoted_searches = set()
|
|
598
|
+
for match in re.finditer(r'search:\s*"([^"]*)"', content_str):
|
|
599
|
+
double_quoted_searches.add(match.group(1))
|
|
600
|
+
|
|
601
|
+
# Temporarily quote unquoted search strings with colons so YAML can parse them
|
|
602
|
+
# This doesn't affect the output - we restore original quoting when writing
|
|
603
|
+
content_str = re.sub(r'(\s+search:\s+)([^"\'\n]+:)(\s*$)', r'\1"\2"\3', content_str, flags=re.MULTILINE)
|
|
594
604
|
|
|
595
605
|
try:
|
|
596
606
|
content = yaml.safe_load(content_str)
|
|
@@ -671,16 +681,11 @@ def sort_specref_yaml(yaml_file):
|
|
|
671
681
|
output_lines.append(f" - file: {source.get('file', '')}")
|
|
672
682
|
if 'search' in source:
|
|
673
683
|
search_val = source['search']
|
|
674
|
-
#
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
not (search_val.startswith('"') and search_val.endswith('"')) and
|
|
680
|
-
'"' not in search_val):
|
|
681
|
-
# Only quote simple strings with colons, not complex patterns
|
|
682
|
-
if not any(char in search_val for char in ['\\', '*', '|', '[', ']', '(', ')']):
|
|
683
|
-
search_val = f'"{search_val}"'
|
|
684
|
+
# Preserve original quoting style exactly
|
|
685
|
+
if search_val in single_quoted_searches:
|
|
686
|
+
search_val = f"'{search_val}'"
|
|
687
|
+
elif search_val in double_quoted_searches:
|
|
688
|
+
search_val = f'"{search_val}"'
|
|
684
689
|
output_lines.append(f" search: {search_val}")
|
|
685
690
|
if 'regex' in source:
|
|
686
691
|
# Keep boolean values lowercase for consistency
|
|
@@ -724,6 +729,9 @@ def replace_spec_tags(file_path, config=None):
|
|
|
724
729
|
re.DOTALL
|
|
725
730
|
)
|
|
726
731
|
|
|
732
|
+
# Collect processed spec items for potential YAML updates
|
|
733
|
+
processed_items = []
|
|
734
|
+
|
|
727
735
|
def rebuild_opening_tag(attributes, hash_value):
|
|
728
736
|
# Rebuild a fresh opening tag from attributes, overriding any existing hash.
|
|
729
737
|
new_opening = "<spec"
|
|
@@ -762,6 +770,17 @@ def replace_spec_tags(file_path, config=None):
|
|
|
762
770
|
spec = get_spec(attributes, preset, fork, version)
|
|
763
771
|
hash_value = hashlib.sha256(spec.encode('utf-8')).hexdigest()[:8]
|
|
764
772
|
|
|
773
|
+
# Collect this item for potential YAML updates
|
|
774
|
+
processed_items.append({
|
|
775
|
+
'attributes': attributes,
|
|
776
|
+
'preset': preset,
|
|
777
|
+
'fork': fork,
|
|
778
|
+
'style': style,
|
|
779
|
+
'version': version,
|
|
780
|
+
'spec': spec,
|
|
781
|
+
'hash': hash_value
|
|
782
|
+
})
|
|
783
|
+
|
|
765
784
|
if style == "hash":
|
|
766
785
|
# Rebuild a fresh self-closing tag.
|
|
767
786
|
updated_tag = rebuild_self_closing_tag(attributes, hash_value)
|
|
@@ -786,6 +805,169 @@ def replace_spec_tags(file_path, config=None):
|
|
|
786
805
|
with open(file_path, 'w') as file:
|
|
787
806
|
file.write(updated_content)
|
|
788
807
|
|
|
808
|
+
# Return processed items for potential YAML updates
|
|
809
|
+
return processed_items
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
def get_yaml_filename_for_spec_attr(spec_attr):
|
|
813
|
+
"""Map spec attribute to YAML filename."""
|
|
814
|
+
attr_to_file = {
|
|
815
|
+
'fn': 'functions.yml',
|
|
816
|
+
'function': 'functions.yml',
|
|
817
|
+
'constant_var': 'constants.yml',
|
|
818
|
+
'config_var': 'configs.yml',
|
|
819
|
+
'preset_var': 'presets.yml',
|
|
820
|
+
'container': 'containers.yml',
|
|
821
|
+
'ssz_object': 'containers.yml',
|
|
822
|
+
'dataclass': 'dataclasses.yml',
|
|
823
|
+
'custom_type': 'types.yml',
|
|
824
|
+
}
|
|
825
|
+
return attr_to_file.get(spec_attr)
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
def get_spec_attr_and_name(attributes):
|
|
829
|
+
"""Extract the spec attribute and item name from tag attributes."""
|
|
830
|
+
spec_attrs = ['fn', 'function', 'constant_var', 'config_var', 'preset_var',
|
|
831
|
+
'container', 'ssz_object', 'dataclass', 'custom_type']
|
|
832
|
+
for attr in spec_attrs:
|
|
833
|
+
if attr in attributes:
|
|
834
|
+
return attr, attributes[attr]
|
|
835
|
+
return None, None
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
def load_yaml_entries(yaml_file):
|
|
839
|
+
"""Load existing entries from a YAML file."""
|
|
840
|
+
if not os.path.exists(yaml_file):
|
|
841
|
+
return []
|
|
842
|
+
|
|
843
|
+
try:
|
|
844
|
+
with open(yaml_file, 'r') as f:
|
|
845
|
+
content_str = f.read()
|
|
846
|
+
|
|
847
|
+
# Try to fix common YAML issues with unquoted search strings containing colons
|
|
848
|
+
content_str = re.sub(r'(\s+search:\s+)([^"\n]+:)(\s*$)', r'\1"\2"\3', content_str, flags=re.MULTILINE)
|
|
849
|
+
|
|
850
|
+
try:
|
|
851
|
+
content = yaml.safe_load(content_str)
|
|
852
|
+
except yaml.YAMLError:
|
|
853
|
+
content = yaml.load(content_str, Loader=yaml.FullLoader)
|
|
854
|
+
|
|
855
|
+
if isinstance(content, list):
|
|
856
|
+
return content
|
|
857
|
+
return []
|
|
858
|
+
except (yaml.YAMLError, IOError):
|
|
859
|
+
return []
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
def extract_spec_tag_key(spec_content):
|
|
863
|
+
"""Extract a unique key from spec tag to identify duplicates."""
|
|
864
|
+
if not spec_content:
|
|
865
|
+
return None
|
|
866
|
+
|
|
867
|
+
# Extract the opening spec tag
|
|
868
|
+
match = re.search(r'<spec\b([^>]*)>', spec_content)
|
|
869
|
+
if not match:
|
|
870
|
+
return None
|
|
871
|
+
|
|
872
|
+
# Extract attributes from the tag
|
|
873
|
+
attributes = extract_attributes(match.group(0))
|
|
874
|
+
|
|
875
|
+
# Build a key from the spec attribute and fork
|
|
876
|
+
# e.g., "constant_var:DOMAIN_PTC_ATTESTER:fork:gloas"
|
|
877
|
+
key_parts = []
|
|
878
|
+
for attr in ['fn', 'function', 'constant_var', 'config_var', 'preset_var',
|
|
879
|
+
'container', 'ssz_object', 'dataclass', 'custom_type']:
|
|
880
|
+
if attr in attributes:
|
|
881
|
+
key_parts.append(f"{attr}:{attributes[attr]}")
|
|
882
|
+
break
|
|
883
|
+
|
|
884
|
+
if 'fork' in attributes:
|
|
885
|
+
key_parts.append(f"fork:{attributes['fork']}")
|
|
886
|
+
|
|
887
|
+
return ':'.join(key_parts) if key_parts else None
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
def add_missing_entries_to_yaml(yaml_file, new_entries):
|
|
891
|
+
"""Add new entries to a YAML file and sort it."""
|
|
892
|
+
if not new_entries:
|
|
893
|
+
return
|
|
894
|
+
|
|
895
|
+
# Extract search values that originally had single or double quotes before YAML parsing
|
|
896
|
+
# This preserves the original quoting style exactly
|
|
897
|
+
single_quoted_searches = set()
|
|
898
|
+
double_quoted_searches = set()
|
|
899
|
+
if os.path.exists(yaml_file):
|
|
900
|
+
with open(yaml_file, 'r') as f:
|
|
901
|
+
content_str = f.read()
|
|
902
|
+
for match in re.finditer(r"search:\s*'([^']*)'", content_str):
|
|
903
|
+
single_quoted_searches.add(match.group(1))
|
|
904
|
+
for match in re.finditer(r'search:\s*"([^"]*)"', content_str):
|
|
905
|
+
double_quoted_searches.add(match.group(1))
|
|
906
|
+
|
|
907
|
+
# Load existing entries
|
|
908
|
+
existing_entries = load_yaml_entries(yaml_file)
|
|
909
|
+
|
|
910
|
+
# Build a set of existing spec tag keys
|
|
911
|
+
existing_spec_keys = set()
|
|
912
|
+
for entry in existing_entries:
|
|
913
|
+
if isinstance(entry, dict) and 'spec' in entry:
|
|
914
|
+
spec_key = extract_spec_tag_key(entry['spec'])
|
|
915
|
+
if spec_key:
|
|
916
|
+
existing_spec_keys.add(spec_key)
|
|
917
|
+
|
|
918
|
+
# Filter out entries that already exist (based on spec tag, not name)
|
|
919
|
+
entries_to_add = []
|
|
920
|
+
for entry in new_entries:
|
|
921
|
+
spec_key = extract_spec_tag_key(entry.get('spec', ''))
|
|
922
|
+
if spec_key and spec_key not in existing_spec_keys:
|
|
923
|
+
entries_to_add.append(entry)
|
|
924
|
+
existing_spec_keys.add(spec_key) # Avoid duplicates within new entries
|
|
925
|
+
|
|
926
|
+
if not entries_to_add:
|
|
927
|
+
return
|
|
928
|
+
|
|
929
|
+
# Combine and write
|
|
930
|
+
all_entries = existing_entries + entries_to_add
|
|
931
|
+
|
|
932
|
+
# Ensure directory exists
|
|
933
|
+
os.makedirs(os.path.dirname(yaml_file) if os.path.dirname(yaml_file) else '.', exist_ok=True)
|
|
934
|
+
|
|
935
|
+
# Write combined entries using the same format as generate_specref_files
|
|
936
|
+
with open(yaml_file, 'w') as f:
|
|
937
|
+
for i, entry in enumerate(all_entries):
|
|
938
|
+
if i > 0:
|
|
939
|
+
f.write('\n')
|
|
940
|
+
f.write(f'- name: {entry["name"]}\n')
|
|
941
|
+
if 'sources' in entry:
|
|
942
|
+
if isinstance(entry['sources'], list) and len(entry['sources']) == 0:
|
|
943
|
+
f.write(' sources: []\n')
|
|
944
|
+
else:
|
|
945
|
+
f.write(' sources:\n')
|
|
946
|
+
for source in entry['sources']:
|
|
947
|
+
if isinstance(source, dict):
|
|
948
|
+
f.write(f' - file: {source.get("file", "")}\n')
|
|
949
|
+
if 'search' in source:
|
|
950
|
+
search_val = source["search"]
|
|
951
|
+
# Preserve original quoting style exactly
|
|
952
|
+
if search_val in single_quoted_searches:
|
|
953
|
+
search_val = f"'{search_val}'"
|
|
954
|
+
elif search_val in double_quoted_searches:
|
|
955
|
+
search_val = f'"{search_val}"'
|
|
956
|
+
f.write(f' search: {search_val}\n')
|
|
957
|
+
if 'regex' in source:
|
|
958
|
+
f.write(f' regex: {source["regex"]}\n')
|
|
959
|
+
else:
|
|
960
|
+
f.write(f' - {source}\n')
|
|
961
|
+
if 'spec' in entry:
|
|
962
|
+
f.write(' spec: |\n')
|
|
963
|
+
for line in entry['spec'].split('\n'):
|
|
964
|
+
f.write(f' {line}\n')
|
|
965
|
+
|
|
966
|
+
# Sort the file
|
|
967
|
+
sort_specref_yaml(yaml_file)
|
|
968
|
+
|
|
969
|
+
print(f"Added {len(entries_to_add)} new entries to {yaml_file}")
|
|
970
|
+
|
|
789
971
|
|
|
790
972
|
def check_source_files(yaml_file, project_root, exceptions=None):
|
|
791
973
|
"""
|
|
@@ -1520,6 +1702,226 @@ def run_checks(project_dir, config):
|
|
|
1520
1702
|
return overall_success, results
|
|
1521
1703
|
|
|
1522
1704
|
|
|
1705
|
+
def update_entry_names_in_yaml_files(project_dir, specrefs_files):
|
|
1706
|
+
"""
|
|
1707
|
+
Update all entry names to use the format <spec_item>#<fork>.
|
|
1708
|
+
"""
|
|
1709
|
+
for yaml_file in specrefs_files:
|
|
1710
|
+
yaml_path = os.path.join(project_dir, yaml_file)
|
|
1711
|
+
|
|
1712
|
+
if not os.path.exists(yaml_path):
|
|
1713
|
+
continue
|
|
1714
|
+
|
|
1715
|
+
# Extract search values that originally had single or double quotes before YAML parsing
|
|
1716
|
+
# This preserves the original quoting style exactly
|
|
1717
|
+
single_quoted_searches = set()
|
|
1718
|
+
double_quoted_searches = set()
|
|
1719
|
+
with open(yaml_path, 'r') as f:
|
|
1720
|
+
content_str = f.read()
|
|
1721
|
+
for match in re.finditer(r"search:\s*'([^']*)'", content_str):
|
|
1722
|
+
single_quoted_searches.add(match.group(1))
|
|
1723
|
+
for match in re.finditer(r'search:\s*"([^"]*)"', content_str):
|
|
1724
|
+
double_quoted_searches.add(match.group(1))
|
|
1725
|
+
|
|
1726
|
+
# Load existing entries
|
|
1727
|
+
existing_entries = load_yaml_entries(yaml_path)
|
|
1728
|
+
if not existing_entries:
|
|
1729
|
+
continue
|
|
1730
|
+
|
|
1731
|
+
updated = False
|
|
1732
|
+
for entry in existing_entries:
|
|
1733
|
+
if not isinstance(entry, dict) or 'spec' not in entry:
|
|
1734
|
+
continue
|
|
1735
|
+
|
|
1736
|
+
# Extract spec tag attributes
|
|
1737
|
+
spec_content = entry['spec']
|
|
1738
|
+
match = re.search(r'<spec\b([^>]*)>', spec_content)
|
|
1739
|
+
if not match:
|
|
1740
|
+
continue
|
|
1741
|
+
|
|
1742
|
+
attributes = extract_attributes(match.group(0))
|
|
1743
|
+
|
|
1744
|
+
# Get the spec item name and fork
|
|
1745
|
+
spec_attr, item_name = get_spec_attr_and_name(attributes)
|
|
1746
|
+
fork = attributes.get('fork')
|
|
1747
|
+
|
|
1748
|
+
if item_name and fork:
|
|
1749
|
+
# Build the expected name
|
|
1750
|
+
expected_name = f'{item_name}#{fork}'
|
|
1751
|
+
|
|
1752
|
+
# Update if different
|
|
1753
|
+
if entry.get('name') != expected_name:
|
|
1754
|
+
entry['name'] = expected_name
|
|
1755
|
+
updated = True
|
|
1756
|
+
|
|
1757
|
+
# Write back if updated
|
|
1758
|
+
if updated:
|
|
1759
|
+
with open(yaml_path, 'w') as f:
|
|
1760
|
+
for i, entry in enumerate(existing_entries):
|
|
1761
|
+
if i > 0:
|
|
1762
|
+
f.write('\n')
|
|
1763
|
+
f.write(f'- name: {entry["name"]}\n')
|
|
1764
|
+
if 'sources' in entry:
|
|
1765
|
+
if isinstance(entry['sources'], list) and len(entry['sources']) == 0:
|
|
1766
|
+
f.write(' sources: []\n')
|
|
1767
|
+
else:
|
|
1768
|
+
f.write(' sources:\n')
|
|
1769
|
+
for source in entry['sources']:
|
|
1770
|
+
if isinstance(source, dict):
|
|
1771
|
+
f.write(f' - file: {source.get("file", "")}\n')
|
|
1772
|
+
if 'search' in source:
|
|
1773
|
+
search_val = source["search"]
|
|
1774
|
+
# Preserve original quoting style exactly
|
|
1775
|
+
if search_val in single_quoted_searches:
|
|
1776
|
+
search_val = f"'{search_val}'"
|
|
1777
|
+
elif search_val in double_quoted_searches:
|
|
1778
|
+
search_val = f'"{search_val}"'
|
|
1779
|
+
f.write(f' search: {search_val}\n')
|
|
1780
|
+
if 'regex' in source:
|
|
1781
|
+
f.write(f' regex: {source["regex"]}\n')
|
|
1782
|
+
else:
|
|
1783
|
+
f.write(f' - {source}\n')
|
|
1784
|
+
if 'spec' in entry:
|
|
1785
|
+
f.write(' spec: |\n')
|
|
1786
|
+
for line in entry['spec'].split('\n'):
|
|
1787
|
+
f.write(f' {line}\n')
|
|
1788
|
+
|
|
1789
|
+
# Sort the file
|
|
1790
|
+
sort_specref_yaml(yaml_path)
|
|
1791
|
+
print(f"Updated entry names in {yaml_file}")
|
|
1792
|
+
|
|
1793
|
+
|
|
1794
|
+
def add_missing_spec_items_to_yaml_files(project_dir, config, specrefs_files):
|
|
1795
|
+
"""
|
|
1796
|
+
Add missing spec items to existing YAML files.
|
|
1797
|
+
Ensures all spec items from the specification exist in YAML files with sources: []
|
|
1798
|
+
"""
|
|
1799
|
+
version = config.get('version', 'nightly')
|
|
1800
|
+
preset = 'mainnet' # Could make this configurable
|
|
1801
|
+
|
|
1802
|
+
# Get all spec items
|
|
1803
|
+
pyspec = get_pyspec(version)
|
|
1804
|
+
if preset not in pyspec:
|
|
1805
|
+
print(f"Error: Preset '{preset}' not found")
|
|
1806
|
+
return
|
|
1807
|
+
|
|
1808
|
+
# Get all forks in chronological order, excluding EIP forks
|
|
1809
|
+
all_forks = sorted(
|
|
1810
|
+
[fork for fork in pyspec[preset].keys() if not fork.startswith("eip")],
|
|
1811
|
+
key=lambda x: (x != "phase0", x)
|
|
1812
|
+
)
|
|
1813
|
+
|
|
1814
|
+
# Map YAML filenames to category keys and spec attribute names
|
|
1815
|
+
filename_to_category = {
|
|
1816
|
+
'constants.yml': ('constant_vars', 'constant_var'),
|
|
1817
|
+
'configs.yml': ('config_vars', 'config_var'),
|
|
1818
|
+
'presets.yml': ('preset_vars', 'preset_var'),
|
|
1819
|
+
'functions.yml': ('functions', 'fn'),
|
|
1820
|
+
'containers.yml': ('ssz_objects', 'container'),
|
|
1821
|
+
'dataclasses.yml': ('dataclasses', 'dataclass'),
|
|
1822
|
+
'types.yml': ('custom_types', 'custom_type'),
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
for yaml_file in specrefs_files:
|
|
1826
|
+
yaml_path = os.path.join(project_dir, yaml_file)
|
|
1827
|
+
yaml_basename = os.path.basename(yaml_file)
|
|
1828
|
+
|
|
1829
|
+
if yaml_basename not in filename_to_category:
|
|
1830
|
+
continue
|
|
1831
|
+
|
|
1832
|
+
category, spec_attr = filename_to_category[yaml_basename]
|
|
1833
|
+
|
|
1834
|
+
# Collect all items in this category organized by name and fork
|
|
1835
|
+
items_by_name = {}
|
|
1836
|
+
for fork in all_forks:
|
|
1837
|
+
if fork not in pyspec[preset]:
|
|
1838
|
+
continue
|
|
1839
|
+
fork_data = pyspec[preset][fork]
|
|
1840
|
+
|
|
1841
|
+
if category not in fork_data:
|
|
1842
|
+
continue
|
|
1843
|
+
|
|
1844
|
+
for item_name, item_data in fork_data[category].items():
|
|
1845
|
+
if item_name not in items_by_name:
|
|
1846
|
+
items_by_name[item_name] = []
|
|
1847
|
+
items_by_name[item_name].append((fork, item_data))
|
|
1848
|
+
|
|
1849
|
+
# Build entries for missing items
|
|
1850
|
+
new_entries = []
|
|
1851
|
+
for item_name in sorted(items_by_name.keys()):
|
|
1852
|
+
forks_data = items_by_name[item_name]
|
|
1853
|
+
|
|
1854
|
+
# Find all unique versions of this item (where content differs)
|
|
1855
|
+
versions = [] # List of (fork, item_data, spec_content)
|
|
1856
|
+
prev_content = None
|
|
1857
|
+
|
|
1858
|
+
for fork, item_data in forks_data:
|
|
1859
|
+
# Build the spec content based on category
|
|
1860
|
+
if category == 'functions':
|
|
1861
|
+
spec_content = item_data
|
|
1862
|
+
elif category in ['constant_vars', 'config_vars', 'preset_vars']:
|
|
1863
|
+
# item_data is a list: [type, value, ...]
|
|
1864
|
+
if isinstance(item_data, (list, tuple)) and len(item_data) >= 2:
|
|
1865
|
+
type_info = item_data[0]
|
|
1866
|
+
value = item_data[1]
|
|
1867
|
+
if type_info:
|
|
1868
|
+
spec_content = f"{item_name}: {type_info} = {value}"
|
|
1869
|
+
else:
|
|
1870
|
+
spec_content = f"{item_name} = {value}"
|
|
1871
|
+
else:
|
|
1872
|
+
spec_content = str(item_data)
|
|
1873
|
+
elif category == 'ssz_objects':
|
|
1874
|
+
spec_content = item_data
|
|
1875
|
+
elif category == 'dataclasses':
|
|
1876
|
+
spec_content = item_data.replace("@dataclass\n", "")
|
|
1877
|
+
elif category == 'custom_types':
|
|
1878
|
+
# custom_types are simple type aliases: TypeName = SomeType
|
|
1879
|
+
spec_content = f"{item_name} = {item_data}"
|
|
1880
|
+
else:
|
|
1881
|
+
spec_content = str(item_data)
|
|
1882
|
+
|
|
1883
|
+
# Only add this version if it's different from the previous one
|
|
1884
|
+
if prev_content is None or spec_content != prev_content:
|
|
1885
|
+
versions.append((fork, item_data, spec_content))
|
|
1886
|
+
prev_content = spec_content
|
|
1887
|
+
|
|
1888
|
+
# Create entries based on number of unique versions
|
|
1889
|
+
use_fork_suffix = len(versions) > 1
|
|
1890
|
+
|
|
1891
|
+
for idx, (fork, item_data, spec_content) in enumerate(versions):
|
|
1892
|
+
# Calculate hash of current version
|
|
1893
|
+
hash_value = hashlib.sha256(spec_content.encode('utf-8')).hexdigest()[:8]
|
|
1894
|
+
|
|
1895
|
+
# For multiple versions after the first, use diff style
|
|
1896
|
+
if use_fork_suffix and idx > 0:
|
|
1897
|
+
# Get previous version for diff
|
|
1898
|
+
prev_fork, _, prev_spec_content = versions[idx - 1]
|
|
1899
|
+
|
|
1900
|
+
# Generate diff
|
|
1901
|
+
diff_content = diff(prev_fork, strip_comments(prev_spec_content), fork, strip_comments(spec_content))
|
|
1902
|
+
|
|
1903
|
+
# Build spec tag with style="diff"
|
|
1904
|
+
spec_tag = f'<spec {spec_attr}="{item_name}" fork="{fork}" style="diff" hash="{hash_value}">'
|
|
1905
|
+
content = diff_content
|
|
1906
|
+
else:
|
|
1907
|
+
# Build spec tag without style="diff"
|
|
1908
|
+
spec_tag = f'<spec {spec_attr}="{item_name}" fork="{fork}" hash="{hash_value}">'
|
|
1909
|
+
content = spec_content
|
|
1910
|
+
|
|
1911
|
+
# Create entry
|
|
1912
|
+
entry_name = f'{item_name}#{fork}' if use_fork_suffix else item_name
|
|
1913
|
+
entry = {
|
|
1914
|
+
'name': entry_name,
|
|
1915
|
+
'sources': [],
|
|
1916
|
+
'spec': f'{spec_tag}\n{content}\n</spec>'
|
|
1917
|
+
}
|
|
1918
|
+
new_entries.append(entry)
|
|
1919
|
+
|
|
1920
|
+
# Add missing entries to the YAML file
|
|
1921
|
+
if new_entries:
|
|
1922
|
+
add_missing_entries_to_yaml(yaml_path, new_entries)
|
|
1923
|
+
|
|
1924
|
+
|
|
1523
1925
|
def generate_specref_files(output_dir, version="nightly", preset="mainnet"):
|
|
1524
1926
|
"""
|
|
1525
1927
|
Generate specref YAML files without sources for manual mapping.
|
|
@@ -8,7 +8,7 @@ long_description = (this_directory / "README.md").read_text(encoding="utf-8")
|
|
|
8
8
|
|
|
9
9
|
setup(
|
|
10
10
|
name="ethspecify",
|
|
11
|
-
version="0.3.
|
|
11
|
+
version="0.3.3",
|
|
12
12
|
description="A utility for processing Ethereum specification tags.",
|
|
13
13
|
long_description=long_description,
|
|
14
14
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|