satori-python-core 0.17.2__tar.gz → 0.17.5__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satori-python-core
3
- Version: 0.17.2
3
+ Version: 0.17.5
4
4
  Summary: Satori Protocol SDK for python, specify common part
5
5
  Home-page: https://github.com/RF-Tar-Railt/satori-python
6
6
  Author-Email: RF-Tar-Railt <rf_tar_railt@qq.com>
@@ -76,6 +76,7 @@ pip install satori-python-server
76
76
  | Satori | `pip install satori-python-adapter-satori` | satori.adapters.satori |
77
77
  | OneBot V11 | `pip install satori-python-adapter-onebot11` | satori.adapters.onebot11.forward, satori.adapters.onebot11.reverse |
78
78
  | Console | `pip install satori-python-adapter-console` | satori.adapters.console |
79
+ | Milky | `pip install satori-python-adapter-milky` | satori.adapters.milky.main, satori.adapters.milky.webhook |
79
80
 
80
81
  ### 社区适配器
81
82
 
@@ -52,6 +52,7 @@ pip install satori-python-server
52
52
  | Satori | `pip install satori-python-adapter-satori` | satori.adapters.satori |
53
53
  | OneBot V11 | `pip install satori-python-adapter-onebot11` | satori.adapters.onebot11.forward, satori.adapters.onebot11.reverse |
54
54
  | Console | `pip install satori-python-adapter-console` | satori.adapters.console |
55
+ | Milky | `pip install satori-python-adapter-milky` | satori.adapters.milky.main, satori.adapters.milky.webhook |
55
56
 
56
57
  ### 社区适配器
57
58
 
@@ -23,7 +23,7 @@ classifiers = [
23
23
  "Programming Language :: Python :: 3.12",
24
24
  "Operating System :: OS Independent",
25
25
  ]
26
- version = "0.17.2"
26
+ version = "0.17.5"
27
27
 
28
28
  [project.license]
29
29
  text = "MIT"
