File tree 2 files changed +49
-1
lines changed
2 files changed +49
-1
lines changed Original file line number Diff line number Diff line change 37
37
class LinkHash :
38
38
"""Links to content may have embedded hash values. This class parses those.
39
39
40
- `name` must be any member of `_SUPPORTED_HASHES`."""
40
+ `name` must be any member of `_SUPPORTED_HASHES`.
41
+
42
+ This class can be converted to and from `ArchiveInfo`. While ArchiveInfo intends to
43
+ be JSON-serializable to conform to PEP 610, this class contains the logic for
44
+ parsing a hash name and value for correctness, and then checking whether that hash
45
+ conforms to a schema with `.is_hash_allowed()`."""
41
46
42
47
name : str
43
48
value : str
@@ -52,6 +57,9 @@ class LinkHash:
52
57
)
53
58
)
54
59
60
+ def __post_init__ (self ) -> None :
61
+ assert self ._hash_re .match (f"{ self .name } ={ self .value } " )
62
+
55
63
@classmethod
56
64
@functools .lru_cache (maxsize = None )
57
65
def split_hash_name_and_value (cls , url : str ) -> Optional ["LinkHash" ]:
@@ -66,6 +74,13 @@ def to_archive_info(self) -> ArchiveInfo:
66
74
"""Convert to ArchiveInfo to form a DirectUrl instance (see PEP 610)."""
67
75
return ArchiveInfo (hash = f"{ self .name } ={ self .value } " )
68
76
77
+ @classmethod
78
+ def from_archive_info (cls , info : ArchiveInfo ) -> Optional ["LinkHash" ]:
79
+ """Parse an ArchiveInfo hash into a LinkHash instance."""
80
+ if info .hash is None :
81
+ return None
82
+ return cls .split_hash_name_and_value (info .hash )
83
+
69
84
def is_hash_allowed (self , hashes : Optional [Hashes ]) -> bool :
70
85
"""
71
86
Return True if the current hash is allowed by `hashes`.
Original file line number Diff line number Diff line change @@ -1048,3 +1048,36 @@ def expand_path(path: str) -> str:
1048
1048
)
1049
1049
def test_link_hash_parsing (url : str , result : Optional [LinkHash ]) -> None :
1050
1050
assert LinkHash .split_hash_name_and_value (url ) == result
1051
+
1052
+
1053
+ @pytest .mark .parametrize (
1054
+ "archive_info, link_hash" ,
1055
+ [
1056
+ (
1057
+ ArchiveInfo (hash = None ),
1058
+ None ,
1059
+ ),
1060
+ (
1061
+ ArchiveInfo (hash = "sha256=aabe42af" ),
1062
+ LinkHash (name = "sha256" , value = "aabe42af" ),
1063
+ ),
1064
+ # Test invalid hash strings, which ArchiveInfo doesn't validate.
1065
+ (
1066
+ # Invalid hash name.
1067
+ ArchiveInfo (hash = "sha500=aabe42af" ),
1068
+ None ,
1069
+ ),
1070
+ (
1071
+ # Invalid hash value.
1072
+ ArchiveInfo (hash = "sha256=g42afbe" ),
1073
+ None ,
1074
+ ),
1075
+ ],
1076
+ )
1077
+ def test_link_hash_archive_info_fungibility (
1078
+ archive_info : ArchiveInfo ,
1079
+ link_hash : Optional [LinkHash ],
1080
+ ) -> None :
1081
+ assert LinkHash .from_archive_info (archive_info ) == link_hash
1082
+ if link_hash is not None :
1083
+ assert link_hash .to_archive_info () == archive_info
You can’t perform that action at this time.
0 commit comments