ethspecify 0.2.5__py3-none-any.whl → 0.2.6__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 +21 -15
- ethspecify/core.py +250 -6
- {ethspecify-0.2.5.dist-info → ethspecify-0.2.6.dist-info}/METADATA +1 -1
- ethspecify-0.2.6.dist-info/RECORD +9 -0
- ethspecify-0.2.5.dist-info/RECORD +0 -9
- {ethspecify-0.2.5.dist-info → ethspecify-0.2.6.dist-info}/WHEEL +0 -0
- {ethspecify-0.2.5.dist-info → ethspecify-0.2.6.dist-info}/entry_points.txt +0 -0
- {ethspecify-0.2.5.dist-info → ethspecify-0.2.6.dist-info}/licenses/LICENSE +0 -0
- {ethspecify-0.2.5.dist-info → ethspecify-0.2.6.dist-info}/top_level.txt +0 -0
ethspecify/cli.py
CHANGED
|
@@ -89,30 +89,36 @@ def check(args):
|
|
|
89
89
|
total_source_files = {"valid": 0, "total": 0}
|
|
90
90
|
|
|
91
91
|
for section_name, section_results in results.items():
|
|
92
|
-
# Determine the type prefix from section name
|
|
93
|
-
if "Config Variables" in section_name:
|
|
94
|
-
type_prefix = "config_var"
|
|
95
|
-
elif "Preset Variables" in section_name:
|
|
96
|
-
type_prefix = "preset_var"
|
|
97
|
-
elif "Ssz Objects" in section_name:
|
|
98
|
-
type_prefix = "ssz_object"
|
|
99
|
-
elif "Dataclasses" in section_name:
|
|
100
|
-
type_prefix = "dataclass"
|
|
101
|
-
else:
|
|
102
|
-
type_prefix = section_name.lower().replace(" ", "_")
|
|
103
|
-
|
|
104
92
|
# Collect source file errors
|
|
105
93
|
source = section_results['source_files']
|
|
106
94
|
total_source_files["valid"] += source["valid"]
|
|
107
95
|
total_source_files["total"] += source["total"]
|
|
108
96
|
all_errors.extend(source["errors"])
|
|
109
97
|
|
|
110
|
-
# Collect missing items
|
|
98
|
+
# Collect missing items
|
|
111
99
|
coverage = section_results['coverage']
|
|
112
100
|
total_coverage["found"] += coverage["found"]
|
|
113
101
|
total_coverage["expected"] += coverage["expected"]
|
|
114
|
-
|
|
115
|
-
|
|
102
|
+
|
|
103
|
+
# For Project Coverage, items already have the proper prefix
|
|
104
|
+
if section_name == "Project Coverage":
|
|
105
|
+
for missing in coverage['missing']:
|
|
106
|
+
all_missing.append(f"MISSING: {missing}")
|
|
107
|
+
else:
|
|
108
|
+
# Determine the type prefix from section name for YAML-based checks
|
|
109
|
+
if "Config Variables" in section_name:
|
|
110
|
+
type_prefix = "config_var"
|
|
111
|
+
elif "Preset Variables" in section_name:
|
|
112
|
+
type_prefix = "preset_var"
|
|
113
|
+
elif "Ssz Objects" in section_name:
|
|
114
|
+
type_prefix = "ssz_object"
|
|
115
|
+
elif "Dataclasses" in section_name:
|
|
116
|
+
type_prefix = "dataclass"
|
|
117
|
+
else:
|
|
118
|
+
type_prefix = section_name.lower().replace(" ", "_")
|
|
119
|
+
|
|
120
|
+
for missing in coverage['missing']:
|
|
121
|
+
all_missing.append(f"MISSING: {type_prefix}.{missing}")
|
|
116
122
|
|
|
117
123
|
# Display only errors and missing items
|
|
118
124
|
for error in all_errors:
|
ethspecify/core.py
CHANGED
|
@@ -822,6 +822,196 @@ def extract_spec_tags_from_yaml(yaml_file, tag_type=None):
|
|
|
822
822
|
return tag_types_found, pairs
|
|
823
823
|
|
|
824
824
|
|
|
825
|
+
def generate_specrefs_from_files(files_with_spec_tags, project_dir):
|
|
826
|
+
"""
|
|
827
|
+
Generate specrefs data from files containing spec tags.
|
|
828
|
+
Returns a dict with spec tag info and their source locations.
|
|
829
|
+
"""
|
|
830
|
+
specrefs = {}
|
|
831
|
+
|
|
832
|
+
for file_path in files_with_spec_tags:
|
|
833
|
+
try:
|
|
834
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
835
|
+
content = f.read()
|
|
836
|
+
|
|
837
|
+
# Find all spec tags in the file
|
|
838
|
+
spec_tag_pattern = r'<spec\s+([^>]+?)(?:\s*/>|>)'
|
|
839
|
+
matches = re.finditer(spec_tag_pattern, content)
|
|
840
|
+
|
|
841
|
+
for match in matches:
|
|
842
|
+
tag_attrs_str = match.group(1)
|
|
843
|
+
attrs = extract_attributes(f"<spec {tag_attrs_str}>")
|
|
844
|
+
|
|
845
|
+
# Determine the spec type and name
|
|
846
|
+
spec_type = None
|
|
847
|
+
spec_name = None
|
|
848
|
+
fork = attrs.get('fork', None)
|
|
849
|
+
|
|
850
|
+
# Check each possible spec attribute
|
|
851
|
+
for attr_name in ['fn', 'function', 'constant_var', 'config_var',
|
|
852
|
+
'preset_var', 'ssz_object', 'dataclass', 'custom_type']:
|
|
853
|
+
if attr_name in attrs:
|
|
854
|
+
spec_type = attr_name
|
|
855
|
+
spec_name = attrs[attr_name]
|
|
856
|
+
break
|
|
857
|
+
|
|
858
|
+
if spec_type and spec_name:
|
|
859
|
+
# Create a unique key for this spec reference
|
|
860
|
+
key = f"{spec_type}.{spec_name}"
|
|
861
|
+
if fork:
|
|
862
|
+
key += f"#{fork}"
|
|
863
|
+
|
|
864
|
+
if key not in specrefs:
|
|
865
|
+
specrefs[key] = {
|
|
866
|
+
'name': spec_name,
|
|
867
|
+
'type': spec_type,
|
|
868
|
+
'fork': fork,
|
|
869
|
+
'sources': []
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
# Add this source location
|
|
873
|
+
rel_path = os.path.relpath(file_path, project_dir)
|
|
874
|
+
|
|
875
|
+
# Get line number of the match
|
|
876
|
+
lines_before = content[:match.start()].count('\n')
|
|
877
|
+
line_num = lines_before + 1
|
|
878
|
+
|
|
879
|
+
specrefs[key]['sources'].append({
|
|
880
|
+
'file': rel_path,
|
|
881
|
+
'line': line_num
|
|
882
|
+
})
|
|
883
|
+
|
|
884
|
+
except (IOError, UnicodeDecodeError):
|
|
885
|
+
continue
|
|
886
|
+
|
|
887
|
+
return specrefs
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
def process_generated_specrefs(specrefs, exceptions, version):
|
|
891
|
+
"""
|
|
892
|
+
Process the generated specrefs and check coverage.
|
|
893
|
+
Returns (success, results)
|
|
894
|
+
"""
|
|
895
|
+
results = {}
|
|
896
|
+
overall_success = True
|
|
897
|
+
|
|
898
|
+
# Group specrefs by type for coverage checking
|
|
899
|
+
specrefs_by_type = {}
|
|
900
|
+
for _, data in specrefs.items():
|
|
901
|
+
spec_type = data['type']
|
|
902
|
+
if spec_type not in specrefs_by_type:
|
|
903
|
+
specrefs_by_type[spec_type] = []
|
|
904
|
+
specrefs_by_type[spec_type].append(data)
|
|
905
|
+
|
|
906
|
+
# Map spec types to history keys
|
|
907
|
+
type_to_history_key = {
|
|
908
|
+
'fn': 'functions',
|
|
909
|
+
'function': 'functions',
|
|
910
|
+
'constant_var': 'constant_vars',
|
|
911
|
+
'config_var': 'config_vars',
|
|
912
|
+
'preset_var': 'preset_vars',
|
|
913
|
+
'ssz_object': 'ssz_objects',
|
|
914
|
+
'dataclass': 'dataclasses',
|
|
915
|
+
'custom_type': 'custom_types'
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
# Map to exception keys
|
|
919
|
+
type_to_exception_key = {
|
|
920
|
+
'fn': 'functions',
|
|
921
|
+
'function': 'functions',
|
|
922
|
+
'constant_var': 'constants',
|
|
923
|
+
'config_var': 'configs',
|
|
924
|
+
'preset_var': 'presets',
|
|
925
|
+
'ssz_object': 'ssz_objects',
|
|
926
|
+
'dataclass': 'dataclasses',
|
|
927
|
+
'custom_type': 'custom_types'
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
# Get spec history for coverage checking
|
|
931
|
+
history = get_spec_item_history("mainnet", version)
|
|
932
|
+
|
|
933
|
+
# Check coverage for each type
|
|
934
|
+
total_found = 0
|
|
935
|
+
total_expected = 0
|
|
936
|
+
all_missing = []
|
|
937
|
+
|
|
938
|
+
for spec_type, items in specrefs_by_type.items():
|
|
939
|
+
history_key = type_to_history_key.get(spec_type, spec_type)
|
|
940
|
+
exception_key = type_to_exception_key.get(spec_type, spec_type)
|
|
941
|
+
|
|
942
|
+
# Get exceptions for this type - handle both singular and plural keys
|
|
943
|
+
type_exceptions = []
|
|
944
|
+
if exception_key in exceptions:
|
|
945
|
+
type_exceptions = exceptions[exception_key]
|
|
946
|
+
# Also check plural forms
|
|
947
|
+
elif exception_key + 's' in exceptions:
|
|
948
|
+
type_exceptions = exceptions[exception_key + 's']
|
|
949
|
+
# Check if singular form exists when we have plural
|
|
950
|
+
elif exception_key.endswith('s') and exception_key[:-1] in exceptions:
|
|
951
|
+
type_exceptions = exceptions[exception_key[:-1]]
|
|
952
|
+
|
|
953
|
+
# Build set of what we found
|
|
954
|
+
found_items = set()
|
|
955
|
+
for item in items:
|
|
956
|
+
if item['fork']:
|
|
957
|
+
found_items.add(f"{item['name']}#{item['fork']}")
|
|
958
|
+
else:
|
|
959
|
+
# If no fork specified, we need to check all forks
|
|
960
|
+
if history_key in history and item['name'] in history[history_key]:
|
|
961
|
+
for fork in history[history_key][item['name']]:
|
|
962
|
+
found_items.add(f"{item['name']}#{fork}")
|
|
963
|
+
|
|
964
|
+
# Check what's expected
|
|
965
|
+
if history_key in history:
|
|
966
|
+
for item_name, forks in history[history_key].items():
|
|
967
|
+
for fork in forks:
|
|
968
|
+
expected_key = f"{item_name}#{fork}"
|
|
969
|
+
total_expected += 1
|
|
970
|
+
|
|
971
|
+
# Check if excepted
|
|
972
|
+
if is_excepted(item_name, fork, type_exceptions):
|
|
973
|
+
total_found += 1
|
|
974
|
+
continue
|
|
975
|
+
|
|
976
|
+
if expected_key in found_items:
|
|
977
|
+
total_found += 1
|
|
978
|
+
else:
|
|
979
|
+
# Use the proper type prefix for the missing item
|
|
980
|
+
type_prefix_map = {
|
|
981
|
+
'functions': 'functions',
|
|
982
|
+
'constant_vars': 'constants',
|
|
983
|
+
'config_vars': 'configs',
|
|
984
|
+
'preset_vars': 'presets',
|
|
985
|
+
'ssz_objects': 'ssz_objects',
|
|
986
|
+
'dataclasses': 'dataclasses',
|
|
987
|
+
'custom_types': 'custom_types'
|
|
988
|
+
}
|
|
989
|
+
prefix = type_prefix_map.get(history_key, history_key)
|
|
990
|
+
all_missing.append(f"{prefix}.{expected_key}")
|
|
991
|
+
|
|
992
|
+
# Count total spec references found
|
|
993
|
+
total_refs = len(specrefs)
|
|
994
|
+
|
|
995
|
+
# Store results
|
|
996
|
+
results['Project Coverage'] = {
|
|
997
|
+
'source_files': {
|
|
998
|
+
'valid': total_refs,
|
|
999
|
+
'total': total_refs,
|
|
1000
|
+
'errors': []
|
|
1001
|
+
},
|
|
1002
|
+
'coverage': {
|
|
1003
|
+
'found': total_found,
|
|
1004
|
+
'expected': total_expected,
|
|
1005
|
+
'missing': all_missing
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
if all_missing:
|
|
1010
|
+
overall_success = False
|
|
1011
|
+
|
|
1012
|
+
return overall_success, results
|
|
1013
|
+
|
|
1014
|
+
|
|
825
1015
|
def check_coverage(yaml_file, tag_type, exceptions, preset="mainnet", version="nightly"):
|
|
826
1016
|
"""
|
|
827
1017
|
Check that all spec items from ethspecify have corresponding tags in the YAML file.
|
|
@@ -890,12 +1080,48 @@ def run_checks(project_dir, config):
|
|
|
890
1080
|
else:
|
|
891
1081
|
# New format: specrefs: { files: [...], exceptions: {...} }
|
|
892
1082
|
specrefs_files = specrefs_config.get('files', [])
|
|
893
|
-
exceptions = specrefs_config.get('exceptions', {})
|
|
894
1083
|
|
|
1084
|
+
# Support exceptions in either specrefs section or root, but not both
|
|
1085
|
+
specrefs_exceptions = specrefs_config.get('exceptions', {})
|
|
1086
|
+
root_exceptions = config.get('exceptions', {})
|
|
1087
|
+
|
|
1088
|
+
if specrefs_exceptions and root_exceptions:
|
|
1089
|
+
print("Warning: Exceptions found in both root and specrefs sections. Using specrefs exceptions.")
|
|
1090
|
+
exceptions = specrefs_exceptions
|
|
1091
|
+
elif specrefs_exceptions:
|
|
1092
|
+
exceptions = specrefs_exceptions
|
|
1093
|
+
else:
|
|
1094
|
+
exceptions = root_exceptions
|
|
1095
|
+
|
|
1096
|
+
# If no files specified, search the whole project for spec tags
|
|
895
1097
|
if not specrefs_files:
|
|
896
|
-
print("
|
|
897
|
-
|
|
898
|
-
|
|
1098
|
+
print("No specific files configured, searching entire project for spec tags...")
|
|
1099
|
+
|
|
1100
|
+
# Determine search root - configurable in specrefs section
|
|
1101
|
+
if 'search_root' in specrefs_config:
|
|
1102
|
+
# Use configured search_root (relative to project_dir)
|
|
1103
|
+
search_root_rel = specrefs_config['search_root']
|
|
1104
|
+
search_root = os.path.join(project_dir, search_root_rel) if not os.path.isabs(search_root_rel) else search_root_rel
|
|
1105
|
+
search_root = os.path.abspath(search_root)
|
|
1106
|
+
else:
|
|
1107
|
+
# Default behavior: if we're in a specrefs directory, search in the parent directory
|
|
1108
|
+
search_root = os.path.dirname(project_dir) if os.path.basename(project_dir) == 'specrefs' else project_dir
|
|
1109
|
+
|
|
1110
|
+
print(f"Searching for spec tags in: {search_root}")
|
|
1111
|
+
|
|
1112
|
+
# Use grep to find all files containing spec tags
|
|
1113
|
+
files_with_spec_tags = grep(search_root, r'<spec\b[^>]*>', [])
|
|
1114
|
+
|
|
1115
|
+
if not files_with_spec_tags:
|
|
1116
|
+
print(f"No files with spec tags found in the project")
|
|
1117
|
+
return True, {}
|
|
1118
|
+
|
|
1119
|
+
# Generate in-memory specrefs data from the found spec tags
|
|
1120
|
+
all_specrefs = generate_specrefs_from_files(files_with_spec_tags, search_root)
|
|
1121
|
+
|
|
1122
|
+
# Process the generated specrefs
|
|
1123
|
+
return process_generated_specrefs(all_specrefs, exceptions, version)
|
|
1124
|
+
|
|
899
1125
|
|
|
900
1126
|
# Map tag types to exception keys (support both singular and plural)
|
|
901
1127
|
exception_key_map = {
|
|
@@ -928,7 +1154,16 @@ def run_checks(project_dir, config):
|
|
|
928
1154
|
# Process each tag type found in the file
|
|
929
1155
|
if not tag_types_found:
|
|
930
1156
|
# No spec tags found, still check source files
|
|
931
|
-
|
|
1157
|
+
# Determine source root - use search_root if configured, otherwise use default behavior
|
|
1158
|
+
if 'search_root' in specrefs_config:
|
|
1159
|
+
search_root_rel = specrefs_config['search_root']
|
|
1160
|
+
source_root = os.path.join(project_dir, search_root_rel) if not os.path.isabs(search_root_rel) else search_root_rel
|
|
1161
|
+
source_root = os.path.abspath(source_root)
|
|
1162
|
+
else:
|
|
1163
|
+
# Default behavior: parent directory
|
|
1164
|
+
source_root = os.path.dirname(project_dir)
|
|
1165
|
+
|
|
1166
|
+
valid_count, total_count, source_errors = check_source_files(yaml_path, source_root, [])
|
|
932
1167
|
|
|
933
1168
|
# Store results using filename as section name
|
|
934
1169
|
section_name = filename.replace('.yml', '').replace('-', ' ').title()
|
|
@@ -980,7 +1215,16 @@ def run_checks(project_dir, config):
|
|
|
980
1215
|
if key in exceptions:
|
|
981
1216
|
all_exceptions.extend(exceptions[key])
|
|
982
1217
|
|
|
983
|
-
|
|
1218
|
+
# Determine source root - use search_root if configured, otherwise use default behavior
|
|
1219
|
+
if 'search_root' in specrefs_config:
|
|
1220
|
+
search_root_rel = specrefs_config['search_root']
|
|
1221
|
+
source_root = os.path.join(project_dir, search_root_rel) if not os.path.isabs(search_root_rel) else search_root_rel
|
|
1222
|
+
source_root = os.path.abspath(source_root)
|
|
1223
|
+
else:
|
|
1224
|
+
# Default behavior: parent directory
|
|
1225
|
+
source_root = os.path.dirname(project_dir)
|
|
1226
|
+
|
|
1227
|
+
valid_count, total_count, source_errors = check_source_files(yaml_path, source_root, all_exceptions)
|
|
984
1228
|
|
|
985
1229
|
# Store results using filename as section name
|
|
986
1230
|
section_name = filename.replace('.yml', '').replace('-', ' ').title()
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
ethspecify/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
ethspecify/cli.py,sha256=RRxFLYkIXze2IfXk9282zVEC43D_ICuDGnn1if1GoCY,7935
|
|
3
|
+
ethspecify/core.py,sha256=1pJhD38JjSZLNySPvdcps1iRR6oxN1kRT-kFoAXfulY,47468
|
|
4
|
+
ethspecify-0.2.6.dist-info/licenses/LICENSE,sha256=Awxsr73mm9YMBVhBYnzeI7bNdRd-bH6RDtO5ItG0DaM,1071
|
|
5
|
+
ethspecify-0.2.6.dist-info/METADATA,sha256=DXAvnsf0VKp58RF-1KxoLl7YC_Ke96nUlV3kwuvsv-4,9212
|
|
6
|
+
ethspecify-0.2.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
+
ethspecify-0.2.6.dist-info/entry_points.txt,sha256=09viGkCg9J3h0c9BFRN-BKaJUEaIc4JyULNgBP5EL_g,51
|
|
8
|
+
ethspecify-0.2.6.dist-info/top_level.txt,sha256=0klaMvlVyOkXW09fwZTijJpdybITEp2c9zQKV5v30VM,11
|
|
9
|
+
ethspecify-0.2.6.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
ethspecify/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
ethspecify/cli.py,sha256=SZ47-lgfeDHmzXCE-rx8ydM66N9NfNAA2GDxoC4DE7E,7641
|
|
3
|
-
ethspecify/core.py,sha256=07SRTRlkTOC_6bqX-bKd8liUfS7nKzl-iQhLtEJYp2o,37999
|
|
4
|
-
ethspecify-0.2.5.dist-info/licenses/LICENSE,sha256=Awxsr73mm9YMBVhBYnzeI7bNdRd-bH6RDtO5ItG0DaM,1071
|
|
5
|
-
ethspecify-0.2.5.dist-info/METADATA,sha256=C2EFGKJ0lArb4-BN3vIwlqi6TfWqj4EN3MZVCc0TU54,9212
|
|
6
|
-
ethspecify-0.2.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
-
ethspecify-0.2.5.dist-info/entry_points.txt,sha256=09viGkCg9J3h0c9BFRN-BKaJUEaIc4JyULNgBP5EL_g,51
|
|
8
|
-
ethspecify-0.2.5.dist-info/top_level.txt,sha256=0klaMvlVyOkXW09fwZTijJpdybITEp2c9zQKV5v30VM,11
|
|
9
|
-
ethspecify-0.2.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|