@@ -67,7 +67,7 @@ excludes = [
67
67
  composite = [
68
68
  "isort ./src/ ./example/",
69
69
  "black ./src/ ./example/",
70
- "ruff check ./src/ ./example/",
70
+ "ruff check",
71
71
  ]
72
72
 
73
73
  [tool.pdm.version]
@@ -91,7 +91,10 @@ extra_standard_library = [
91
91
  line-length = 120
92
92
  target-version = "py310"
93
93
  exclude = [
94
- "exam.py",
94
+ "exam_qps.py",
95
+ "exam1.py",
96
+ "exam2.py",
97
+ "src/satori/_vendor/*",
95
98
  ]
96
99
 
97
100
  [tool.ruff.lint]
@@ -109,7 +112,7 @@ ignore = [
109
112
  "F403",
110
113
  "F405",
111
114
  "C901",
112
- "UP038",
115
+ "T201",
113
116
  ]
114
117
 
115
118
  [tool.pyright]
@@ -42,4 +42,4 @@ from .model import Role as Role
42
42
  from .model import Upload as Upload
43
43
  from .model import User as User
44
44
 
45
- __version__ = "0.17.2"
45
+ __version__ = "0.17.5"
@@ -0,0 +1,197 @@
1
+ """
2
+ Name: fleep.py
3
+ Description: File format determination library
4
+ Author: Mykyta Paliienko
5
+ License: MIT
6
+ """
7
+
8
+ # fmt: off
9
+
10
+ data = [
11
+ {"type": "raster-image", "extension": "bmp", "mime": "image/bmp", "offset": 0, "signature": ["42 4D"]},
12
+ {"type": "raster-image", "extension": "gif", "mime": "image/gif", "offset": 0, "signature": ["47 49 46 38"]},
13
+ {"type": "raster-image", "extension": "jpg", "mime": "image/jpeg", "offset": 0, "signature": ["FF D8 FF"]},
14
+ {"type": "raster-image", "extension": "jp2", "mime": "image/jp2", "offset": 0, "signature": ["00 00 00 0C 6A 50 20 20"]},
15
+ {"type": "raster-image", "extension": "png", "mime": "image/png", "offset": 0, "signature": ["89 50 4E 47 0D 0A 1A 0A"]},
16
+ {"type": "raster-image", "extension": "webp", "mime": "image/webp", "offset": 8, "signature": ["57 45 42 50"]},
17
+ {"type": "raster-image", "extension": "ico", "mime": "image/x-icon", "offset": 0, "signature": ["00 00 01 00"]},
18
+ {"type": "raster-image", "extension": "psd", "mime": "image/vnd.adobe.photoshop", "offset": 0, "signature": ["38 42 50 53"]},
19
+ {"type": "raster-image", "extension": "tiff", "mime": "image/tiff", "offset": 0, "signature": ["49 20 49", "49 49 2A 00", "4D 4D 00 2A", "4D 4D 00 2B"]},
20
+ {"type": "raw-image", "extension": "raw", "mime": "application/octet-stream", "offset": 0, "signature": ["49 49 55 00"]},
21
+ {"type": "raw-image", "extension": "arw", "mime": "application/octet-stream", "offset": 0, "signature": ["49 49 2A 00"]},
22
+ {"type": "raw-image", "extension": "x3f", "mime": "application/octet-stream", "offset": 0, "signature": ["46 4F 56 62"]},
23
+ {"type": "raw-image", "extension": "srw", "mime": "application/octet-stream", "offset": 0, "signature": ["4D 4D 00 2A"]},
24
+ {"type": "raw-image", "extension": "pef", "mime": "application/octet-stream", "offset": 0, "signature": ["4D 4D 00 2A"]},
25
+ {"type": "raw-image", "extension": "rw2", "mime": "application/octet-stream", "offset": 0, "signature": ["49 49 55 00"]},
26
+ {"type": "raw-image", "extension": "nef", "mime": "application/octet-stream", "offset": 0, "signature": ["4D 4D 00 2A"]},
27
+ {"type": "raw-image", "extension": "nrw", "mime": "application/octet-stream", "offset": 0, "signature": ["49 49 2A 00"]},
28
+ {"type": "raw-image", "extension": "raf", "mime": "application/octet-stream", "offset": 0, "signature": ["46 55 4A 49"]},
29
+ {"type": "raw-image", "extension": "erf", "mime": "application/octet-stream", "offset": 0, "signature": ["4D 4D 00 2A"]},
30
+ {"type": "raw-image", "extension": "crw", "mime": "application/octet-stream", "offset": 0, "signature": ["49 49 1A 00"]},
31
+ {"type": "raw-image", "extension": "cr2", "mime": "application/octet-stream", "offset": 0, "signature": ["49 49 2A 00"]},
32
+ {"type": "raw-image", "extension": "orf", "mime": "application/octet-stream", "offset": 0, "signature": ["49 49 52 4F", "49 49 52 53"]},
33
+ {"type": "raw-image", "extension": "dng", "mime": "application/octet-stream", "offset": 0, "signature": ["4D 4D 00 2A", "49 49 2A 00"]},
34
+ {"type": "vector-image", "extension": "ai", "mime": "application/postscript", "offset": 0, "signature": ["25 50 44 46"]},
35
+ {"type": "vector-image", "extension": "eps", "mime": "application/postscript", "offset": 0, "signature": ["C5 D0 D3 C6", "25 21 50 53 2D 41 64 6F"]},
36
+ {"type": "3d-image", "extension": "obj", "mime": "text/plain", "offset": 2, "signature": ["4D 61 78 32 4F 62 6A", "42 6C 65 6E 64 65 72"]},
37
+ {"type": "3d-image", "extension": "mtl", "mime": "text/plain", "offset": 2, "signature": ["4D 61 78 32 4D 74 6C", "42 6C 65 6E 64 65 72 20 4D 54 4C 20 46 69 6C 65"]},
38
+ {"type": "3d-image", "extension": "xsi", "mime": "text/plain", "offset": 0, "signature": ["78 73 69"]},
39
+ {"type": "3d-image", "extension": "ply", "mime": "text/plain", "offset": 50, "signature": ["70 6C 79"]},
40
+ {"type": "3d-image", "extension": "ma", "mime": "text/plain", "offset": 2, "signature": ["4D 61 79 61"]},
41
+ {"type": "3d-image", "extension": "wrl", "mime": "text/plain", "offset": 1, "signature": ["56 52 4D 4C"]},
42
+ {"type": "3d-image", "extension": "x3d", "mime": "application/xml", "offset": 50, "signature": ["58 33 44"]},
43
+ {"type": "3d-image", "extension": "fbx", "mime": "application/octet-stream", "offset": 2, "signature": ["46 42 58"]},
44
+ {"type": "3d-image", "extension": "ms3d", "mime": "application/octet-stream", "offset": 0, "signature": ["4D 53 33 44"]},
45
+ {"type": "3d-image", "extension": "c4d", "mime": "application/octet-stream", "offset": 0, "signature": ["58 43 34 44 43 34 44 36"]},
46
+ {"type": "audio", "extension": "aiff", "mime": "audio/aiff", "offset": 0, "signature": ["46 4F 52 4D 00"]},
47
+ {"type": "audio", "extension": "aac", "mime": "audio/aac", "offset": 0, "signature": ["FF F1", "FF F9"]},
48
+ {"type": "audio", "extension": "midi", "mime": "audio/midi", "offset": 0, "signature": ["4D 54 68 64"]},
49
+ {"type": "audio", "extension": "mp3", "mime": "audio/mpeg", "offset": 0, "signature": ["49 44 33", "FF FB"]},
50
+ {"type": "audio", "extension": "m4a", "mime": "audio/mp4", "offset": 4, "signature": ["66 74 79 70 4D 34 41 20"]},
51
+ {"type": "audio", "extension": "oga", "mime": "audio/ogg", "offset": 0, "signature": ["4F 67 67 53 00 02 00 00"]},
52
+ {"type": "audio", "extension": "wav", "mime": "audio/wav", "offset": 0, "signature": ["52 49 46 46"]},
53
+ {"type": "audio", "extension": "wma", "mime": "audio/x-ms-wma", "offset": 0, "signature": ["30 26 B2 75 8E 66 CF 11"]},
54
+ {"type": "audio", "extension": "flac", "mime": "audio/flac", "offset": 0, "signature": ["66 4C 61 43 00 00 00 22"]},
55
+ {"type": "audio", "extension": "mka", "mime": "audio/x-matroska", "offset": 31, "signature": ["6D 61 74 72 6F 73 6B 61"]},
56
+ {"type": "audio", "extension": "au", "mime": "audio/basic", "offset": 0, "signature": ["2E 73 6E 64"]},
57
+ {"type": "audio", "extension": "ra", "mime": "application/octet-stream", "offset": 0, "signature": ["2E 52 4D 46"]},
58
+ {"type": "audio", "extension": "amr", "mime": "application/octet-stream", "offset": 0, "signature": ["23 21 41 4D"]},
59
+ {"type": "audio", "extension": "ac3", "mime": "application/octet-stream", "offset": 0, "signature": ["0B 77"]},
60
+ {"type": "audio", "extension": "voc", "mime": "application/octet-stream", "offset": 0, "signature": ["43 72 65 61 74 69 76 65"]},
61
+ {"type": "video", "extension": "3g2", "mime": "video/3gpp2", "offset": 4, "signature": ["66 74 79 70 33 67 70"]},
62
+ {"type": "video", "extension": "3gp", "mime": "video/3gpp", "offset": 4, "signature": ["66 74 79 70 33 67 70"]},
63
+ {"type": "video", "extension": "avi", "mime": "video/avi", "offset": 8, "signature": ["41 56 49 20 4C 49 53 54"]},
64
+ {"type": "video", "extension": "flv", "mime": "video/x-flv", "offset": 0, "signature": ["46 4C 56"]},
65
+ {"type": "video", "extension": "mxf", "mime": "application/mxf", "offset": 0, "signature": ["06 0E 2B 34"]},
66
+ {"type": "video", "extension": "m4v", "mime": "video/mp4", "offset": 4, "signature": ["66 74 79 70 4D 34 56 20", "66 74 79 70 6D 70 34 32"]},
67
+ {"type": "video", "extension": "mkv", "mime": "video/x-matroska", "offset": 31, "signature": ["6D 61 74 72 6F 73 6B 61"]},
68
+ {"type": "video", "extension": "mov", "mime": "video/quicktime", "offset": 4, "signature": ["66 74 79 70 71 74 20 20", "6D 6F 6F 76", "66 72 65 65", "6D 64 61 74", "77 69 64 65", "70 6E 6F 74", "73 6B 69 70"]},
69
+ {"type": "video", "extension": "mp4", "mime": "video/mp4", "offset": 4, "signature": ["66 74 79 70 4D 53 4E 56", "66 74 79 70 69 73 6F 6D"]},
70
+ {"type": "video", "extension": "swf", "mime": "application/vnd.adobe.flash-movie", "offset": 0, "signature": ["43 57 53", "46 57 53"]},
71
+ {"type": "video", "extension": "mpg", "mime": "video/mpeg", "offset": 0, "signature": ["00 00 01 BA"]},
72
+ {"type": "video", "extension": "vob", "mime": "video/dvd", "offset": 0, "signature": ["00 00 01 BA"]},
73
+ {"type": "video", "extension": "wmv", "mime": "video/x-ms-wmv", "offset": 0, "signature": ["30 26 B2 75 8E 66 CF 11"]},
74
+ {"type": "video", "extension": "asf", "mime": "video/x-ms-asf", "offset": 0, "signature": ["30 26 B2 75 8E 66 CF 11"]},
75
+ {"type": "video", "extension": "ogv", "mime": "video/ogg", "offset": 0, "signature": ["4F 67 67 53 00 02 00 00"]},
76
+ {"type": "video", "extension": "webm", "mime": "video/webm", "offset": 0, "signature": ["1A 45 DF A3"]},
77
+ {"type": "document", "extension": "odt", "mime": "application/vnd.oasis.opendocument.text", "offset": 73, "signature": ["74 65 78 74"]},
78
+ {"type": "document", "extension": "odp", "mime": "application/vnd.oasis.opendocument.presentation", "offset": 73, "signature": ["70 72 65 73 65 6E 74 61 74 69 6F 6E"]},
79
+ {"type": "document", "extension": "ods", "mime": "application/vnd.oasis.opendocument.spreadsheet", "offset": 73, "signature": ["73 70 72 65 61 64 73 68 65 65 74"]},
80
+ {"type": "document", "extension": "doc", "mime": "application/vnd.ms-excel", "offset": 0, "signature": ["D0 CF 11 E0 A1 B1 1A E1", "50 4B 03 04 14 00 06 00"]},
81
+ {"type": "document", "extension": "pps", "mime": "application/vnd.ms-powerpoint", "offset": 0, "signature": ["D0 CF 11 E0 A1 B1 1A E1", "50 4B 03 04 14 00 06 00"]},
82
+ {"type": "document", "extension": "ppt", "mime": "application/vnd.ms-powerpoint", "offset": 0, "signature": ["D0 CF 11 E0 A1 B1 1A E1", "50 4B 03 04 14 00 06 00"]},
83
+ {"type": "document", "extension": "xls", "mime": "application/vnd.ms-excel", "offset": 0, "signature": ["D0 CF 11 E0 A1 B1 1A E1", "50 4B 03 04 14 00 06 00"]},
84
+ {"type": "document", "extension": "docx", "mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "offset": 0, "signature": ["D0 CF 11 E0 A1 B1 1A E1", "50 4B 03 04 14 00 06 00"]},
85
+ {"type": "document", "extension": "pptx", "mime": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "offset": 0, "signature": ["D0 CF 11 E0 A1 B1 1A E1", "50 4B 03 04 14 00 06 00"]},
86
+ {"type": "document", "extension": "xlsx", "mime": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "offset": 0, "signature": ["D0 CF 11 E0 A1 B1 1A E1", "50 4B 03 04 14 00 06 00"]},
87
+ {"type": "document", "extension": "pages", "mime": "application/zip", "offset": 0, "signature": ["50 4B 03 04"]},
88
+ {"type": "document", "extension": "key", "mime": "application/zip", "offset": 0, "signature": ["50 4B 03 04"]},
89
+ {"type": "document", "extension": "numbers", "mime": "application/zip", "offset": 0, "signature": ["50 4B 03 04"]},
90
+ {"type": "document", "extension": "pdf", "mime": "application/pdf", "offset": 0, "signature": ["25 50 44 46"]},
91
+ {"type": "document", "extension": "rtf", "mime": "application/rtf", "offset": 0, "signature": ["7B 5C 72 74 66 31"]},
92
+ {"type": "document", "extension": "epub", "mime": "application/epub+zip", "offset": 0, "signature": ["50 4B 03 04"]},
93
+ {"type": "document", "extension": "xml", "mime": "application/xml", "offset": 2, "signature": ["78 6D 6C"]},
94
+ {"type": "archive", "extension": "7z", "mime": "application/x-7z-compressed", "offset": 0, "signature": ["37 7A BC AF 27 1C"]},
95
+ {"type": "archive", "extension": "rar", "mime": "application/vnd.rar", "offset": 0, "signature": ["52 61 72 21 1A 07 00", "52 61 72 21 1A 07 01 00"]},
96
+ {"type": "archive", "extension": "tar.z", "mime": "application/x-compress", "offset": 0, "signature": ["1F 9D", "1F A0"]},
97
+ {"type": "archive", "extension": "gz", "mime": "application/gzip", "offset": 0, "signature": ["1F 8B 08"]},
98
+ {"type": "archive", "extension": "zip", "mime": "application/zip", "offset": 0, "signature": ["50 4B 03 04", "50 4B 05 06", "50 4B 07 08"]},
99
+ {"type": "archive", "extension": "dmg", "mime": "application/x-apple-diskimage", "offset": 0, "signature": ["78 01 73 0D 62 62 60"]},
100
+ {"type": "archive", "extension": "iso", "mime": "application/octet-stream", "offset": 0, "signature": ["43 49 53 4F", "43 44 30 30 31"]},
101
+ {"type": "executable", "extension": "com", "mime": "application/x-msdownload", "offset": 0, "signature": ["4D 5A"]},
102
+ {"type": "executable", "extension": "exe", "mime": "application/vnd.microsoft.portable-executable", "offset": 0, "signature": ["4D 5A 90 00"]},
103
+ {"type": "executable", "extension": "jar", "mime": "application/java-archive", "offset": 0, "signature": ["50 4B 03 04"]},
104
+ {"type": "font", "extension": "ttf", "mime": "font/ttf", "offset": 0, "signature": ["00 01 00 00"]},
105
+ {"type": "font", "extension": "otf", "mime": "font/otf", "offset": 0, "signature": ["4F 54 54 4F"]},
106
+ {"type": "font", "extension": "woff", "mime": "font/woff", "offset": 0, "signature": ["77 4F 46 46"]},
107
+ {"type": "font", "extension": "woff2", "mime": "font/woff2", "offset": 0, "signature": ["77 4F 46 32"]},
108
+ {"type": "system", "extension": "cab", "mime": "application/vnd.ms-cab-compressed", "offset": 0, "signature": ["4D 53 43 46"]},
109
+ {"type": "system", "extension": "cat", "mime": "application/vnd.microsoft.portable-executable", "offset": 0, "signature": ["30 82"]},
110
+ {"type": "system", "extension": "dll", "mime": "application/vnd.microsoft.portable-executable", "offset": 0, "signature": ["4D 5A 90 00"]},
111
+ {"type": "system", "extension": "drv", "mime": "application/vnd.microsoft.portable-executable", "offset": 0, "signature": ["4D 5A 90 00"]},
112
+ {"type": "system", "extension": "sdb", "mime": "application/vnd.microsoft.portable-executable", "offset": 8, "signature": ["73 64 62 66"]},
113
+ {"type": "system", "extension": "sys", "mime": "application/vnd.microsoft.portable-executable", "offset": 0, "signature": ["4D 5A 80 00", "4D 5A 90 00"]},
114
+ {"type": "system", "extension": "reg", "mime": "application/vnd.microsoft.portable-executable", "offset": 0, "signature": ["52 45 47 45 44 49 54", "57 69 6E 64 6F 77 73 20 52 65 67 69 73 74 72 79"]},
115
+ {"type": "database", "extension": "sqlite", "mime": "application/x-sqlite3", "offset": 0, "signature": ["53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00"]}
116
+ ]
117
+
118
+ # fmt: on
119
+
120
+
121
+ class Info:
122
+ """
123
+ Generates object with given arguments
124
+
125
+ Args:
126
+ types (list) -> list of file types
127
+ extensions (list) -> list of file extensions
128
+ mimes (list) -> list of file MIME types
129
+
130
+ Returns:
131
+ (<class 'fleep.Info'>) -> Class instance
132
+ """
133
+
134
+ def __init__(self, types: list, extensions: list, mimes: list):
135
+ self.types = types
136
+ self.extensions = extensions
137
+ self.mimes = mimes
138
+
139
+ def type_matches(self, type_: str):
140
+ """Checks if file type matches with given type"""
141
+ return type_ in self.types
142
+
143
+ def extension_matches(self, extension: str):
144
+ """Checks if file extension matches with given extension"""
145
+ return extension in self.extensions
146
+
147
+ def mime_matches(self, mime: str):
148
+ """Checks if file MIME type matches with given MIME type"""
149
+ return mime in self.mimes
150
+
151
+
152
+ def get(obj: bytes):
153
+ """
154
+ Determines file format and picks suitable file types, extensions and MIME types
155
+
156
+ Args:
157
+ obj (bytes) -> byte sequence (128 bytes are enough)
158
+
159
+ Returns:
160
+ (<class 'fleep.Info'>) -> Class instance
161
+ """
162
+
163
+ if not isinstance(obj, bytes):
164
+ raise TypeError("object type must be bytes")
165
+
166
+ stream = " ".join([f"{byte:02X}" for byte in obj])
167
+
168
+ types = {}
169
+ extensions = {}
170
+ mimes = {}
171
+ for element in data:
172
+ for signature in element["signature"]:
173
+ offset = element["offset"] * 2 + element["offset"]
174
+ if signature == stream[offset : len(signature) + offset]:
175
+ types[element["type"]] = len(signature)
176
+ extensions[element["extension"]] = len(signature)
177
+ mimes[element["mime"]] = len(signature)
178
+ return Info(
179
+ sorted(types, key=lambda x: types.get(x, False), reverse=True),
180
+ sorted(extensions.keys(), key=lambda x: extensions.get(x, False), reverse=True),
181
+ sorted(mimes.keys(), key=lambda x: mimes.get(x, False), reverse=True),
182
+ )
183
+
184
+
185
+ def supported_types():
186
+ """Returns a list of supported file types"""
187
+ return sorted({x["type"] for x in data})
188
+
189
+
190
+ def supported_extensions():
191
+ """Returns a list of supported file extensions"""
192
+ return sorted({x["extension"] for x in data})
193
+
194
+
195
+ def supported_mimes():
196
+ """Returns a list of supported file MIME types"""
197
+ return sorted({x["mime"] for x in data})
@@ -7,6 +7,7 @@ from types import UnionType
7
7
  from typing import Any, ClassVar, Final, TypeVar, Union, final, get_args, get_origin, overload
8
8
  from typing_extensions import override
9
9
 
10
+ from ._vendor.fleep import get
10
11
  from .parser import Element as RawElement
11
12
  from .parser import escape, param_case, parse
12
13
  from .parser import select as select_raw
@@ -217,8 +218,14 @@ class Resource(Element):
217
218
  data |= {"src": url}
218
219
  elif path:
219
220
  data |= {"src": Path(path).as_uri()}
220
- elif raw and mime:
221
+ elif raw:
221
222
  bd = raw.getvalue() if isinstance(raw, BytesIO) else raw
223
+ if mime is None:
224
+ info = get(bd)
225
+ if info.mimes:
226
+ mime = info.mimes[0]
227
+ else:
228
+ raise ValueError("Cannot detect mime type, please specify it")
222
229
  data |= {"src": f"data:{mime};base64,{b64encode(bd).decode('utf-8')}"}
223
230
  else:
224
231
  raise ValueError(f"{cls} need at least one of url, path and raw")
@@ -306,7 +306,7 @@ def parse_tokens(tokens: list[str | Token], context: dict | None = None) -> list
306
306
  while mat := attr_pat.search(token.extra):
307
307
  key = mat.group(1)
308
308
  groupdict = mat.groupdict()
309
- v2 = groupdict.get("value2", groupdict.get("value1"))
309
+ v2 = groupdict.get("value2") or groupdict.get("value1")
310
310
  curly = groupdict.get("curly")
311
311
  if curly and context is not None:
312
312
  attrs[key] = interpolate(curly, context)