+29
-1
poetry.lock
+29
-1
poetry.lock
···
272
272
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
273
273
274
274
[[package]]
275
+
name = "jsonpath-ng"
276
+
version = "1.7.0"
277
+
description = "A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators and providing clear AST for metaprogramming."
278
+
optional = false
279
+
python-versions = "*"
280
+
groups = ["main"]
281
+
files = [
282
+
{file = "jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c"},
283
+
{file = "jsonpath_ng-1.7.0-py2-none-any.whl", hash = "sha256:898c93fc173f0c336784a3fa63d7434297544b7198124a68f9a3ef9597b0ae6e"},
284
+
{file = "jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6"},
285
+
]
286
+
287
+
[package.dependencies]
288
+
ply = "*"
289
+
290
+
[[package]]
275
291
name = "lxml"
276
292
version = "6.0.1"
277
293
description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
···
412
428
groups = ["main"]
413
429
files = [
414
430
{file = "morphys-1.0-py2.py3-none-any.whl", hash = "sha256:76d6dbaa4d65f597e59d332c81da786d83e4669387b9b2a750cfec74e7beec20"},
431
+
]
432
+
433
+
[[package]]
434
+
name = "ply"
435
+
version = "3.11"
436
+
description = "Python Lex & Yacc"
437
+
optional = false
438
+
python-versions = "*"
439
+
groups = ["main"]
440
+
files = [
441
+
{file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"},
442
+
{file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"},
415
443
]
416
444
417
445
[[package]]
···
586
614
[metadata]
587
615
lock-version = "2.1"
588
616
python-versions = ">=3.13"
589
-
content-hash = "f1ed7d854143c5a27897e3599cafa301e939d46f52b876acaae299f57fb47950"
617
+
content-hash = "91faa3780c077d6f2d96269f782e106c25e374a61a51ecf7a8e56bfdf5564c1c"
+1
-1
pyproject.toml
+1
-1
pyproject.toml
···
5
5
authors = [{ name = "diaowinner", email = "diaowinner@qq.com" }]
6
6
readme = "README.md"
7
7
requires-python = ">=3.13"
8
-
dependencies = ["py-cid (>=0.3.0,<0.4.0)", "cbor2 (>=5.7.0,<5.8.0)", "dnspython (>=2.7.0,<3.0.0)", "requests (>=2.32.5,<3.0.0)", "pyld[requests] (>=2.0.4,<3.0.0)"]
8
+
dependencies = ["py-cid (>=0.3.0,<0.4.0)", "cbor2 (>=5.7.0,<5.8.0)", "dnspython (>=2.7.0,<3.0.0)", "requests (>=2.32.5,<3.0.0)", "pyld[requests] (>=2.0.4,<3.0.0)", "jsonpath-ng (>=1.7.0,<2.0.0)"]
9
9
license = "MIT OR Apache-2.0"
10
10
license-files = ["LICEN[CS]E.*"]
11
11
+113
src/atpasser/uri/__init__.py
+113
src/atpasser/uri/__init__.py
···
1
+
import urllib.parse as up
2
+
from atpasser import handle, did
3
+
import jsonpath_ng
4
+
5
+
6
+
class URI:
7
+
"""
8
+
A class representing an AT URI.
9
+
10
+
Attributes:
11
+
uri (str): The AT URI.
12
+
"""
13
+
14
+
def __init__(self, uri: str) -> None:
15
+
"""
16
+
Initalizes an AT URI.
17
+
18
+
Attributes:
19
+
uri (str): The AT URI.
20
+
"""
21
+
22
+
if not set(uri).issubset(set([chr(i) for i in range(0x80)])):
23
+
raise ValueError("invalid char in uri")
24
+
25
+
if len(uri) > 8000:
26
+
raise ValueError("uri longer than 8000 chars") # the doc says 8 "kilobytes"
27
+
28
+
if not uri.startswith("at://"):
29
+
raise ValueError("not starts with at://")
30
+
31
+
burner = uri[5:].split("#")
32
+
33
+
try:
34
+
fragment = up.unquote(burner[1])
35
+
except:
36
+
fragment = None
37
+
38
+
try:
39
+
query = up.unquote(burner[0].split("?")[1])
40
+
except:
41
+
query = None
42
+
43
+
path = [up.unquote(segment) for segment in burner[0].split("/")[1:]]
44
+
if len(path) > 0 and path[-1] == "":
45
+
raise ValueError("trailing slash")
46
+
47
+
authorityValue = up.unquote(burner[0].split("/")[0])
48
+
49
+
p = up.urlparse(up.unquote("//" + authorityValue))
50
+
if p.username is not None or p.password is not None:
51
+
raise ValueError("userinfo unsupported")
52
+
53
+
# We decided not to detect if it's a handle there
54
+
#
55
+
# try:
56
+
# authority = handle.Handle(authorityValue)
57
+
# except:
58
+
# try:
59
+
# authority = did.DID(authorityValue)
60
+
# except:
61
+
# raise ValueError("authority is neither handle nor tid")
62
+
63
+
if fragment != None:
64
+
self.fragment = jsonpath_ng.parse(fragment)
65
+
self.fragmentAsText = up.quote(fragment)
66
+
67
+
if query != None:
68
+
self.query = up.parse_qs(query)
69
+
self.queryAsText = up.quote(query)
70
+
else:
71
+
self.query, self.queryAsText = None, None
72
+
73
+
self.path = path
74
+
pathAsText = "/".join([up.quote(i) for i in path])
75
+
self.pathAsText = pathAsText
76
+
77
+
self.authorityAsText = authorityValue
78
+
try:
79
+
authority = handle.Handle(authorityValue)
80
+
except:
81
+
try:
82
+
authority = did.DID(authorityValue)
83
+
except:
84
+
authority = None
85
+
self.authority = authority
86
+
87
+
unescapedAV = up.quote(authorityValue).replace("%3A", ":")
88
+
self.uri = "at://{}/{}{}{}".format(
89
+
unescapedAV,
90
+
pathAsText,
91
+
"?" + query if query != None else "",
92
+
"?" + fragment if fragment != None else "",
93
+
)
94
+
95
+
def __str__(self) -> str:
96
+
"""
97
+
98
+
Convert the RecordKey to a string by given the URI.
99
+
"""
100
+
return self.uri
101
+
102
+
def __eq__(self, value: object, /) -> bool:
103
+
"""
104
+
105
+
Check if the 2 values are exactly the same.
106
+
"""
107
+
108
+
if isinstance(value, URI):
109
+
110
+
return str(self) == str(value)
111
+
else:
112
+
113
+
raise TypeError