+57
-13
src/atpasser/uri/__init__.py
+57
-13
src/atpasser/uri/__init__.py
···
40
40
"""
41
41
42
42
if not set(uri).issubset(set([chr(i) for i in range(0x80)])):
43
-
raise InvalidURIError(uri, "contains invalid characters", "URI must only contain ASCII characters")
43
+
raise InvalidURIError(
44
+
uri,
45
+
"contains invalid characters",
46
+
"URI must only contain ASCII characters",
47
+
)
44
48
45
49
if len(uri) > 8000:
46
-
raise InvalidURIError(uri, "exceeds maximum length", f"URI length {len(uri)} exceeds maximum allowed length of 8000 characters")
50
+
raise InvalidURIError(
51
+
uri,
52
+
"exceeds maximum length",
53
+
f"URI length {len(uri)} exceeds maximum allowed length of 8000 characters",
54
+
)
47
55
48
56
if not uri.startswith("at://"):
49
57
raise InvalidURIError(uri, "invalid format", "URI must start with 'at://'")
···
55
63
except IndexError:
56
64
fragment = None
57
65
except Exception as e:
58
-
raise InvalidURIError(uri, "fragment parsing failed", f"Failed to parse URI fragment: {str(e)}")
66
+
raise InvalidURIError(
67
+
uri,
68
+
"fragment parsing failed",
69
+
f"Failed to parse URI fragment: {str(e)}",
70
+
)
59
71
60
72
try:
61
73
query = up.unquote(burner[0].split("?")[1])
62
74
except IndexError:
63
75
query = None
64
76
except Exception as e:
65
-
raise InvalidURIError(uri, "query parameter parsing failed", f"Failed to parse query parameters: {str(e)}")
77
+
raise InvalidURIError(
78
+
uri,
79
+
"query parameter parsing failed",
80
+
f"Failed to parse query parameters: {str(e)}",
81
+
)
66
82
67
83
try:
68
84
path = [up.unquote(segment) for segment in burner[0].split("/")[1:]]
69
85
except Exception as e:
70
-
raise InvalidURIError(uri, "path parsing failed", f"Failed to parse URI path: {str(e)}")
71
-
86
+
raise InvalidURIError(
87
+
uri, "path parsing failed", f"Failed to parse URI path: {str(e)}"
88
+
)
89
+
72
90
if len(path) > 0 and path[-1] == "":
73
-
raise InvalidURIError(uri, "invalid path format", "URI cannot end with a slash")
91
+
raise InvalidURIError(
92
+
uri, "invalid path format", "URI cannot end with a slash"
93
+
)
74
94
75
95
try:
76
96
authorityValue = up.unquote(burner[0].split("/")[0])
77
97
except Exception as e:
78
-
raise InvalidURIError(uri, "authority parsing failed", f"Failed to parse URI authority: {str(e)}")
98
+
raise InvalidURIError(
99
+
uri,
100
+
"authority parsing failed",
101
+
f"Failed to parse URI authority: {str(e)}",
102
+
)
79
103
80
104
try:
81
105
p = up.urlparse(up.unquote("//" + authorityValue))
82
106
if p.username is not None or p.password is not None:
83
-
raise InvalidURIError(uri, "unsupported format", "URI does not support user information (username:password)")
107
+
raise InvalidURIError(
108
+
uri,
109
+
"unsupported format",
110
+
"URI does not support user information (username:password)",
111
+
)
84
112
except Exception as e:
85
113
if not isinstance(e, InvalidURIError):
86
-
raise InvalidURIError(uri, "authority parsing failed", f"Failed to parse authority: {str(e)}")
114
+
raise InvalidURIError(
115
+
uri,
116
+
"authority parsing failed",
117
+
f"Failed to parse authority: {str(e)}",
118
+
)
87
119
else:
88
120
raise
89
121
···
102
134
self.fragment = jsonpath_ng.parse(fragment)
103
135
self.fragmentAsText = up.quote(fragment)
104
136
except Exception as e:
105
-
raise InvalidURIError(uri, "JSONPath parsing failed", f"Failed to parse JSONPath fragment '{fragment}': {str(e)}")
137
+
raise InvalidURIError(
138
+
uri,
139
+
"JSONPath parsing failed",
140
+
f"Failed to parse JSONPath fragment '{fragment}': {str(e)}",
141
+
)
106
142
107
143
if query != None:
108
144
try:
109
145
self.query = up.parse_qs(query)
110
146
self.queryAsText = up.quote(query)
111
147
except Exception as e:
112
-
raise InvalidURIError(uri, "query parameter parsing failed", f"Failed to parse query parameters '{query}': {str(e)}")
148
+
raise InvalidURIError(
149
+
uri,
150
+
"query parameter parsing failed",
151
+
f"Failed to parse query parameters '{query}': {str(e)}",
152
+
)
113
153
else:
114
154
self.query, self.queryAsText = None, None
115
155
···
137
177
"#" + fragment if fragment != None else "",
138
178
)
139
179
except Exception as e:
140
-
raise InvalidURIError(uri, "URI construction failed", f"Failed to construct canonical URI: {str(e)}")
180
+
raise InvalidURIError(
181
+
uri,
182
+
"URI construction failed",
183
+
f"Failed to construct canonical URI: {str(e)}",
184
+
)
141
185
142
186
def __str__(self) -> str:
143
187
"""Convert the URI object to its string representation.
+24
-8
src/atpasser/uri/did.py
+24
-8
src/atpasser/uri/did.py
···
30
30
"""
31
31
pattern = re.compile("^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$")
32
32
patternMatch = pattern.match(uri)
33
-
33
+
34
34
if not patternMatch:
35
-
raise InvalidDIDError(uri, "invalid format", "DID must follow 'did:method:identifier' format")
36
-
35
+
raise InvalidDIDError(
36
+
uri, "invalid format", "DID must follow 'did:method:identifier' format"
37
+
)
38
+
37
39
if len(uri) > 2048:
38
-
raise InvalidDIDError(uri, "exceeds maximum length", f"DID length {len(uri)} exceeds maximum allowed length of 2048 characters")
39
-
40
+
raise InvalidDIDError(
41
+
uri,
42
+
"exceeds maximum length",
43
+
f"DID length {len(uri)} exceeds maximum allowed length of 2048 characters",
44
+
)
45
+
40
46
self.uri = patternMatch[0]
41
47
42
48
def __str__(self) -> str:
···
86
92
elif self.uri.startswith("did:web:"):
87
93
domain = self.uri.replace("did:web:", "")
88
94
if not domain:
89
-
raise InvalidDIDError(self.uri, "invalid format", "did:web DID must contain a domain")
95
+
raise InvalidDIDError(
96
+
self.uri, "invalid format", "did:web DID must contain a domain"
97
+
)
90
98
return jsonld.expand(f"https://{domain}/.well-known/did.json")
91
99
else:
92
-
raise InvalidDIDError(self.uri, "unsupported DID method", f"Unsupported DID method: {self.uri.split(':')[1]}")
100
+
raise InvalidDIDError(
101
+
self.uri,
102
+
"unsupported DID method",
103
+
f"Unsupported DID method: {self.uri.split(':')[1]}",
104
+
)
93
105
except Exception as e:
94
106
if isinstance(e, (InvalidDIDError, ResolutionError)):
95
107
raise
96
-
raise ResolutionError(self.uri, "fetch DID document", f"Failed to fetch or parse DID document: {str(e)}")
108
+
raise ResolutionError(
109
+
self.uri,
110
+
"fetch DID document",
111
+
f"Failed to fetch or parse DID document: {str(e)}",
112
+
)
+16
-11
src/atpasser/uri/exceptions.py
+16
-11
src/atpasser/uri/exceptions.py
···
7
7
8
8
class URIError(Exception):
9
9
"""Base class for AT Protocol URI related errors."""
10
+
10
11
pass
11
12
12
13
13
14
class InvalidURIError(URIError):
14
15
"""Raised when URI format is invalid."""
15
-
16
+
16
17
def __init__(self, uri: str, reason: str, details: Optional[str] = None):
17
18
self.uri = uri
18
19
self.reason = reason
···
25
26
26
27
class InvalidDIDError(URIError):
27
28
"""Raised when DID format is invalid."""
28
-
29
+
29
30
def __init__(self, did: str, reason: str, details: Optional[str] = None):
30
31
self.did = did
31
32
self.reason = reason
···
38
39
39
40
class InvalidHandleError(URIError):
40
41
"""Raised when Handle format is invalid."""
41
-
42
+
42
43
def __init__(self, handle: str, reason: str, details: Optional[str] = None):
43
44
self.handle = handle
44
45
self.reason = reason
···
51
52
52
53
class InvalidNSIDError(URIError):
53
54
"""Raised when NSID format is invalid."""
54
-
55
+
55
56
def __init__(self, nsid: str, reason: str, details: Optional[str] = None):
56
57
self.nsid = nsid
57
58
self.reason = reason
···
64
65
65
66
class InvalidRKeyError(URIError):
66
67
"""Raised when RKey format is invalid."""
67
-
68
+
68
69
def __init__(self, rkey: str, reason: str, details: Optional[str] = None):
69
70
self.rkey = rkey
70
71
self.reason = reason
···
77
78
78
79
class InvalidRestrictedURIError(URIError):
79
80
"""Raised when Restricted URI format is invalid."""
80
-
81
+
81
82
def __init__(self, uri: str, reason: str, details: Optional[str] = None):
82
83
self.uri = uri
83
84
self.reason = reason
···
90
91
91
92
class ResolutionError(URIError):
92
93
"""Raised when resolution operation fails."""
93
-
94
-
def __init__(self, target: str, operation: str, reason: str, details: Optional[str] = None):
94
+
95
+
def __init__(
96
+
self, target: str, operation: str, reason: str, details: Optional[str] = None
97
+
):
95
98
self.target = target
96
99
self.operation = operation
97
100
self.reason = reason
···
104
107
105
108
class ValidationError(URIError):
106
109
"""Raised when validation operation fails."""
107
-
108
-
def __init__(self, value: Any, field: str, reason: str, details: Optional[str] = None):
110
+
111
+
def __init__(
112
+
self, value: Any, field: str, reason: str, details: Optional[str] = None
113
+
):
109
114
self.value = value
110
115
self.field = field
111
116
self.reason = reason
···
113
118
message = f"Validation failed: value '{value}' for field '{field}' {reason}"
114
119
if details:
115
120
message += f" ({details})"
116
-
super().__init__(message)
121
+
super().__init__(message)
+61
-17
src/atpasser/uri/handle.py
+61
-17
src/atpasser/uri/handle.py
···
32
32
"""
33
33
34
34
if len(handle) > 253:
35
-
raise InvalidHandleError(handle, "exceeds maximum length", f"Handle length {len(handle)} exceeds maximum allowed length of 253 characters")
35
+
raise InvalidHandleError(
36
+
handle,
37
+
"exceeds maximum length",
38
+
f"Handle length {len(handle)} exceeds maximum allowed length of 253 characters",
39
+
)
36
40
37
41
labels = handle.lower().split(".")
38
42
39
43
if len(labels) < 2:
40
-
raise InvalidHandleError(handle, "invalid format", "Handle must contain at least one dot separator, e.g., 'example.com'")
44
+
raise InvalidHandleError(
45
+
handle,
46
+
"invalid format",
47
+
"Handle must contain at least one dot separator, e.g., 'example.com'",
48
+
)
41
49
42
50
if labels[0] == "" or labels[-1] == "":
43
-
raise InvalidHandleError(handle, "invalid format", "Handle cannot start or end with a dot")
51
+
raise InvalidHandleError(
52
+
handle, "invalid format", "Handle cannot start or end with a dot"
53
+
)
44
54
45
55
for i, label in enumerate(labels):
46
56
if len(label) not in range(1, 64):
47
-
raise InvalidHandleError(handle, "segment length error", f"Handle segment {i+1} length {len(label)} is not in the 1-63 character range")
57
+
raise InvalidHandleError(
58
+
handle,
59
+
"segment length error",
60
+
f"Handle segment {i+1} length {len(label)} is not in the 1-63 character range",
61
+
)
48
62
charset = set(label)
49
63
validset = set("abcdefghijklmnopqrstuvwxyz0123456789-")
50
64
if not charset.issubset(validset):
51
65
invalid_chars = charset - validset
52
-
raise InvalidHandleError(handle, "contains invalid characters", f"Handle segment {i+1} contains invalid characters: {', '.join(invalid_chars)}")
66
+
raise InvalidHandleError(
67
+
handle,
68
+
"contains invalid characters",
69
+
f"Handle segment {i+1} contains invalid characters: {', '.join(invalid_chars)}",
70
+
)
53
71
if label.startswith("-") or label.endswith("-"):
54
-
raise InvalidHandleError(handle, "invalid format", f"Handle segment {i+1} cannot start or end with a hyphen")
72
+
raise InvalidHandleError(
73
+
handle,
74
+
"invalid format",
75
+
f"Handle segment {i+1} cannot start or end with a hyphen",
76
+
)
55
77
56
78
tld = labels[-1]
57
79
if tld[0] in "0123456789":
58
-
raise InvalidHandleError(handle, "invalid format", "Handle's top-level domain cannot start with a digit")
80
+
raise InvalidHandleError(
81
+
handle,
82
+
"invalid format",
83
+
"Handle's top-level domain cannot start with a digit",
84
+
)
59
85
60
86
self.handle = handle
61
87
···
111
137
answers = dns.resolver.resolve("_atproto." + self.handle, "TXT")
112
138
except Exception as e:
113
139
answers = []
114
-
140
+
115
141
for answer in answers:
116
142
answer_str = str(answer)
117
143
if answer_str.startswith('"did='):
···
122
148
# Continue trying other records or methods
123
149
continue
124
150
except Exception as e:
125
-
raise ResolutionError(self.handle, "DNS resolution", f"Error parsing DNS TXT record: {str(e)}")
126
-
151
+
raise ResolutionError(
152
+
self.handle,
153
+
"DNS resolution",
154
+
f"Error parsing DNS TXT record: {str(e)}",
155
+
)
156
+
127
157
# If DNS resolution fails, try HTTP method
128
158
try:
129
-
response = requests.get(f"https://{self.handle}/.well-known/atproto-did")
159
+
response = requests.get(
160
+
f"https://{self.handle}/.well-known/atproto-did"
161
+
)
130
162
if response.status_code // 100 != 2:
131
163
return None
132
-
164
+
133
165
# Some websites may return incorrect Content-Type, so here we only warn without throwing an exception
134
166
content_type = response.headers.get("Content-Type")
135
167
if content_type != "text/plain" and content_type:
136
168
# Log warning but don't block processing
137
169
pass
138
-
170
+
139
171
try:
140
172
return DID(response.text.strip())
141
173
except InvalidDIDError:
142
174
return None
143
175
except requests.RequestException as e:
144
-
raise ResolutionError(self.handle, "HTTP request", f"Error requesting well-known endpoint: {str(e)}")
176
+
raise ResolutionError(
177
+
self.handle,
178
+
"HTTP request",
179
+
f"Error requesting well-known endpoint: {str(e)}",
180
+
)
145
181
except Exception as e:
146
-
raise ResolutionError(self.handle, "HTTP parsing", f"Error parsing HTTP response: {str(e)}")
147
-
182
+
raise ResolutionError(
183
+
self.handle,
184
+
"HTTP parsing",
185
+
f"Error parsing HTTP response: {str(e)}",
186
+
)
187
+
148
188
except Exception as e:
149
189
if isinstance(e, ResolutionError):
150
190
raise
151
-
raise ResolutionError(self.handle, "resolution", f"Unknown error occurred while resolving Handle: {str(e)}")
191
+
raise ResolutionError(
192
+
self.handle,
193
+
"resolution",
194
+
f"Unknown error occurred while resolving Handle: {str(e)}",
195
+
)
+82
-20
src/atpasser/uri/nsid.py
+82
-20
src/atpasser/uri/nsid.py
···
36
36
if "#" in nsid:
37
37
parts = nsid.split("#", 1)
38
38
if len(parts) != 2:
39
-
raise InvalidNSIDError(nsid, "invalid format", "NSID fragment format is incorrect")
39
+
raise InvalidNSIDError(
40
+
nsid, "invalid format", "NSID fragment format is incorrect"
41
+
)
40
42
nsidWithoutFragment, fragment = parts
41
43
else:
42
44
nsidWithoutFragment, fragment = nsid, None
···
45
47
if not set([x for x in nsidWithoutFragment]).issubset(
46
48
set([chr(i) for i in range(0x80)])
47
49
):
48
-
raise InvalidNSIDError(nsid, "contains invalid characters", "NSID must only contain ASCII characters")
50
+
raise InvalidNSIDError(
51
+
nsid,
52
+
"contains invalid characters",
53
+
"NSID must only contain ASCII characters",
54
+
)
49
55
50
56
# Check length
51
57
if len(nsidWithoutFragment) > 317:
52
-
raise InvalidNSIDError(nsid, "exceeds maximum length", f"NSID length {len(nsidWithoutFragment)} exceeds maximum allowed length of 317 characters")
58
+
raise InvalidNSIDError(
59
+
nsid,
60
+
"exceeds maximum length",
61
+
f"NSID length {len(nsidWithoutFragment)} exceeds maximum allowed length of 317 characters",
62
+
)
53
63
54
64
segments = nsidWithoutFragment.split(".")
55
65
56
66
# Check for leading or trailing dots
57
67
if nsidWithoutFragment.startswith(".") or nsidWithoutFragment.endswith("."):
58
-
raise InvalidNSIDError(nsid, "invalid format", "NSID cannot start or end with a dot")
68
+
raise InvalidNSIDError(
69
+
nsid, "invalid format", "NSID cannot start or end with a dot"
70
+
)
59
71
60
72
# Check segment count
61
73
if len(segments) < 3:
62
-
raise InvalidNSIDError(nsid, "invalid format", f"NSID must contain at least 3 segments, currently has {len(segments)}")
74
+
raise InvalidNSIDError(
75
+
nsid,
76
+
"invalid format",
77
+
f"NSID must contain at least 3 segments, currently has {len(segments)}",
78
+
)
63
79
64
80
domainAuthority = [segment.lower() for segment in segments[0:-1]]
65
81
66
82
# Check domain authority length
67
83
if len(".".join(domainAuthority)) > 253:
68
-
raise InvalidNSIDError(nsid, "domain authority length exceeds limit", "Domain authority part length exceeds 253 characters")
84
+
raise InvalidNSIDError(
85
+
nsid,
86
+
"domain authority length exceeds limit",
87
+
"Domain authority part length exceeds 253 characters",
88
+
)
69
89
70
90
# Check each domain segment
71
91
for i, segment in enumerate(domainAuthority):
72
92
if len(segment) > 63 or segment == "":
73
-
raise InvalidNSIDError(nsid, "segment length error", f"Domain authority segment {i+1} length is not in the 1-63 character range")
93
+
raise InvalidNSIDError(
94
+
nsid,
95
+
"segment length error",
96
+
f"Domain authority segment {i+1} length is not in the 1-63 character range",
97
+
)
74
98
if not set(segment).issubset(set("abcdefghijklmnopqrstuvwxyz0123456789-")):
75
-
invalid_chars = set(segment) - set("abcdefghijklmnopqrstuvwxyz0123456789-")
76
-
raise InvalidNSIDError(nsid, "contains invalid characters", f"Domain authority segment {i+1} contains invalid characters: {', '.join(invalid_chars)}")
99
+
invalid_chars = set(segment) - set(
100
+
"abcdefghijklmnopqrstuvwxyz0123456789-"
101
+
)
102
+
raise InvalidNSIDError(
103
+
nsid,
104
+
"contains invalid characters",
105
+
f"Domain authority segment {i+1} contains invalid characters: {', '.join(invalid_chars)}",
106
+
)
77
107
if segment.startswith("-") or segment.endswith("-"):
78
-
raise InvalidNSIDError(nsid, "invalid format", f"Domain authority segment {i+1} cannot start or end with a hyphen")
79
-
108
+
raise InvalidNSIDError(
109
+
nsid,
110
+
"invalid format",
111
+
f"Domain authority segment {i+1} cannot start or end with a hyphen",
112
+
)
113
+
80
114
# Check if top-level domain starts with a digit
81
115
if segments[0][0] in "0123456789":
82
-
raise InvalidNSIDError(nsid, "invalid format", "NSID's top-level domain cannot start with a digit")
116
+
raise InvalidNSIDError(
117
+
nsid,
118
+
"invalid format",
119
+
"NSID's top-level domain cannot start with a digit",
120
+
)
83
121
84
122
self.domainAuthority = domainAuthority
85
123
self.domainAuthorityAsText = ".".join(domainAuthority)
···
88
126
89
127
# Check name
90
128
if name == "" or len(name) > 63:
91
-
raise InvalidNSIDError(nsid, "name length error", "NSID name cannot be empty and length cannot exceed 63 characters")
129
+
raise InvalidNSIDError(
130
+
nsid,
131
+
"name length error",
132
+
"NSID name cannot be empty and length cannot exceed 63 characters",
133
+
)
92
134
93
135
if not set(name).issubset(
94
136
set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
95
137
):
96
-
invalid_chars = set(name) - set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
97
-
raise InvalidNSIDError(nsid, "contains invalid characters", f"NSID name contains invalid characters: {', '.join(invalid_chars)}")
138
+
invalid_chars = set(name) - set(
139
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
140
+
)
141
+
raise InvalidNSIDError(
142
+
nsid,
143
+
"contains invalid characters",
144
+
f"NSID name contains invalid characters: {', '.join(invalid_chars)}",
145
+
)
98
146
99
147
if name[0] in "0123456789":
100
-
raise InvalidNSIDError(nsid, "invalid format", "NSID name cannot start with a digit")
148
+
raise InvalidNSIDError(
149
+
nsid, "invalid format", "NSID name cannot start with a digit"
150
+
)
101
151
102
152
self.name = name
103
153
104
154
# Check fragment
105
155
if fragment != None:
106
156
if fragment == "" or len(fragment) > 63:
107
-
raise InvalidNSIDError(nsid, "fragment length error", "NSID fragment cannot be empty and length cannot exceed 63 characters")
157
+
raise InvalidNSIDError(
158
+
nsid,
159
+
"fragment length error",
160
+
"NSID fragment cannot be empty and length cannot exceed 63 characters",
161
+
)
108
162
109
163
if not set(fragment).issubset(
110
164
set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
111
165
):
112
-
invalid_chars = set(fragment) - set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
113
-
raise InvalidNSIDError(nsid, "contains invalid characters", f"NSID fragment contains invalid characters: {', '.join(invalid_chars)}")
166
+
invalid_chars = set(fragment) - set(
167
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
168
+
)
169
+
raise InvalidNSIDError(
170
+
nsid,
171
+
"contains invalid characters",
172
+
f"NSID fragment contains invalid characters: {', '.join(invalid_chars)}",
173
+
)
114
174
115
175
if fragment[0] in "0123456789":
116
-
raise InvalidNSIDError(nsid, "invalid format", "NSID fragment cannot start with a digit")
176
+
raise InvalidNSIDError(
177
+
nsid, "invalid format", "NSID fragment cannot start with a digit"
178
+
)
117
179
118
180
self.fragment = fragment
119
181
+28
-8
src/atpasser/uri/restricted.py
+28
-8
src/atpasser/uri/restricted.py
···
4
4
from .exceptions import InvalidRestrictedURIError, InvalidURIError
5
5
6
6
7
-
8
7
class RestrictedURI(URI):
9
8
"""A class representing a restricted AT Protocol URI for record access.
10
9
···
41
40
except InvalidURIError:
42
41
raise
43
42
except Exception as e:
44
-
raise InvalidURIError(uri, "base URI validation failed", f"Failed to parse base URI: {str(e)}")
43
+
raise InvalidURIError(
44
+
uri, "base URI validation failed", f"Failed to parse base URI: {str(e)}"
45
+
)
45
46
46
47
if self.query is not None:
47
-
raise InvalidRestrictedURIError(uri, "query parameters not supported", "Restricted URI cannot contain query parameters")
48
+
raise InvalidRestrictedURIError(
49
+
uri,
50
+
"query parameters not supported",
51
+
"Restricted URI cannot contain query parameters",
52
+
)
48
53
49
54
if self.fragment is not None:
50
-
raise InvalidRestrictedURIError(uri, "fragments not supported", "Restricted URI cannot contain fragments")
55
+
raise InvalidRestrictedURIError(
56
+
uri,
57
+
"fragments not supported",
58
+
"Restricted URI cannot contain fragments",
59
+
)
51
60
52
61
if self.authority is None:
53
-
raise InvalidRestrictedURIError(uri, "invalid authority", "Restricted URI must contain a valid DID or Handle")
62
+
raise InvalidRestrictedURIError(
63
+
uri,
64
+
"invalid authority",
65
+
"Restricted URI must contain a valid DID or Handle",
66
+
)
54
67
55
68
try:
56
69
if len(self.path) == 0:
···
64
77
self.collection = nsid.NSID(self.path[0])
65
78
self.rkey = rKey.RKey(self.path[1])
66
79
else:
67
-
raise InvalidRestrictedURIError(uri, "too many path segments", f"Restricted URI can have at most 2 path segments, currently has {len(self.path)}")
80
+
raise InvalidRestrictedURIError(
81
+
uri,
82
+
"too many path segments",
83
+
f"Restricted URI can have at most 2 path segments, currently has {len(self.path)}",
84
+
)
68
85
except Exception as e:
69
86
if isinstance(e, (InvalidRestrictedURIError, InvalidURIError)):
70
87
raise
71
-
raise InvalidRestrictedURIError(uri, "parsing error", f"Error occurred while parsing Restricted URI: {str(e)}")
72
-
88
+
raise InvalidRestrictedURIError(
89
+
uri,
90
+
"parsing error",
91
+
f"Error occurred while parsing Restricted URI: {str(e)}",
92
+
)
+28
-11
src/atpasser/uri/rkey.py
+28
-11
src/atpasser/uri/rkey.py
···
30
30
"""
31
31
32
32
if recordKey == "":
33
-
raise InvalidRKeyError(recordKey, "record key is empty", "Record key cannot be empty")
34
-
33
+
raise InvalidRKeyError(
34
+
recordKey, "record key is empty", "Record key cannot be empty"
35
+
)
36
+
35
37
if len(recordKey) > 512:
36
-
raise InvalidRKeyError(recordKey, "exceeds maximum length", f"Record key length {len(recordKey)} exceeds maximum allowed length of 512 characters")
38
+
raise InvalidRKeyError(
39
+
recordKey,
40
+
"exceeds maximum length",
41
+
f"Record key length {len(recordKey)} exceeds maximum allowed length of 512 characters",
42
+
)
37
43
38
44
if recordKey == "..":
39
-
raise InvalidRKeyError(recordKey, "reserved value", "'..' is a reserved value and cannot be used as a record key")
40
-
45
+
raise InvalidRKeyError(
46
+
recordKey,
47
+
"reserved value",
48
+
"'..' is a reserved value and cannot be used as a record key",
49
+
)
50
+
41
51
if recordKey == ".":
42
-
raise InvalidRKeyError(recordKey, "reserved value", "'.' is a reserved value and cannot be used as a record key")
52
+
raise InvalidRKeyError(
53
+
recordKey,
54
+
"reserved value",
55
+
"'.' is a reserved value and cannot be used as a record key",
56
+
)
43
57
44
-
valid_chars = set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_:~")
58
+
valid_chars = set(
59
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_:~"
60
+
)
45
61
if not set(recordKey).issubset(valid_chars):
46
62
invalid_chars = set(recordKey) - valid_chars
47
-
raise InvalidRKeyError(recordKey, "contains invalid characters", f"Record key contains invalid characters: {', '.join(invalid_chars)}")
63
+
raise InvalidRKeyError(
64
+
recordKey,
65
+
"contains invalid characters",
66
+
f"Record key contains invalid characters: {', '.join(invalid_chars)}",
67
+
)
48
68
49
69
self.recordKey = recordKey
50
-
51
70
52
71
def __str__(self) -> str:
53
72
"""Convert the Record Key object to its string representation.
···
56
75
str: The canonical record key string.
57
76
"""
58
77
return self.recordKey
59
-
60
78
61
79
def __eq__(self, value: object, /) -> bool:
62
80
"""Check if two RKey objects represent the same record key.
···
72
90
return str(self) == str(value)
73
91
else:
74
92
return False
75
-