Skip to content

Commit d8bd84d

Browse files
Added more dynamic report of pitfalls from multiple sources issue #86
1 parent 076dcbf commit d8bd84d

17 files changed

Lines changed: 324 additions & 181 deletions

File tree

src/rsmetacheck/scripts/pitfalls/p001.py

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import re
2-
from typing import Dict, Optional, List, Union
2+
from typing import Dict, Optional, List
33
from rsmetacheck.utils.pitfall_utils import normalize_version
44
from rsmetacheck.utils.pitfall_utils import extract_metadata_source_filename
55

@@ -95,15 +95,15 @@ def extract_latest_release_version(somef_data: Dict) -> Optional[str]:
9595

9696
return None
9797

98+
9899
def detect_version_mismatch(somef_data: Dict, file_name: str) -> list:
99100
"""
100101
Detect version mismatches between metadata files and the latest release.
101-
Checks all metadata files and returns one result dict per metadata source
102-
that has an inconsistency.
102+
Returns a single result with all mismatched sources merged into one evidence message.
103103
104104
- Metadata version > release by >= 2 in any component: pitfall
105-
- Metadata version > release by < 2: note (best practice to prepare ahead)
106-
- Metadata version < release: pitfall (metadata was not updated)
105+
- Metadata version > release by < 2: note
106+
- Metadata version < release: pitfall
107107
- Metadata version == release: no issue
108108
"""
109109
metadata_versions = extract_version_from_metadata(somef_data)
@@ -113,7 +113,10 @@ def detect_version_mismatch(somef_data: Dict, file_name: str) -> list:
113113
return []
114114

115115
normalized_release_version = normalize_version(release_version)
116-
results = []
116+
117+
pitfall_sources = []
118+
note_sources = []
119+
all_mismatches = []
117120

118121
for md_info in metadata_versions:
119122
metadata_version = normalize_version(md_info["version"])
@@ -122,48 +125,56 @@ def detect_version_mismatch(somef_data: Dict, file_name: str) -> list:
122125
if metadata_version == normalized_release_version:
123126
continue
124127

128+
all_mismatches.append({
129+
"source_file": metadata_source_file,
130+
"source": md_info["source"],
131+
"metadata_version": metadata_version,
132+
})
133+
125134
if _metadata_ahead_of_release(metadata_version, normalized_release_version):
126135
if _version_diff_significant(metadata_version, normalized_release_version):
127-
results.append({
128-
"has_pitfall": True,
129-
"has_note": False,
130-
"file_name": file_name,
131-
"metadata_version": metadata_version,
132-
"release_version": normalized_release_version,
133-
"metadata_source": md_info["source"],
134-
"metadata_source_file": metadata_source_file,
135-
"note_text": None,
136-
"notes": []
137-
})
136+
pitfall_sources.append(metadata_source_file)
138137
else:
139-
note_text = f"Version discrepancy: {metadata_source_file} version '{metadata_version}' is ahead of release version '{normalized_release_version}'"
140-
results.append({
141-
"has_pitfall": False,
142-
"has_note": True,
143-
"file_name": file_name,
144-
"metadata_version": metadata_version,
145-
"release_version": normalized_release_version,
146-
"metadata_source": md_info["source"],
147-
"metadata_source_file": metadata_source_file,
148-
"note_text": note_text,
149-
"notes": [{
150-
"metadata_source_file": metadata_source_file,
151-
"metadata_version": metadata_version,
152-
"release_version": normalized_release_version,
153-
"note_text": note_text
154-
}]
155-
})
138+
note_sources.append(metadata_source_file)
156139
else:
157-
results.append({
158-
"has_pitfall": True,
159-
"has_note": False,
160-
"file_name": file_name,
161-
"metadata_version": metadata_version,
140+
pitfall_sources.append(metadata_source_file)
141+
142+
if not all_mismatches:
143+
return []
144+
145+
has_any_pitfall = len(pitfall_sources) > 0
146+
has_any_note = len(note_sources) > 0
147+
148+
all_source_files = [m["source_file"] for m in all_mismatches]
149+
150+
# Build notes for any ahead-by-small-amount sources
151+
notes = []
152+
note_text = None
153+
for m in all_mismatches:
154+
m_version = m["metadata_version"]
155+
if _metadata_ahead_of_release(m_version, normalized_release_version) and not _version_diff_significant(m_version, normalized_release_version):
156+
text = f"Version discrepancy: {m['source_file']} version '{m_version}' is ahead of release version '{normalized_release_version}'"
157+
notes.append({
158+
"metadata_source_file": m["source_file"],
159+
"metadata_version": m_version,
162160
"release_version": normalized_release_version,
163-
"metadata_source": md_info["source"],
164-
"metadata_source_file": metadata_source_file,
165-
"note_text": None,
166-
"notes": []
161+
"note_text": text
167162
})
168-
169-
return results
163+
if note_text is None:
164+
note_text = text
165+
166+
result = {
167+
"has_pitfall": has_any_pitfall,
168+
"has_note": (not has_any_pitfall and has_any_note),
169+
"file_name": file_name,
170+
"mismatched_sources": all_mismatches,
171+
"metadata_source_files": all_source_files,
172+
"release_version": normalized_release_version,
173+
"note_text": note_text,
174+
"notes": notes,
175+
"metadata_version": all_mismatches[0]["metadata_version"] if all_mismatches else None,
176+
"metadata_source_file": all_mismatches[0]["source_file"] if all_mismatches else None,
177+
"metadata_source": all_mismatches[0]["source"] if all_mismatches else None,
178+
}
179+
180+
return [result]

src/rsmetacheck/scripts/pitfalls/p003.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ def has_multiple_authors_in_single_field(author_value: str) -> bool:
3232
def detect_multiple_authors_single_field_pitfall(somef_data: Dict, file_name: str) -> Dict:
3333
"""
3434
Detect when metadata files have multiple authors in a single field instead of a list.
35+
Checks all metadata sources and collects all affected files.
3536
"""
3637
result = {
3738
"has_pitfall": False,
3839
"file_name": file_name,
3940
"author_value": None,
4041
"source": None,
4142
"metadata_source_file": None,
43+
"metadata_source_files": [],
4244
"multiple_authors_detected": False
4345
}
4446

@@ -69,22 +71,26 @@ def detect_multiple_authors_single_field_pitfall(somef_data: Dict, file_name: st
6971
# Handle different value formats
7072
if isinstance(author_value, str):
7173
if has_multiple_authors_in_single_field(author_value):
74+
source_filename = extract_metadata_source_filename(source)
75+
if result["author_value"] is None:
76+
result["author_value"] = author_value
77+
result["source"] = source
78+
result["metadata_source_file"] = source_filename
79+
result["metadata_source_files"].append(source_filename)
7280
result["has_pitfall"] = True
73-
result["author_value"] = author_value
74-
result["source"] = source
75-
result["metadata_source_file"] = extract_metadata_source_filename(source)
7681
result["multiple_authors_detected"] = True
77-
break
7882

7983
elif isinstance(author_value, dict) and "name" in author_value:
8084
# Handle structured author data
8185
name_value = author_value["name"]
8286
if isinstance(name_value, str) and has_multiple_authors_in_single_field(name_value):
87+
source_filename = extract_metadata_source_filename(source)
88+
if result["author_value"] is None:
89+
result["author_value"] = name_value
90+
result["source"] = source
91+
result["metadata_source_file"] = source_filename
92+
result["metadata_source_files"].append(source_filename)
8393
result["has_pitfall"] = True
84-
result["author_value"] = name_value
85-
result["source"] = source
86-
result["metadata_source_file"] = extract_metadata_source_filename(source)
8794
result["multiple_authors_detected"] = True
88-
break
8995

9096
return result

src/rsmetacheck/scripts/pitfalls/p006.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,15 @@ def is_local_file_license(license_value: str) -> bool:
4040
def detect_local_file_license_pitfall(somef_data: Dict, file_name: str) -> Dict:
4141
"""
4242
Detect when license in metadata files points to a local file instead of stating the name.
43+
Checks all metadata sources and collects all affected files.
4344
"""
4445
result = {
4546
"has_pitfall": False,
4647
"file_name": file_name,
4748
"license_value": None,
4849
"source": None,
4950
"metadata_source_file": None,
51+
"metadata_source_files": [],
5052
"is_local_file": False
5153
}
5254

@@ -68,11 +70,13 @@ def detect_local_file_license_pitfall(somef_data: Dict, file_name: str) -> Dict:
6870
license_value = entry["result"]["value"]
6971

7072
if is_local_file_license(license_value):
73+
source_filename = extract_metadata_source_filename(source)
74+
if result["license_value"] is None:
75+
result["license_value"] = license_value
76+
result["source"] = source if source else f"technique: {technique}"
77+
result["metadata_source_file"] = source_filename
78+
result["metadata_source_files"].append(source_filename)
7179
result["has_pitfall"] = True
72-
result["license_value"] = license_value
73-
result["source"] = source if source else f"technique: {technique}"
74-
result["metadata_source_file"] = extract_metadata_source_filename(source)
7580
result["is_local_file"] = True
76-
break
7781

7882
return result

src/rsmetacheck/scripts/pitfalls/p008.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,15 @@ def extract_urls_from_requirements(requirement_text: str) -> list:
9292
def detect_invalid_software_requirement_pitfall(somef_data: Dict, file_name: str) -> Dict:
9393
"""
9494
Detect when metadata files have software requirements pointing to invalid pages.
95+
Checks all metadata sources and collects all affected files.
9596
"""
9697
result = {
9798
"has_pitfall": False,
9899
"file_name": file_name,
99100
"invalid_urls": [],
100101
"source": None,
101102
"metadata_source_file": None,
103+
"metadata_source_files": [],
102104
"requirement_text": None
103105
}
104106

@@ -129,16 +131,18 @@ def detect_invalid_software_requirement_pitfall(somef_data: Dict, file_name: str
129131
url_status = check_url_status(req_value)
130132

131133
if not url_status["is_accessible"]:
132-
result["has_pitfall"] = True
133-
result["invalid_urls"] = [{
134+
source_filename = extract_metadata_source_filename(source)
135+
if result["source"] is None:
136+
result["source"] = source
137+
result["metadata_source_file"] = source_filename
138+
result["requirement_text"] = req_value
139+
result["invalid_urls"].append({
134140
"url": req_value,
135141
"status_code": url_status["status_code"],
136142
"error": url_status["error"]
137-
}]
138-
result["source"] = source
139-
result["metadata_source_file"] = extract_metadata_source_filename(source)
140-
result["requirement_text"] = req_value
141-
break
143+
})
144+
result["metadata_source_files"].append(source_filename)
145+
result["has_pitfall"] = True
142146
else:
143147
requirement_text = ""
144148
if isinstance(req_value, str):
@@ -167,11 +171,13 @@ def detect_invalid_software_requirement_pitfall(somef_data: Dict, file_name: str
167171
})
168172

169173
if invalid_urls:
174+
source_filename = extract_metadata_source_filename(source)
175+
if result["source"] is None:
176+
result["source"] = source
177+
result["metadata_source_file"] = source_filename
178+
result["requirement_text"] = requirement_text
179+
result["invalid_urls"].extend(invalid_urls)
180+
result["metadata_source_files"].append(source_filename)
170181
result["has_pitfall"] = True
171-
result["invalid_urls"] = invalid_urls
172-
result["source"] = source
173-
result["metadata_source_file"] = extract_metadata_source_filename(source)
174-
result["requirement_text"] = requirement_text
175-
break
176182

177183
return result

src/rsmetacheck/scripts/pitfalls/p009.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,15 @@ def is_homepage_url_repo(url: str) -> bool:
6868
def detect_coderepository_homepage_pitfall(somef_data: Dict, file_name: str) -> Dict:
6969
"""
7070
Detect when code repository in metadata files points to homepage instead of repository.
71+
Checks all metadata sources and collects all affected files.
7172
"""
7273
result = {
7374
"has_pitfall": False,
7475
"file_name": file_name,
7576
"repository_url": None,
7677
"source": None,
7778
"metadata_source_file": None,
79+
"metadata_source_files": [],
7880
"is_homepage": False
7981
}
8082

@@ -103,11 +105,13 @@ def detect_coderepository_homepage_pitfall(somef_data: Dict, file_name: str) ->
103105
repo_url = entry["result"]["value"]
104106

105107
if is_homepage_url_repo(repo_url):
108+
source_filename = extract_metadata_source_filename(source)
109+
if result["repository_url"] is None:
110+
result["repository_url"] = repo_url
111+
result["source"] = source if source else f"technique: {technique}"
112+
result["metadata_source_file"] = source_filename
113+
result["metadata_source_files"].append(source_filename)
106114
result["has_pitfall"] = True
107-
result["repository_url"] = repo_url
108-
result["source"] = source if source else f"technique: {technique}"
109-
result["metadata_source_file"] = extract_metadata_source_filename(source)
110115
result["is_homepage"] = True
111-
break
112116

113117
return result

src/rsmetacheck/scripts/pitfalls/p013.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
def detect_license_no_version_pitfall(somef_data: Dict, file_name: str) -> Dict:
77
"""
88
Detect when license from metadata files doesn't have specific version.
9+
Checks all metadata sources and collects all affected files.
910
"""
1011
result = {
1112
"has_pitfall": False,
1213
"file_name": file_name,
1314
"license_value": None,
1415
"metadata_source_file": None,
16+
"metadata_source_files": [],
1517
"source": None
1618
}
1719

@@ -52,17 +54,19 @@ def detect_license_no_version_pitfall(somef_data: Dict, file_name: str) -> Dict:
5254

5355
if "0BSD" in license_value:
5456
continue
55-
57+
5658
if "LICENSEREF-" in license_upper:
5759
continue
5860

5961
for license_name, version_pattern in versioned_patterns.items():
6062
if re.search(rf"\b{license_name}\b", license_upper):
6163
if not re.search(version_pattern, license_upper, re.IGNORECASE):
64+
source_filename = extract_metadata_source_filename(source)
65+
if result["license_value"] is None:
66+
result["license_value"] = license_value
67+
result["source"] = source
68+
result["metadata_source_file"] = source_filename
69+
result["metadata_source_files"].append(source_filename)
6270
result["has_pitfall"] = True
63-
result["license_value"] = license_value
64-
result["source"] = source
65-
result["metadata_source_file"] = extract_metadata_source_filename(source)
66-
return result
6771

6872
return result

src/rsmetacheck/scripts/pitfalls/p016.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ def detect_different_repository_pitfall(somef_data: Dict, file_name: str) -> Dic
3333
"file_name": file_name,
3434
"github_api_url": None,
3535
"metadata_urls": [],
36-
"different_urls": []
36+
"different_urls": [],
37+
"metadata_source_files": []
3738
}
3839

3940
if "code_repository" not in somef_data:
@@ -79,5 +80,6 @@ def detect_different_repository_pitfall(somef_data: Dict, file_name: str) -> Dic
7980
result["github_api_url"] = github_api_url
8081
result["metadata_urls"] = metadata_urls
8182
result["different_urls"] = different_urls
83+
result["metadata_source_files"] = ["codemeta.json"]
8284

8385
return result

0 commit comments

Comments
 (0)