Pyjoyment 0.1.0__py3-none-any.whl

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.
Files changed (200) hide show
  1. Pyjo/App/ASGI.py +735 -0
  2. Pyjo/App/CGI.py +773 -0
  3. Pyjo/App/FastCGI.py +631 -0
  4. Pyjo/App/Lambda.py +987 -0
  5. Pyjo/App/WSGI.py +427 -0
  6. Pyjo/App/__init__.py +409 -0
  7. Pyjo/Asset/File.py +377 -0
  8. Pyjo/Asset/Memory.py +235 -0
  9. Pyjo/Asset/__init__.py +200 -0
  10. Pyjo/Base.py +215 -0
  11. Pyjo/Cache.py +119 -0
  12. Pyjo/Collection.py +570 -0
  13. Pyjo/Content/MultiPart.py +325 -0
  14. Pyjo/Content/Single.py +193 -0
  15. Pyjo/Content/__init__.py +806 -0
  16. Pyjo/Cookie/Request.py +90 -0
  17. Pyjo/Cookie/Response.py +294 -0
  18. Pyjo/Cookie/__init__.py +143 -0
  19. Pyjo/DOM/CSS.py +1019 -0
  20. Pyjo/DOM/HTML.py +550 -0
  21. Pyjo/DOM/__init__.py +1329 -0
  22. Pyjo/Date.py +294 -0
  23. Pyjo/EventEmitter.py +522 -0
  24. Pyjo/Exception.py +494 -0
  25. Pyjo/File.py +959 -0
  26. Pyjo/GetOpt.py +605 -0
  27. Pyjo/Headers.py +760 -0
  28. Pyjo/HelloWorld.py +77 -0
  29. Pyjo/Home.py +109 -0
  30. Pyjo/IOLoop/ChildProcess.py +591 -0
  31. Pyjo/IOLoop/Client.py +736 -0
  32. Pyjo/IOLoop/Datagram.py +982 -0
  33. Pyjo/IOLoop/Messages.py +664 -0
  34. Pyjo/IOLoop/Server.py +458 -0
  35. Pyjo/IOLoop/Stdio.py +446 -0
  36. Pyjo/IOLoop/Stream.py +644 -0
  37. Pyjo/IOLoop/Subprocess.py +419 -0
  38. Pyjo/IOLoop/TLS.py +360 -0
  39. Pyjo/IOLoop/__init__.py +1036 -0
  40. Pyjo/IOLoop/certs/README.md +41 -0
  41. Pyjo/IOLoop/certs/ca.crt +20 -0
  42. Pyjo/IOLoop/certs/server.crt +21 -0
  43. Pyjo/IOLoop/certs/server.key +28 -0
  44. Pyjo/JSON/Pointer.py +135 -0
  45. Pyjo/JSON/RPC.py +772 -0
  46. Pyjo/JSON/Schema.py +2506 -0
  47. Pyjo/JSON/__init__.py +573 -0
  48. Pyjo/Loader.py +342 -0
  49. Pyjo/Log.py +723 -0
  50. Pyjo/Message/Request.py +725 -0
  51. Pyjo/Message/Response.py +476 -0
  52. Pyjo/Message/__init__.py +830 -0
  53. Pyjo/Parameters.py +472 -0
  54. Pyjo/Path.py +419 -0
  55. Pyjo/Promise.py +894 -0
  56. Pyjo/Prove.py +1305 -0
  57. Pyjo/Reactor/Asyncio.py +854 -0
  58. Pyjo/Reactor/Poll.py +439 -0
  59. Pyjo/Reactor/__init__.py +525 -0
  60. Pyjo/SSE.py +119 -0
  61. Pyjo/Server/ASGI.py +486 -0
  62. Pyjo/Server/CGI.py +259 -0
  63. Pyjo/Server/Daemon.py +1007 -0
  64. Pyjo/Server/FastCGI.py +517 -0
  65. Pyjo/Server/Hot/__init__.py +687 -0
  66. Pyjo/Server/Hot/__main__.py +5 -0
  67. Pyjo/Server/Lambda.py +641 -0
  68. Pyjo/Server/Prefork.py +917 -0
  69. Pyjo/Server/Prethread.py +394 -0
  70. Pyjo/Server/Reload/Backend/Poll.py +114 -0
  71. Pyjo/Server/Reload/Backend/__init__.py +126 -0
  72. Pyjo/Server/Reload/__init__.py +482 -0
  73. Pyjo/Server/Reload/__main__.py +5 -0
  74. Pyjo/Server/Stdio.py +228 -0
  75. Pyjo/Server/WSGI.py +349 -0
  76. Pyjo/Server/__init__.py +396 -0
  77. Pyjo/String/Bytes.py +532 -0
  78. Pyjo/String/HTML.py +210 -0
  79. Pyjo/String/Mixin.py +333 -0
  80. Pyjo/String/Template.py +470 -0
  81. Pyjo/String/Unicode.py +553 -0
  82. Pyjo/String/__init__.py +0 -0
  83. Pyjo/TOML.py +232 -0
  84. Pyjo/Template.py +1290 -0
  85. Pyjo/Test/Pyjo.py +1454 -0
  86. Pyjo/Test/__init__.py +1233 -0
  87. Pyjo/Test/pytest_plugin.py +305 -0
  88. Pyjo/Transaction/HTTP.py +374 -0
  89. Pyjo/Transaction/WebSocket.py +709 -0
  90. Pyjo/Transaction/__init__.py +429 -0
  91. Pyjo/URL/Template.py +459 -0
  92. Pyjo/URL/__init__.py +651 -0
  93. Pyjo/Upload.py +139 -0
  94. Pyjo/UserAgent/CookieJar.py +391 -0
  95. Pyjo/UserAgent/Proxy.py +138 -0
  96. Pyjo/UserAgent/Server.py +239 -0
  97. Pyjo/UserAgent/Transactor.py +766 -0
  98. Pyjo/UserAgent/__init__.py +1243 -0
  99. Pyjo/Util.py +3567 -0
  100. Pyjo/WebSocket.py +332 -0
  101. Pyjo/YAML.py +1167 -0
  102. Pyjo/__init__.py +441 -0
  103. Pyjo/__main__.py +35 -0
  104. Pyjo/py.typed +0 -0
  105. Pyjoyment/Command/Author/__init__.py +20 -0
  106. Pyjoyment/Command/Author/generate/__init__.py +106 -0
  107. Pyjoyment/Command/Author/generate/app.py +299 -0
  108. Pyjoyment/Command/Author/generate/dockerfile.py +281 -0
  109. Pyjoyment/Command/Author/generate/kubernetes.py +514 -0
  110. Pyjoyment/Command/Author/generate/lambda_zip.py +372 -0
  111. Pyjoyment/Command/Author/generate/lite_app.py +113 -0
  112. Pyjoyment/Command/Author/generate/plugin.py +267 -0
  113. Pyjoyment/Command/Author/generate/pyproject.py +123 -0
  114. Pyjoyment/Command/Author/inflate.py +101 -0
  115. Pyjoyment/Command/__init__.py +332 -0
  116. Pyjoyment/Command/asgi.py +92 -0
  117. Pyjoyment/Command/cgi.py +90 -0
  118. Pyjoyment/Command/daemon.py +160 -0
  119. Pyjoyment/Command/eval.py +128 -0
  120. Pyjoyment/Command/fastcgi.py +99 -0
  121. Pyjoyment/Command/get.py +305 -0
  122. Pyjoyment/Command/lambda.py +117 -0
  123. Pyjoyment/Command/prefork.py +181 -0
  124. Pyjoyment/Command/prethread.py +126 -0
  125. Pyjoyment/Command/routes.py +133 -0
  126. Pyjoyment/Command/stdio.py +92 -0
  127. Pyjoyment/Command/version.py +141 -0
  128. Pyjoyment/Command/wsgi.py +93 -0
  129. Pyjoyment/Commands.py +546 -0
  130. Pyjoyment/Controller.py +1307 -0
  131. Pyjoyment/HelloWorld.py +56 -0
  132. Pyjoyment/Lite.py +494 -0
  133. Pyjoyment/Plugin/AllowedHosts.py +205 -0
  134. Pyjoyment/Plugin/Config.py +248 -0
  135. Pyjoyment/Plugin/DefaultHelpers.py +1202 -0
  136. Pyjoyment/Plugin/EPLRenderer.py +134 -0
  137. Pyjoyment/Plugin/EPRenderer.py +212 -0
  138. Pyjoyment/Plugin/HeaderCondition.py +98 -0
  139. Pyjoyment/Plugin/Health.py +325 -0
  140. Pyjoyment/Plugin/JSONConfig.py +172 -0
  141. Pyjoyment/Plugin/Mount.py +223 -0
  142. Pyjoyment/Plugin/PyRenderer.py +209 -0
  143. Pyjoyment/Plugin/TOMLConfig.py +141 -0
  144. Pyjoyment/Plugin/TagHelpers.py +959 -0
  145. Pyjoyment/Plugin/YAMLConfig.py +178 -0
  146. Pyjoyment/Plugin/__init__.py +80 -0
  147. Pyjoyment/Plugins.py +215 -0
  148. Pyjoyment/Renderer.py +673 -0
  149. Pyjoyment/Routes/Match.py +251 -0
  150. Pyjoyment/Routes/Pattern.py +443 -0
  151. Pyjoyment/Routes/Route.py +827 -0
  152. Pyjoyment/Routes/__init__.py +419 -0
  153. Pyjoyment/Sessions.py +255 -0
  154. Pyjoyment/Static.py +421 -0
  155. Pyjoyment/Types.py +238 -0
  156. Pyjoyment/Validator/Validation.py +395 -0
  157. Pyjoyment/Validator/__init__.py +304 -0
  158. Pyjoyment/__init__.py +1083 -0
  159. Pyjoyment/__main__.py +5 -0
  160. Pyjoyment/py.typed +0 -0
  161. Pyjoyment/resources/public/favicon.ico +0 -0
  162. Pyjoyment/resources/public/favicon.svg +345 -0
  163. Pyjoyment/resources/public/pyjo/bootstrap/LICENSE +21 -0
  164. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.bundle.min.js.map +1 -0
  165. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.css +6 -0
  166. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.js +7 -0
  167. Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.min.css.map +1 -0
  168. Pyjoyment/resources/public/pyjo/failunicat.png +0 -0
  169. Pyjoyment/resources/public/pyjo/failunicat.svg +50 -0
  170. Pyjoyment/resources/public/pyjo/highlight.js/LICENSE +29 -0
  171. Pyjoyment/resources/public/pyjo/highlight.js/highlight-mojo-dark.css +56 -0
  172. Pyjoyment/resources/public/pyjo/highlight.js/highlight.min.js +1262 -0
  173. Pyjoyment/resources/public/pyjo/highlight.js/pyjoyment.min.js +1 -0
  174. Pyjoyment/resources/public/pyjo/logo-black-4x.png +0 -0
  175. Pyjoyment/resources/public/pyjo/logo-black-4x.svg +346 -0
  176. Pyjoyment/resources/public/pyjo/logo-white-2x.png +0 -0
  177. Pyjoyment/resources/public/pyjo/logo-white-2x.svg +346 -0
  178. Pyjoyment/resources/public/pyjo/logo-white-4x.png +0 -0
  179. Pyjoyment/resources/public/pyjo/logo-white-4x.svg +346 -0
  180. Pyjoyment/resources/public/pyjo/logo-white.png +0 -0
  181. Pyjoyment/resources/public/pyjo/logo-white.svg +346 -0
  182. Pyjoyment/resources/public/pyjo/logo.png +0 -0
  183. Pyjoyment/resources/public/pyjo/logo.svg +346 -0
  184. Pyjoyment/resources/public/pyjo/notfound.png +0 -0
  185. Pyjoyment/resources/public/pyjo/notfound.svg +3 -0
  186. Pyjoyment/resources/public/pyjo/nounicat.png +0 -0
  187. Pyjoyment/resources/public/pyjo/nounicat.svg +5 -0
  188. Pyjoyment/resources/public/pyjo/pinstripe-dark.png +0 -0
  189. Pyjoyment/resources/public/pyjo/pinstripe-dark.svg +4 -0
  190. Pyjoyment/resources/public/pyjo/pinstripe-light.png +0 -0
  191. Pyjoyment/resources/public/pyjo/pinstripe-light.svg +4 -0
  192. Pyjoyment/resources/public/pyjo/pyjo.css +204 -0
  193. Pyjoyment/resources/templates/pyjo/debug.html.ep +346 -0
  194. Pyjoyment/resources/templates/pyjo/exception.html.ep +32 -0
  195. Pyjoyment/resources/templates/pyjo/not_found.html.ep +45 -0
  196. pyjoyment-0.1.0.dist-info/METADATA +376 -0
  197. pyjoyment-0.1.0.dist-info/RECORD +200 -0
  198. pyjoyment-0.1.0.dist-info/WHEEL +4 -0
  199. pyjoyment-0.1.0.dist-info/entry_points.txt +8 -0
  200. pyjoyment-0.1.0.dist-info/licenses/LICENSE +201 -0
Pyjo/Util.py ADDED
@@ -0,0 +1,3567 @@
1
+ """
2
+ Pyjo.Util - Portable utility functions
3
+ ======================================
4
+ ::
5
+
6
+ from Pyjo.Util import b64_encode, url_escape, url_unescape
7
+
8
+ string = 'test=23'
9
+ escaped = url_escape(string)
10
+ print(url_unescape(escaped))
11
+ print(b64_encode(escaped.encode(), ''))
12
+
13
+ :mod:`Pyjo.Util` provides portable utility functions for :mod:`Pyjo`.
14
+
15
+ Next to the functions of Mojolicious, it has functions of its own that turn
16
+ statements into expressions, so that they can be used in a lambda, a callback
17
+ or a chain: :func:`die`, :func:`getenv`, :func:`isalive`, :func:`notnone`,
18
+ :func:`setenv`, :func:`slurp`, :func:`spurt` and :func:`warn`. ::
19
+
20
+ home = getenv('HOME') or die('HOME is not defined')
21
+
22
+ See also
23
+ --------
24
+
25
+ :mod:`Pyjoyment`, https://mojolicious.org.
26
+
27
+ Ported from `Mojo::Util <https://github.com/mojolicious/mojo/blob/v9.52/lib/Mojo/Util.pm>`_
28
+ and `Mojo::BaseUtil <https://github.com/mojolicious/mojo/blob/v9.52/lib/Mojo/BaseUtil.pm>`_
29
+ of Mojolicious 9.52.
30
+
31
+ Functions
32
+ ---------
33
+ """
34
+
35
+ import ast
36
+ import base64
37
+ import codecs
38
+ import functools
39
+ import hashlib
40
+ import hmac
41
+ import os
42
+ import pprint
43
+ import re
44
+ import secrets
45
+ import socket
46
+ import sys
47
+ import time
48
+ import unicodedata
49
+ import warnings
50
+ import zlib
51
+
52
+ from collections.abc import Callable, Iterable, Sequence
53
+ from gzip import compress as _gzip_compress
54
+ from gzip import decompress as _gzip_decompress
55
+ from types import FunctionType, ModuleType, TracebackType
56
+ from typing import TYPE_CHECKING, Any, NoReturn, Protocol, cast, overload
57
+
58
+
59
+ if TYPE_CHECKING:
60
+ import Pyjo.GetOpt
61
+
62
+
63
+ # "Sun, 06 Nov 1994 08:49:37 GMT" and "Sunday, 06-Nov-94 08:49:37 GMT"
64
+ _RE_EXPIRES = re.compile(r'=\s*(\w+\W+\d+\W+\w+\W+\d+\W+\d+:\d+:\d+\W*\w+)')
65
+
66
+ # Header key/value pairs
67
+ _RE_HEADER_NAME = re.compile(r'[,;\s]*([^=;, ]+)\s*')
68
+ _RE_HEADER_SEPARATOR = re.compile(r'[;\s]*,\s*')
69
+ _RE_PARAM_NAME = re.compile(r'[;\s]*([^=;, ]+)\s*')
70
+ _RE_QUOTED_VALUE = re.compile(r'=\s*("(?:\\\\|\\"|[^"])*")')
71
+ _RE_UNQUOTED_VALUE = re.compile(r'=\s*([^;, ]*)')
72
+
73
+ # HTML entities
74
+ _RE_ENTITY = re.compile(r'&(?:\#((?:[0-9]{1,7}|x[0-9a-fA-F]{1,6}));|(\w+[;=]?))')
75
+
76
+
77
+ def b64_decode(b64: str | bytes) -> bytes:
78
+ """::
79
+
80
+ data = b64_decode(b64)
81
+
82
+ Base64 decode bytes, ignoring the characters that are not Base64 and the
83
+ ones after the padding, as ``MIME::Base64`` of Perl does.
84
+ """
85
+ if isinstance(b64, str):
86
+ b64 = b64.encode('latin-1', 'ignore')
87
+ b64 = re.sub(rb'[^A-Za-z0-9+/=]', b'', b64).split(b'=', 1)[0]
88
+ if len(b64) % 4 == 1:
89
+ b64 = b64[:-1]
90
+ return base64.b64decode(b64 + b'=' * (-len(b64) % 4))
91
+
92
+
93
+ def b64_encode(data: bytes, eol: str = '\n') -> str:
94
+ r"""::
95
+
96
+ b64 = b64_encode(data)
97
+ b64 = b64_encode(data, "\n")
98
+
99
+ Base64 encode bytes, the line ending defaults to a newline.
100
+ """
101
+ b64 = base64.b64encode(data).decode('ascii')
102
+ return ''.join(b64[i : i + 76] + eol for i in range(0, len(b64), 76))
103
+
104
+
105
+ def camelize(string: str) -> str:
106
+ """::
107
+
108
+ camelcase = camelize(snakecase)
109
+
110
+ Convert ``snake_case`` string to ``CamelCase`` and replace ``-`` with ``::``. ::
111
+
112
+ # "FooBar"
113
+ camelize('foo_bar')
114
+
115
+ # "FooBar::Baz"
116
+ camelize('foo_bar-baz')
117
+
118
+ # "FooBar::Baz"
119
+ camelize('FooBar::Baz')
120
+ """
121
+ if re.match(r'[A-Z]', string):
122
+ return string
123
+
124
+ # CamelCase words
125
+ return '::'.join(''.join(_ucfirst(w.lower()) for w in _split('_', part)) for part in _split('-', string))
126
+
127
+
128
+ def class_to_file(class_: str) -> str:
129
+ """::
130
+
131
+ file = class_to_file('Foo.Bar')
132
+
133
+ Convert a class name to a file. ::
134
+
135
+ # "foo_bar"
136
+ class_to_file('Foo.Bar')
137
+
138
+ # "foobar"
139
+ class_to_file('FOO.Bar')
140
+
141
+ # "foo_bar"
142
+ class_to_file('FooBar')
143
+
144
+ # "foobar"
145
+ class_to_file('FOOBar')
146
+ """
147
+ # Left out from Mojolicious: the deprecated package separator "'", which Python has never had
148
+ class_ = class_.replace('.', '')
149
+ class_ = re.sub(r'([A-Z])([A-Z]*)', lambda m: m[1] + m[2].lower(), class_)
150
+ return decamelize(class_)
151
+
152
+
153
+ def class_to_path(class_: str) -> str:
154
+ """::
155
+
156
+ path = class_to_path('Foo.Bar')
157
+
158
+ Convert class name to path, as a module is found in a directory of
159
+ :data:`sys.path`. ::
160
+
161
+ # "Foo/Bar.py"
162
+ class_to_path('Foo.Bar')
163
+
164
+ # "FooBar.py"
165
+ class_to_path('FooBar')
166
+ """
167
+ # Left out from Mojolicious: the deprecated package separator "'", which Python has never had
168
+ return '/'.join(class_.split('.')) + '.py'
169
+
170
+
171
+ def decamelize(string: str) -> str:
172
+ """::
173
+
174
+ snakecase = decamelize(camelcase)
175
+
176
+ Convert ``CamelCase`` string to ``snake_case`` and replace ``::`` with ``-``. ::
177
+
178
+ # "foo_bar"
179
+ decamelize('FooBar')
180
+
181
+ # "foo_bar-baz"
182
+ decamelize('FooBar::Baz')
183
+
184
+ # "foo_bar-baz"
185
+ decamelize('foo_bar-baz')
186
+ """
187
+ if not re.match(r'[A-Z]', string):
188
+ return string
189
+
190
+ # snake_case words
191
+ return '-'.join(
192
+ '_'.join(w.lower() for w in re.split(r'([A-Z]{1}[^A-Z]*)', part) if len(w)) for part in _split('::', string)
193
+ )
194
+
195
+
196
+ def decode(charset: str, data: str | bytes) -> str | None:
197
+ """::
198
+
199
+ chars = decode('UTF-8', data)
200
+
201
+ Decode bytes to characters, or return ``None`` if decoding failed. A string
202
+ of the characters 0-255 is decoded as the bytes it holds, like a Perl byte
203
+ string; one with other characters does not decode.
204
+ """
205
+ if isinstance(data, str):
206
+ try:
207
+ data = data.encode('latin-1')
208
+ except UnicodeEncodeError:
209
+ return None
210
+ try:
211
+ return codecs.lookup(charset).decode(data)[0]
212
+ except (LookupError, UnicodeDecodeError):
213
+ return None
214
+
215
+
216
+ # Left out from Mojolicious: decrypt_cookie, as ChaCha20-Poly1305 of the sessions is not in the standard library
217
+
218
+
219
+ def deprecated(*args: object) -> None:
220
+ """::
221
+
222
+ deprecated('foo is DEPRECATED in favor of bar')
223
+
224
+ Warn about deprecated feature from perspective of caller, with a
225
+ :exc:`DeprecationWarning`. You can also set the ``PYJO_FATAL_DEPRECATIONS``
226
+ environment variable to make them raise it instead.
227
+ """
228
+ msg = ''.join(map(str, args))
229
+ if os.environ.get('PYJO_FATAL_DEPRECATIONS'):
230
+ raise DeprecationWarning(msg)
231
+ # Unlike Mojolicious: the filters of warnings decide whether the warning is shown, as Python shows a
232
+ # DeprecationWarning by default only where __main__ triggers it, where Mojo always warns
233
+ warnings.warn(msg, DeprecationWarning, stacklevel=3)
234
+
235
+
236
+ def die(e: BaseException | str) -> NoReturn:
237
+ """::
238
+
239
+ die(Exception('Something went wrong'))
240
+ die('Exit immediately')
241
+
242
+ Raise an exception. :class:`SystemExit` exception is raised
243
+ if parameter is not an exception already. ::
244
+
245
+ os.environ.get('HOME') or die('HOME is not defined')
246
+ """
247
+ if isinstance(e, BaseException):
248
+ raise e
249
+ else:
250
+ raise SystemExit(e)
251
+
252
+
253
+ def dumper(*values: object) -> str:
254
+ """::
255
+
256
+ python = dumper({'some': 'data'})
257
+
258
+ Dump a Python data structure with :mod:`pprint`.
259
+ """
260
+ return ''.join(pprint.pformat(value) + '\n' for value in values)
261
+
262
+
263
+ def encode(charset: str, chars: str) -> bytes:
264
+ """::
265
+
266
+ data = encode('UTF-8', chars)
267
+
268
+ Encode characters to bytes. A character that the encoding cannot
269
+ represent becomes a replacement character.
270
+ """
271
+ try:
272
+ codecs.lookup(charset)
273
+ except LookupError:
274
+ raise LookupError(f"Unknown encoding '{charset}'") from None
275
+ return chars.encode(charset, 'replace')
276
+
277
+
278
+ # Left out from Mojolicious: encrypt_cookie, as ChaCha20-Poly1305 of the sessions is not in the standard library
279
+
280
+
281
+ def extract_usage(file: 'str | os.PathLike[str] | None' = None, section: str | None = None) -> str:
282
+ """::
283
+
284
+ usage = extract_usage()
285
+ usage = extract_usage('/home/sri/foo.py')
286
+ usage = extract_usage(section='myscript')
287
+
288
+ Extract usage message from the synopsis of the docstring of a module, its
289
+ first literal block, defaults to using the file this function was called
290
+ from. ::
291
+
292
+ # "Usage: APPLICATION test [OPTIONS]\\n"
293
+ extract_usage()
294
+
295
+ \"\"\"
296
+ Test command
297
+ ============
298
+ ::
299
+
300
+ Usage: APPLICATION test [OPTIONS]
301
+ \"\"\"
302
+
303
+ With a section, the first literal block after its heading, as the usage of
304
+ a script that is a function of the module. ::
305
+
306
+ # "Usage: myscript [OPTIONS]\\n"
307
+ extract_usage(section='myscript')
308
+
309
+ \"\"\"
310
+ Test module
311
+ ===========
312
+ ::
313
+
314
+ test = Test.new()
315
+
316
+ myscript
317
+ --------
318
+ ::
319
+
320
+ Usage: myscript [OPTIONS]
321
+ \"\"\"
322
+ """
323
+ # Unlike Mojolicious: the synopsis of the docstring, which the file is parsed for without running it, where Mojo
324
+ # extracts the section SYNOPSIS of POD
325
+ path = os.fspath(file) if file is not None else sys._getframe(1).f_code.co_filename # pyright: ignore[reportPrivateUsage]
326
+ with open(path, encoding='utf-8') as handle:
327
+ docstring = ast.get_docstring(ast.parse(handle.read()), clean=False) or ''
328
+
329
+ lines = docstring.split('\n')
330
+
331
+ # Not in Mojolicious: the section of a heading, as the usage of a script that is a function of the module
332
+ if section is not None:
333
+ start = next(
334
+ (
335
+ i + 2
336
+ for i, line in enumerate(lines[:-1])
337
+ if line.strip() == section and re.fullmatch(r'\s*([-=~^"\'])\1+\s*', lines[i + 1])
338
+ ),
339
+ None,
340
+ )
341
+ if start is None:
342
+ return ''
343
+ lines = lines[start:]
344
+
345
+ block: list[str] = []
346
+ for i, line in enumerate(lines):
347
+ if line.rstrip().endswith('::'):
348
+ indent = len(line) - len(line.lstrip())
349
+ for line in lines[i + 1 :]:
350
+ if line.strip() and len(line) - len(line.lstrip()) <= indent:
351
+ break
352
+ block.append(line)
353
+ break
354
+ usage = unindent('\n'.join(block).strip('\n'))
355
+ return f'{usage}\n' if usage else ''
356
+
357
+
358
+ def generate_secret() -> str:
359
+ """::
360
+
361
+ secret = generate_secret()
362
+
363
+ Generate a random secret with a cryptographically secure random number
364
+ generator.
365
+ """
366
+ return secrets.token_urlsafe(128)
367
+
368
+
369
+ @overload
370
+ def getenv(name: str) -> str | None: ...
371
+
372
+
373
+ @overload
374
+ def getenv(name: str, default: str) -> str: ...
375
+
376
+
377
+ def getenv(name: str, default: str | None = None) -> str | None:
378
+ """::
379
+
380
+ envvar = getenv(name)
381
+ envvar = getenv(name, default)
382
+
383
+ Get the environment variable. Returns default value or ``None``
384
+ if variable is not defined.
385
+ """
386
+ return os.environ.get(name, default)
387
+
388
+
389
+ @overload
390
+ def getopt(
391
+ specs: 'dict[str, Pyjo.GetOpt.Target]',
392
+ /,
393
+ *,
394
+ pass_through: bool = False,
395
+ require_order: bool = False,
396
+ env_prefix: str | None = None,
397
+ ) -> 'Pyjo.GetOpt.Pyjo_GetOpt_Result': ...
398
+
399
+
400
+ @overload
401
+ def getopt(
402
+ args: list[str],
403
+ specs: 'dict[str, Pyjo.GetOpt.Target]',
404
+ /,
405
+ *,
406
+ pass_through: bool = False,
407
+ require_order: bool = False,
408
+ env_prefix: str | None = None,
409
+ ) -> 'Pyjo.GetOpt.Pyjo_GetOpt_Result': ...
410
+
411
+
412
+ def getopt(
413
+ args: 'list[str] | dict[str, Pyjo.GetOpt.Target]',
414
+ specs: 'dict[str, Pyjo.GetOpt.Target] | None' = None,
415
+ /,
416
+ *,
417
+ pass_through: bool = False,
418
+ require_order: bool = False,
419
+ env_prefix: str | None = None,
420
+ ) -> 'Pyjo.GetOpt.Pyjo_GetOpt_Result':
421
+ """::
422
+
423
+ opts = getopt({'H|headers=s': headers, 't|timeout=i': None, 'v|verbose': None})
424
+ opts = getopt(args, {'H|headers=s': headers, 't|timeout=i': None, 'v|verbose': None})
425
+ opts = getopt(args, {'H|headers=s': headers, 't|timeout=i': None}, pass_through=True)
426
+ opts = getopt(args, {'v|verbose': None}, require_order=True)
427
+ opts = getopt(args, {'t|timeout=i': None}, env_prefix='PYJO_')
428
+
429
+ Extract options from a list with :mod:`Pyjo.GetOpt`, defaults to using
430
+ the arguments of ``sys.argv``. The target of an option is a list for its
431
+ values, a callback, which gets its name and its value, or ``None`` for
432
+ its value in the result, which is false after an error. With
433
+ ``require_order``, the options end at the first argument that is not one.
434
+ With ``env_prefix``, an option that is not given has the value of the
435
+ environment variable of its name, as ``PYJO_TIMEOUT`` for ``--timeout``,
436
+ see :mod:`Pyjo.GetOpt`. ::
437
+
438
+ # Extract "charset" option
439
+ opts = getopt(['--charset', 'UTF-8'], {'charset=s': None})
440
+ print(opts.charset)
441
+ """
442
+ # Unlike Mojolicious: the options in a dict, and the values without a target in the result, as Python has no
443
+ # references to variables, which Mojo passes as the targets
444
+ import Pyjo.GetOpt
445
+
446
+ argv = None if isinstance(args, dict) else args
447
+ parser = Pyjo.GetOpt.new(pass_through=pass_through, require_order=require_order, env_prefix=env_prefix)
448
+ for spec, target in (args if isinstance(args, dict) else specs or {}).items():
449
+ parser.option(spec, target)
450
+ return parser.parse(argv)
451
+
452
+
453
+ def gunzip(compressed: bytes) -> bytes:
454
+ """::
455
+
456
+ uncompressed = gunzip(compressed)
457
+
458
+ Uncompress bytes with :mod:`gzip`.
459
+ """
460
+ try:
461
+ return _gzip_decompress(compressed)
462
+ except (EOFError, OSError, zlib.error) as e:
463
+ raise ValueError(f"Couldn't gunzip: {e}") from e
464
+
465
+
466
+ def gzip(uncompressed: bytes) -> bytes:
467
+ """::
468
+
469
+ compressed = gzip(uncompressed)
470
+
471
+ Compress bytes with :mod:`gzip`.
472
+ """
473
+ return _gzip_compress(uncompressed)
474
+
475
+
476
+ def header_params(value: str) -> tuple[dict[str, str], str]:
477
+ """::
478
+
479
+ params, remainder = header_params('one=foo; two="bar", three=baz')
480
+
481
+ Extract HTTP header field parameters until the first comma according to
482
+ :rfc:`5987`.
483
+ """
484
+ params: dict[str, str] = {}
485
+ pos = 0
486
+ while m := _RE_PARAM_NAME.match(value, pos):
487
+ name, pos = m[1], m.end()
488
+
489
+ # Quoted value
490
+ if quoted := _RE_QUOTED_VALUE.match(value, pos):
491
+ params.setdefault(name, unquote(quoted[1]))
492
+ pos = quoted.end()
493
+
494
+ # Unquoted value
495
+ elif unquoted := _RE_UNQUOTED_VALUE.match(value, pos):
496
+ params.setdefault(name, unquoted[1])
497
+ pos = unquoted.end()
498
+
499
+ return params, value[pos:]
500
+
501
+
502
+ def hmac_sha1_sum(data: bytes, secret: bytes) -> str:
503
+ """::
504
+
505
+ checksum = hmac_sha1_sum(data, b'passw0rd')
506
+
507
+ Generate HMAC-SHA1 checksum for bytes with :mod:`hmac`. ::
508
+
509
+ # "11cedfd5ec11adc0ec234466d8a0f2a83736aa68"
510
+ hmac_sha1_sum(b'foo', b'passw0rd')
511
+ """
512
+ return hmac.new(secret, data, hashlib.sha1).hexdigest()
513
+
514
+
515
+ def html_attr_unescape(string: str) -> str:
516
+ """::
517
+
518
+ string = html_attr_unescape(escaped)
519
+
520
+ Same as :func:`html_unescape`, but handles special rules from the
521
+ `HTML Living Standard <https://html.spec.whatwg.org>`_ for HTML
522
+ attributes. ::
523
+
524
+ # "foo=bar&ltest=baz"
525
+ html_attr_unescape('foo=bar&ltest=baz')
526
+
527
+ # "foo=bar<est=baz"
528
+ html_attr_unescape('foo=bar&lt;est=baz')
529
+ """
530
+ return _html(string, True)
531
+
532
+
533
+ def html_unescape(string: str) -> str:
534
+ """::
535
+
536
+ string = html_unescape(escaped)
537
+
538
+ Unescape all HTML entities in string. ::
539
+
540
+ # "<div>"
541
+ html_unescape('&lt;div&gt;')
542
+ """
543
+ return _html(string, False)
544
+
545
+
546
+ def humanize_bytes(size: int | float) -> str:
547
+ """::
548
+
549
+ string = humanize_bytes(1234)
550
+
551
+ Turn number of bytes into a simplified human readable format. ::
552
+
553
+ # "1B"
554
+ humanize_bytes(1)
555
+
556
+ # "7.5GiB"
557
+ humanize_bytes(8007188480)
558
+
559
+ # "13GiB"
560
+ humanize_bytes(13443399680)
561
+
562
+ # "-685MiB"
563
+ humanize_bytes(-717946880)
564
+ """
565
+ prefix = '-' if size < 0 else ''
566
+
567
+ if (size := abs(size)) < 1024:
568
+ return f'{prefix}{size}B'
569
+ if (size := size / 1024) < 1024:
570
+ return f'{prefix}{_round(size)}KiB'
571
+ if (size := size / 1024) < 1024:
572
+ return f'{prefix}{_round(size)}MiB'
573
+ if (size := size / 1024) < 1024:
574
+ return f'{prefix}{_round(size)}GiB'
575
+ return f'{prefix}{_round(size / 1024)}TiB'
576
+
577
+
578
+ def isalive(obj: object) -> bool:
579
+ """::
580
+
581
+ boolean = isalive(weakref.proxy(obj))
582
+
583
+ Check if object behind weak reference proxy still exists.
584
+ """
585
+ try:
586
+ obj.__class__
587
+ return True
588
+ except ReferenceError:
589
+ return False
590
+
591
+
592
+ def md5_bytes(data: bytes) -> bytes:
593
+ """::
594
+
595
+ checksum = md5_bytes(data)
596
+
597
+ Generate binary MD5 checksum for bytes with :mod:`hashlib`.
598
+ """
599
+ return hashlib.md5(data).digest()
600
+
601
+
602
+ def md5_sum(data: bytes) -> str:
603
+ """::
604
+
605
+ checksum = md5_sum(data)
606
+
607
+ Generate MD5 checksum for bytes with :mod:`hashlib`. ::
608
+
609
+ # "acbd18db4cc2f85cedef654fccc4a4d8"
610
+ md5_sum(b'foo')
611
+ """
612
+ return hashlib.md5(data).hexdigest()
613
+
614
+
615
+ def monkey_patch(target: type | ModuleType, **patch: Callable[..., Any]) -> None:
616
+ """::
617
+
618
+ monkey_patch(cls, foo=lambda: ...)
619
+ monkey_patch(module, foo=lambda: ...)
620
+ monkey_patch(cls, foo=lambda: ..., bar=lambda: ...)
621
+
622
+ Monkey patch functions into a class or a module, with names of their
623
+ own. ::
624
+
625
+ monkey_patch(MyApp,
626
+ one=lambda: print('One!'),
627
+ two=lambda: print('Two!'),
628
+ three=lambda: print('Three!'))
629
+ """
630
+ # Unlike Mojolicious: a class or a module, not the name of a package, which Mojo creates when it does not exist
631
+ if isinstance(target, type):
632
+ module, prefix = target.__module__, target.__qualname__ + '.'
633
+ else:
634
+ module, prefix = target.__name__, ''
635
+ for name, cb in patch.items():
636
+ if isinstance(cb, FunctionType):
637
+ cb.__module__, cb.__name__, cb.__qualname__ = module, name, prefix + name
638
+ setattr(target, name, cb)
639
+
640
+
641
+ def network_contains(cidr: str, addr: str) -> bool:
642
+ """::
643
+
644
+ boolean = network_contains(network, address)
645
+
646
+ Check that a given address is contained within a network in CIDR form. If
647
+ the network is a single address, the addresses must be equivalent. ::
648
+
649
+ # True
650
+ network_contains('10.0.0.0/8', '10.10.10.10')
651
+ network_contains('10.10.10.10', '10.10.10.10')
652
+ network_contains('fc00::/7', 'fc00::c0:ff:ee')
653
+
654
+ # False
655
+ network_contains('10.0.0.0/29', '10.10.10.10')
656
+ network_contains('10.10.10.12', '10.10.10.10')
657
+ network_contains('fc00::/7', '::1')
658
+ """
659
+ if not len(cidr) or not len(addr):
660
+ return False
661
+
662
+ # Parse inputs
663
+ net, slash, mask = cidr.partition('/')
664
+ v6 = ':' in net
665
+ if v6 != (':' in addr):
666
+ return False
667
+
668
+ # Convert addresses to binary
669
+ family = socket.AF_INET6 if v6 else socket.AF_INET
670
+ try:
671
+ net_bytes, addr_bytes = socket.inet_pton(family, net), socket.inet_pton(family, addr)
672
+ except OSError:
673
+ return False
674
+ length = 128 if v6 else 32
675
+
676
+ # Apply mask if given
677
+ addr_int = int.from_bytes(addr_bytes)
678
+ if slash:
679
+ bits = int(mask) if mask.isdigit() else 0
680
+ addr_int &= int(('1' * bits).ljust(length, '0')[:length], 2)
681
+
682
+ # Compare
683
+ return int.from_bytes(net_bytes) ^ addr_int == 0
684
+
685
+
686
+ def notnone[T](*args: T | Callable[[], T] | None) -> T | None:
687
+ """::
688
+
689
+ value = notnone(value, another_value, default_value)
690
+ value = notnone(value, lambda: default_value)
691
+
692
+ Return the first argument that is not ``None``. An argument that is
693
+ callable is called, and its value is returned (lazy evaluation). ::
694
+
695
+ from Pyjo.Util import getenv
696
+
697
+ def print_log(msg, **kwargs):
698
+ level = notnone(kwargs.get('log_level'), lambda: int(getenv('LOG_LEVEL', '3')))
699
+ ...
700
+ """
701
+ for a in args:
702
+ if a is not None:
703
+ # cast: a T that is callable itself is called too
704
+ return cast(Callable[[], T], a)() if callable(a) else a
705
+ return None
706
+
707
+
708
+ def punycode_decode(punycode: str) -> str:
709
+ """::
710
+
711
+ string = punycode_decode(punycode)
712
+
713
+ Punycode decode string as described in :rfc:`3492`. ::
714
+
715
+ # "bücher"
716
+ punycode_decode('bcher-kva')
717
+ """
718
+ return punycode.encode('ascii').decode('punycode')
719
+
720
+
721
+ def punycode_encode(string: str) -> str:
722
+ """::
723
+
724
+ punycode = punycode_encode(string)
725
+
726
+ Punycode encode string as described in :rfc:`3492`. ::
727
+
728
+ # "bcher-kva"
729
+ punycode_encode('bücher')
730
+ """
731
+ return string.encode('punycode').decode('ascii')
732
+
733
+
734
+ re_b_quote = re.compile(rb'(["\\])')
735
+ re_u_quote = re.compile(r'(["\\])')
736
+
737
+
738
+ @overload
739
+ def quote(string: str) -> str: ...
740
+
741
+
742
+ @overload
743
+ def quote(string: bytes) -> bytes: ...
744
+
745
+
746
+ def quote(string: str | bytes) -> str | bytes:
747
+ """::
748
+
749
+ quoted = quote(string)
750
+
751
+ Quote string.
752
+ """
753
+ if isinstance(string, bytes):
754
+ string = re_b_quote.sub(rb'\\\1', string)
755
+ return b'"' + string + b'"'
756
+ else:
757
+ string = re_u_quote.sub(r'\\\1', string)
758
+ return '"' + string + '"'
759
+
760
+
761
+ def random_bytes(count: int) -> bytes:
762
+ """::
763
+
764
+ random = random_bytes(count)
765
+
766
+ Generate ``count`` bytes worth of high entropy randomness.
767
+ """
768
+ if not isinstance(count, int) or count < 0: # pyright: ignore[reportUnnecessaryIsInstance]
769
+ import Pyjo.Exception
770
+
771
+ raise Pyjo.Exception.new('Byte count must be a defined, non-negative integer')
772
+ return secrets.token_bytes(count)
773
+
774
+
775
+ def scope_guard(cb: Callable[[], object]) -> '_Guard':
776
+ """::
777
+
778
+ with scope_guard(lambda: ...):
779
+ ...
780
+
781
+ Create anonymous scope guard object that will execute the passed callback
782
+ when the ``with`` block ends. ::
783
+
784
+ # Execute callback at end of block
785
+ with scope_guard(lambda: print('Mojo!')):
786
+ print('Hello')
787
+ """
788
+ # Unlike Mojolicious: a context manager, not an object that calls back when it is destroyed, as PyPy frees an
789
+ # object only when its garbage collector runs
790
+ return _Guard(cb)
791
+
792
+
793
+ def secure_compare(one: str | bytes, two: str | bytes) -> bool:
794
+ """::
795
+
796
+ boolean = secure_compare(string1, string2)
797
+
798
+ Constant time comparison algorithm to prevent timing attacks. The secret
799
+ string should be the second argument, to avoid leaking information about
800
+ the length of the string.
801
+ """
802
+ return hmac.compare_digest(
803
+ one.encode('UTF-8') if isinstance(one, str) else one, two.encode('UTF-8') if isinstance(two, str) else two
804
+ )
805
+
806
+
807
+ def setenv(name: str, value: str | None) -> None:
808
+ """::
809
+
810
+ setenv(name, value)
811
+
812
+ Set the environment variable or remove it if value is ``None``.
813
+ """
814
+ if value is None:
815
+ os.environ.pop(name, None)
816
+ else:
817
+ os.environ[name] = value
818
+
819
+
820
+ def sha1_bytes(data: bytes) -> bytes:
821
+ """::
822
+
823
+ checksum = sha1_bytes(data)
824
+
825
+ Generate binary SHA1 checksum for bytes with :mod:`hashlib`.
826
+ """
827
+ return hashlib.sha1(data).digest()
828
+
829
+
830
+ def sha1_sum(data: bytes) -> str:
831
+ """::
832
+
833
+ checksum = sha1_sum(data)
834
+
835
+ Generate SHA1 checksum for bytes with :mod:`hashlib`. ::
836
+
837
+ # "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
838
+ sha1_sum(b'foo')
839
+ """
840
+ return hashlib.sha1(data).hexdigest()
841
+
842
+
843
+ def slurp(path: str | os.PathLike[str], charset: str = 'utf-8') -> str:
844
+ """::
845
+
846
+ string = slurp('/etc/passwd')
847
+ string = slurp('/etc/passwd', 'utf-8')
848
+
849
+ Read all data at once from file as characters.
850
+ """
851
+ with open(path, 'rb') as f:
852
+ return f.read().decode(charset)
853
+
854
+
855
+ def slugify(value: str | int, allow_unicode: bool = False) -> str:
856
+ """::
857
+
858
+ slug = slugify(string)
859
+ slug = slugify(string, boolean)
860
+
861
+ Returns a URL slug generated from the input string. Non-word characters are
862
+ removed, the string is trimmed and lowercased, and whitespace characters
863
+ are replaced by a dash. By default, non-ASCII characters are normalized to
864
+ ASCII word characters or removed, but if a true value is passed as the
865
+ second parameter, all word characters will be allowed in the result
866
+ according to unicode semantics. ::
867
+
868
+ # "joel-is-a-slug"
869
+ slugify('Joel is a slug')
870
+
871
+ # "this-is-my-resume"
872
+ slugify('This is: my - résumé! ☃ ')
873
+
874
+ # "this-is-my-résumé"
875
+ slugify('This is: my - résumé! ☃ ', True)
876
+ """
877
+ if allow_unicode:
878
+ # Perl's \w matches the combining marks, Python's does not
879
+ value = ''.join(
880
+ c
881
+ for c in unicodedata.normalize('NFKC', str(value))
882
+ if re.match(r'[\w\s-]', c) or unicodedata.category(c).startswith('M')
883
+ )
884
+ else:
885
+ value = re.sub(r'[^a-zA-Z0-9_\t\n\x0b\f\r -]+', '', unicodedata.normalize('NFKD', str(value)))
886
+ return re.sub(r'[-\s]+', '-', trim(value).lower())
887
+
888
+
889
+ def split_cookie_header(string: str) -> list[list[tuple[str, str | None]]]:
890
+ """::
891
+
892
+ tree = split_cookie_header('a=b; expires=Thu, 07 Aug 2008 07:07:59 GMT')
893
+
894
+ Same as :func:`split_header`, but handles ``expires`` values from
895
+ :rfc:`6265`.
896
+ """
897
+ return _header(string, True)
898
+
899
+
900
+ def split_header(string: str) -> list[list[tuple[str, str | None]]]:
901
+ """::
902
+
903
+ tree = split_header('foo="bar baz"; test=123, yada')
904
+
905
+ Split HTTP header value into key/value pairs, each comma separated part gets
906
+ its own list of ``(key, value)`` tuples, and keys without a value get
907
+ ``None`` assigned. ::
908
+
909
+ # 'one'
910
+ split_header('one; two="three four", five=six')[0][0][0]
911
+
912
+ # 'two'
913
+ split_header('one; two="three four", five=six')[0][1][0]
914
+
915
+ # 'three four'
916
+ split_header('one; two="three four", five=six')[0][1][1]
917
+
918
+ # 'five'
919
+ split_header('one; two="three four", five=six')[1][0][0]
920
+
921
+ # 'six'
922
+ split_header('one; two="three four", five=six')[1][0][1]
923
+ """
924
+ return _header(string, False)
925
+
926
+
927
+ def spurt(content: str, path: str | os.PathLike[str], charset: str = 'utf-8') -> int:
928
+ """::
929
+
930
+ written = spurt(string, '/etc/passwd')
931
+ written = spurt(string, '/etc/passwd', 'utf-8')
932
+
933
+ Write all characters at once to file, and return the number of bytes
934
+ written.
935
+ """
936
+ with open(path, 'wb') as f:
937
+ return f.write(content.encode(charset))
938
+
939
+
940
+ def steady_time() -> float:
941
+ """::
942
+
943
+ time = steady_time()
944
+
945
+ High resolution time elapsed from an arbitrary fixed point in the past,
946
+ resilient to time jumps if a monotonic clock is available through
947
+ :func:`time.monotonic`.
948
+ """
949
+ return time.monotonic()
950
+
951
+
952
+ def tablify(rows: Sequence[Sequence[object]]) -> str:
953
+ r"""::
954
+
955
+ table = tablify([['foo', 'bar'], ['baz', 'yada']])
956
+
957
+ Row-oriented generator for text tables. ::
958
+
959
+ # "foo bar\nyada yada\nbaz yada\n"
960
+ tablify([['foo', 'bar'], ['yada', 'yada'], ['baz', 'yada']])
961
+ """
962
+ table = [[re.sub(r'[\r\n]', '', str(v) if v is not None else '') for v in row] for row in rows]
963
+
964
+ spec: list[int] = []
965
+ for row in table:
966
+ for i, v in enumerate(row):
967
+ if i >= len(spec):
968
+ spec.append(len(v))
969
+ elif len(v) >= spec[i]:
970
+ spec[i] = len(v)
971
+
972
+ return ''.join(
973
+ ' '.join(v.ljust(spec[i]) if i < len(spec) - 1 else v for i, v in enumerate(row)) + '\n' for row in table
974
+ )
975
+
976
+
977
+ def term_escape(string: str) -> str:
978
+ r"""::
979
+
980
+ escaped = term_escape(string)
981
+
982
+ Escape all POSIX control characters except for ``\n``. ::
983
+
984
+ # "foo\\x09bar\\x0d\n"
985
+ term_escape("foo\tbar\r\n")
986
+ """
987
+ return re.sub(r'[\x00-\x09\x0b-\x1f\x7f\x80-\x9f]', lambda m: f'\\x{ord(m[0]):02x}', string)
988
+
989
+
990
+ def trim(string: str) -> str:
991
+ """::
992
+
993
+ trimmed = trim(string)
994
+
995
+ Trim whitespace characters from both ends of string. ::
996
+
997
+ # "foo bar"
998
+ trim(' foo bar ')
999
+ """
1000
+ return re.sub(r'\s+\Z', '', re.sub(r'\A\s+', '', string))
1001
+
1002
+
1003
+ def unindent(string: str) -> str:
1004
+ r"""::
1005
+
1006
+ unindented = unindent(string)
1007
+
1008
+ Unindent multi-line string. ::
1009
+
1010
+ # "foo\nbar\nbaz\n"
1011
+ unindent(" foo\n bar\n baz\n")
1012
+ """
1013
+ indents = [len(m[0]) for line in string.split('\n') if (m := re.match(r'[ \t]*', line)) and len(m[0])]
1014
+ if indents:
1015
+ string = re.sub(rf'^[ \t]{{0,{min(indents)}}}', '', string, flags=re.M)
1016
+ return string
1017
+
1018
+
1019
+ @overload
1020
+ def unquote(string: str) -> str: ...
1021
+
1022
+
1023
+ @overload
1024
+ def unquote(string: bytes) -> bytes: ...
1025
+
1026
+
1027
+ def unquote(string: str | bytes) -> str | bytes:
1028
+ """::
1029
+
1030
+ string = unquote(quoted)
1031
+
1032
+ Unquote string.
1033
+ """
1034
+ if isinstance(string, str):
1035
+ string, n = re.subn(r'^"(.*)"$', lambda m: m[1], string)
1036
+ return string.replace('\\\\', '\\').replace('\\"', '"') if n else string
1037
+ string, n = re.subn(rb'^"(.*)"$', lambda m: m[1], bytes(string))
1038
+ return string.replace(b'\\\\', b'\\').replace(b'\\"', b'"') if n else string
1039
+
1040
+
1041
+ @overload
1042
+ def url_escape(string: str, pattern: str | None = None) -> str: ...
1043
+
1044
+
1045
+ @overload
1046
+ def url_escape(string: bytes, pattern: bytes | None = None) -> bytes: ...
1047
+
1048
+
1049
+ def url_escape(string: str | bytes, pattern: str | bytes | None = None) -> str | bytes:
1050
+ r"""::
1051
+
1052
+ escaped = url_escape(string)
1053
+ escaped = url_escape(string, r'^A-Za-z0-9\-._~')
1054
+
1055
+ Percent encode unsafe characters in string as described in :rfc:`3986`,
1056
+ the pattern used defaults to ``^A-Za-z0-9\-._~``. ::
1057
+
1058
+ # "foo%3Bbar"
1059
+ url_escape('foo;bar')
1060
+ """
1061
+ if isinstance(string, str):
1062
+ regex = _url_pattern(pattern.decode('latin-1') if isinstance(pattern, bytes) else pattern or r'^A-Za-z0-9\-._~')
1063
+ return regex.sub(lambda m: f'%{ord(m[0]):02X}', string)
1064
+ regex = _url_pattern(pattern.encode('latin-1') if isinstance(pattern, str) else pattern or rb'^A-Za-z0-9\-._~')
1065
+ return regex.sub(lambda m: b'%%%02X' % m[0][0], string)
1066
+
1067
+
1068
+ @overload
1069
+ def url_unescape(string: str) -> str: ...
1070
+
1071
+
1072
+ @overload
1073
+ def url_unescape(string: bytes) -> bytes: ...
1074
+
1075
+
1076
+ def url_unescape(string: str | bytes) -> str | bytes:
1077
+ """::
1078
+
1079
+ string = url_unescape(escaped)
1080
+
1081
+ Decode percent encoded characters in string as described in :rfc:`3986`.
1082
+ A percent encoded byte in a string becomes the character with the same
1083
+ code, as in Perl. ::
1084
+
1085
+ # "foo;bar"
1086
+ url_unescape('foo%3Bbar')
1087
+ """
1088
+ if isinstance(string, str):
1089
+ return re.sub(r'%([0-9a-fA-F]{2})', lambda m: chr(int(m[1], 16)), string)
1090
+ return re.sub(rb'%([0-9a-fA-F]{2})', lambda m: bytes([int(m[1], 16)]), string)
1091
+
1092
+
1093
+ def warn(*args: object) -> None:
1094
+ """::
1095
+
1096
+ warn(string)
1097
+ warn(string, value)
1098
+
1099
+ Output the arguments to ``sys.stderr``, joined as Perl's ``warn`` joins a
1100
+ list, with a newline at the end unless there is one already.
1101
+ """
1102
+ msg = ''.join(map(str, args))
1103
+ if not msg.endswith('\n'):
1104
+ msg += '\n'
1105
+ print(msg, end='', file=sys.stderr)
1106
+
1107
+
1108
+ # Characters that should be escaped in XML
1109
+ XML = {'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'}
1110
+
1111
+ # Numeric character references to C1 controls are remapped through windows-1252 (WHATWG HTML)
1112
+ _C1 = {
1113
+ 0x80: 0x20AC,
1114
+ 0x82: 0x201A,
1115
+ 0x83: 0x0192,
1116
+ 0x84: 0x201E,
1117
+ 0x85: 0x2026,
1118
+ 0x86: 0x2020,
1119
+ 0x87: 0x2021,
1120
+ 0x88: 0x02C6,
1121
+ 0x89: 0x2030,
1122
+ 0x8A: 0x0160,
1123
+ 0x8B: 0x2039,
1124
+ 0x8C: 0x0152,
1125
+ 0x8E: 0x017D,
1126
+ 0x91: 0x2018,
1127
+ 0x92: 0x2019,
1128
+ 0x93: 0x201C,
1129
+ 0x94: 0x201D,
1130
+ 0x95: 0x2022,
1131
+ 0x96: 0x2013,
1132
+ 0x97: 0x2014,
1133
+ 0x98: 0x02DC,
1134
+ 0x99: 0x2122,
1135
+ 0x9A: 0x0161,
1136
+ 0x9B: 0x203A,
1137
+ 0x9C: 0x0153,
1138
+ 0x9E: 0x017E,
1139
+ 0x9F: 0x0178,
1140
+ }
1141
+
1142
+
1143
+ # Not in Mojolicious: markup, as the __html__ protocol of markupsafe, Jinja and Django marks it
1144
+ class Markup(Protocol):
1145
+ def __html__(self) -> str: ...
1146
+
1147
+
1148
+ def xml_escape(string: str | Markup | None) -> str:
1149
+ """::
1150
+
1151
+ escaped = xml_escape(string)
1152
+
1153
+ Escape unsafe characters ``&``, ``<``, ``>``, ``"`` and ``'`` in string,
1154
+ but do not escape markup: an object with the method ``__html__``, as
1155
+ :mod:`Pyjo.String.HTML`, which gives the markup. ::
1156
+
1157
+ # "&lt;div&gt;"
1158
+ xml_escape('<div>')
1159
+
1160
+ # "<div>"
1161
+ from Pyjo.String.HTML import html
1162
+ xml_escape(html('<div>'))
1163
+ """
1164
+ # Unlike Mojolicious: markup is what has __html__, not every Pyjo.String.Unicode, as Mojo leaves every
1165
+ # Mojo::ByteStream, which u() and Pyjo.Collection.join make of any string
1166
+ # The markup as a plain string, which a string joins as it is, where the markup itself would escape the string
1167
+ if (markup := getattr(string, '__html__', None)) is not None:
1168
+ return str(markup())
1169
+ string = str(string) if string is not None else ''
1170
+ return re.sub(r'[&<>"\']', lambda m: XML[m[0]], string)
1171
+
1172
+
1173
+ def xor_encode(data: bytes, key: bytes) -> bytes:
1174
+ """::
1175
+
1176
+ encoded = xor_encode(data, key)
1177
+
1178
+ XOR encode string with variable length key.
1179
+ """
1180
+ size = len(data)
1181
+ key = (bytes(key) * (size // len(key) + 1))[:size]
1182
+ return (int.from_bytes(data) ^ int.from_bytes(key)).to_bytes(size)
1183
+
1184
+
1185
+ ENTITIES = {
1186
+ 'Aacute;': '\xc1',
1187
+ 'Aacute': '\xc1',
1188
+ 'aacute;': '\xe1',
1189
+ 'aacute': '\xe1',
1190
+ 'Abreve;': '\u0102',
1191
+ 'abreve;': '\u0103',
1192
+ 'ac;': '\u223e',
1193
+ 'acd;': '\u223f',
1194
+ 'acE;': '\u223e\u0333',
1195
+ 'Acirc;': '\xc2',
1196
+ 'Acirc': '\xc2',
1197
+ 'acirc;': '\xe2',
1198
+ 'acirc': '\xe2',
1199
+ 'acute;': '\xb4',
1200
+ 'acute': '\xb4',
1201
+ 'Acy;': '\u0410',
1202
+ 'acy;': '\u0430',
1203
+ 'AElig;': '\xc6',
1204
+ 'AElig': '\xc6',
1205
+ 'aelig;': '\xe6',
1206
+ 'aelig': '\xe6',
1207
+ 'af;': '\u2061',
1208
+ 'Afr;': '\U0001d504',
1209
+ 'afr;': '\U0001d51e',
1210
+ 'Agrave;': '\xc0',
1211
+ 'Agrave': '\xc0',
1212
+ 'agrave;': '\xe0',
1213
+ 'agrave': '\xe0',
1214
+ 'alefsym;': '\u2135',
1215
+ 'aleph;': '\u2135',
1216
+ 'Alpha;': '\u0391',
1217
+ 'alpha;': '\u03b1',
1218
+ 'Amacr;': '\u0100',
1219
+ 'amacr;': '\u0101',
1220
+ 'amalg;': '\u2a3f',
1221
+ 'AMP;': '\x26',
1222
+ 'AMP': '\x26',
1223
+ 'amp;': '\x26',
1224
+ 'amp': '\x26',
1225
+ 'And;': '\u2a53',
1226
+ 'and;': '\u2227',
1227
+ 'andand;': '\u2a55',
1228
+ 'andd;': '\u2a5c',
1229
+ 'andslope;': '\u2a58',
1230
+ 'andv;': '\u2a5a',
1231
+ 'ang;': '\u2220',
1232
+ 'ange;': '\u29a4',
1233
+ 'angle;': '\u2220',
1234
+ 'angmsd;': '\u2221',
1235
+ 'angmsdaa;': '\u29a8',
1236
+ 'angmsdab;': '\u29a9',
1237
+ 'angmsdac;': '\u29aa',
1238
+ 'angmsdad;': '\u29ab',
1239
+ 'angmsdae;': '\u29ac',
1240
+ 'angmsdaf;': '\u29ad',
1241
+ 'angmsdag;': '\u29ae',
1242
+ 'angmsdah;': '\u29af',
1243
+ 'angrt;': '\u221f',
1244
+ 'angrtvb;': '\u22be',
1245
+ 'angrtvbd;': '\u299d',
1246
+ 'angsph;': '\u2222',
1247
+ 'angst;': '\xc5',
1248
+ 'angzarr;': '\u237c',
1249
+ 'Aogon;': '\u0104',
1250
+ 'aogon;': '\u0105',
1251
+ 'Aopf;': '\U0001d538',
1252
+ 'aopf;': '\U0001d552',
1253
+ 'ap;': '\u2248',
1254
+ 'apacir;': '\u2a6f',
1255
+ 'apE;': '\u2a70',
1256
+ 'ape;': '\u224a',
1257
+ 'apid;': '\u224b',
1258
+ 'apos;': '\x27',
1259
+ 'ApplyFunction;': '\u2061',
1260
+ 'approx;': '\u2248',
1261
+ 'approxeq;': '\u224a',
1262
+ 'Aring;': '\xc5',
1263
+ 'Aring': '\xc5',
1264
+ 'aring;': '\xe5',
1265
+ 'aring': '\xe5',
1266
+ 'Ascr;': '\U0001d49c',
1267
+ 'ascr;': '\U0001d4b6',
1268
+ 'Assign;': '\u2254',
1269
+ 'ast;': '\x2a',
1270
+ 'asymp;': '\u2248',
1271
+ 'asympeq;': '\u224d',
1272
+ 'Atilde;': '\xc3',
1273
+ 'Atilde': '\xc3',
1274
+ 'atilde;': '\xe3',
1275
+ 'atilde': '\xe3',
1276
+ 'Auml;': '\xc4',
1277
+ 'Auml': '\xc4',
1278
+ 'auml;': '\xe4',
1279
+ 'auml': '\xe4',
1280
+ 'awconint;': '\u2233',
1281
+ 'awint;': '\u2a11',
1282
+ 'backcong;': '\u224c',
1283
+ 'backepsilon;': '\u03f6',
1284
+ 'backprime;': '\u2035',
1285
+ 'backsim;': '\u223d',
1286
+ 'backsimeq;': '\u22cd',
1287
+ 'Backslash;': '\u2216',
1288
+ 'Barv;': '\u2ae7',
1289
+ 'barvee;': '\u22bd',
1290
+ 'Barwed;': '\u2306',
1291
+ 'barwed;': '\u2305',
1292
+ 'barwedge;': '\u2305',
1293
+ 'bbrk;': '\u23b5',
1294
+ 'bbrktbrk;': '\u23b6',
1295
+ 'bcong;': '\u224c',
1296
+ 'Bcy;': '\u0411',
1297
+ 'bcy;': '\u0431',
1298
+ 'bdquo;': '\u201e',
1299
+ 'becaus;': '\u2235',
1300
+ 'Because;': '\u2235',
1301
+ 'because;': '\u2235',
1302
+ 'bemptyv;': '\u29b0',
1303
+ 'bepsi;': '\u03f6',
1304
+ 'bernou;': '\u212c',
1305
+ 'Bernoullis;': '\u212c',
1306
+ 'Beta;': '\u0392',
1307
+ 'beta;': '\u03b2',
1308
+ 'beth;': '\u2136',
1309
+ 'between;': '\u226c',
1310
+ 'Bfr;': '\U0001d505',
1311
+ 'bfr;': '\U0001d51f',
1312
+ 'bigcap;': '\u22c2',
1313
+ 'bigcirc;': '\u25ef',
1314
+ 'bigcup;': '\u22c3',
1315
+ 'bigodot;': '\u2a00',
1316
+ 'bigoplus;': '\u2a01',
1317
+ 'bigotimes;': '\u2a02',
1318
+ 'bigsqcup;': '\u2a06',
1319
+ 'bigstar;': '\u2605',
1320
+ 'bigtriangledown;': '\u25bd',
1321
+ 'bigtriangleup;': '\u25b3',
1322
+ 'biguplus;': '\u2a04',
1323
+ 'bigvee;': '\u22c1',
1324
+ 'bigwedge;': '\u22c0',
1325
+ 'bkarow;': '\u290d',
1326
+ 'blacklozenge;': '\u29eb',
1327
+ 'blacksquare;': '\u25aa',
1328
+ 'blacktriangle;': '\u25b4',
1329
+ 'blacktriangledown;': '\u25be',
1330
+ 'blacktriangleleft;': '\u25c2',
1331
+ 'blacktriangleright;': '\u25b8',
1332
+ 'blank;': '\u2423',
1333
+ 'blk12;': '\u2592',
1334
+ 'blk14;': '\u2591',
1335
+ 'blk34;': '\u2593',
1336
+ 'block;': '\u2588',
1337
+ 'bne;': '\x3d\u20e5',
1338
+ 'bnequiv;': '\u2261\u20e5',
1339
+ 'bNot;': '\u2aed',
1340
+ 'bnot;': '\u2310',
1341
+ 'Bopf;': '\U0001d539',
1342
+ 'bopf;': '\U0001d553',
1343
+ 'bot;': '\u22a5',
1344
+ 'bottom;': '\u22a5',
1345
+ 'bowtie;': '\u22c8',
1346
+ 'boxbox;': '\u29c9',
1347
+ 'boxDL;': '\u2557',
1348
+ 'boxDl;': '\u2556',
1349
+ 'boxdL;': '\u2555',
1350
+ 'boxdl;': '\u2510',
1351
+ 'boxDR;': '\u2554',
1352
+ 'boxDr;': '\u2553',
1353
+ 'boxdR;': '\u2552',
1354
+ 'boxdr;': '\u250c',
1355
+ 'boxH;': '\u2550',
1356
+ 'boxh;': '\u2500',
1357
+ 'boxHD;': '\u2566',
1358
+ 'boxHd;': '\u2564',
1359
+ 'boxhD;': '\u2565',
1360
+ 'boxhd;': '\u252c',
1361
+ 'boxHU;': '\u2569',
1362
+ 'boxHu;': '\u2567',
1363
+ 'boxhU;': '\u2568',
1364
+ 'boxhu;': '\u2534',
1365
+ 'boxminus;': '\u229f',
1366
+ 'boxplus;': '\u229e',
1367
+ 'boxtimes;': '\u22a0',
1368
+ 'boxUL;': '\u255d',
1369
+ 'boxUl;': '\u255c',
1370
+ 'boxuL;': '\u255b',
1371
+ 'boxul;': '\u2518',
1372
+ 'boxUR;': '\u255a',
1373
+ 'boxUr;': '\u2559',
1374
+ 'boxuR;': '\u2558',
1375
+ 'boxur;': '\u2514',
1376
+ 'boxV;': '\u2551',
1377
+ 'boxv;': '\u2502',
1378
+ 'boxVH;': '\u256c',
1379
+ 'boxVh;': '\u256b',
1380
+ 'boxvH;': '\u256a',
1381
+ 'boxvh;': '\u253c',
1382
+ 'boxVL;': '\u2563',
1383
+ 'boxVl;': '\u2562',
1384
+ 'boxvL;': '\u2561',
1385
+ 'boxvl;': '\u2524',
1386
+ 'boxVR;': '\u2560',
1387
+ 'boxVr;': '\u255f',
1388
+ 'boxvR;': '\u255e',
1389
+ 'boxvr;': '\u251c',
1390
+ 'bprime;': '\u2035',
1391
+ 'Breve;': '\u02d8',
1392
+ 'breve;': '\u02d8',
1393
+ 'brvbar;': '\xa6',
1394
+ 'brvbar': '\xa6',
1395
+ 'Bscr;': '\u212c',
1396
+ 'bscr;': '\U0001d4b7',
1397
+ 'bsemi;': '\u204f',
1398
+ 'bsim;': '\u223d',
1399
+ 'bsime;': '\u22cd',
1400
+ 'bsol;': '\x5c',
1401
+ 'bsolb;': '\u29c5',
1402
+ 'bsolhsub;': '\u27c8',
1403
+ 'bull;': '\u2022',
1404
+ 'bullet;': '\u2022',
1405
+ 'bump;': '\u224e',
1406
+ 'bumpE;': '\u2aae',
1407
+ 'bumpe;': '\u224f',
1408
+ 'Bumpeq;': '\u224e',
1409
+ 'bumpeq;': '\u224f',
1410
+ 'Cacute;': '\u0106',
1411
+ 'cacute;': '\u0107',
1412
+ 'Cap;': '\u22d2',
1413
+ 'cap;': '\u2229',
1414
+ 'capand;': '\u2a44',
1415
+ 'capbrcup;': '\u2a49',
1416
+ 'capcap;': '\u2a4b',
1417
+ 'capcup;': '\u2a47',
1418
+ 'capdot;': '\u2a40',
1419
+ 'CapitalDifferentialD;': '\u2145',
1420
+ 'caps;': '\u2229\ufe00',
1421
+ 'caret;': '\u2041',
1422
+ 'caron;': '\u02c7',
1423
+ 'Cayleys;': '\u212d',
1424
+ 'ccaps;': '\u2a4d',
1425
+ 'Ccaron;': '\u010c',
1426
+ 'ccaron;': '\u010d',
1427
+ 'Ccedil;': '\xc7',
1428
+ 'Ccedil': '\xc7',
1429
+ 'ccedil;': '\xe7',
1430
+ 'ccedil': '\xe7',
1431
+ 'Ccirc;': '\u0108',
1432
+ 'ccirc;': '\u0109',
1433
+ 'Cconint;': '\u2230',
1434
+ 'ccups;': '\u2a4c',
1435
+ 'ccupssm;': '\u2a50',
1436
+ 'Cdot;': '\u010a',
1437
+ 'cdot;': '\u010b',
1438
+ 'cedil;': '\xb8',
1439
+ 'cedil': '\xb8',
1440
+ 'Cedilla;': '\xb8',
1441
+ 'cemptyv;': '\u29b2',
1442
+ 'cent;': '\xa2',
1443
+ 'cent': '\xa2',
1444
+ 'CenterDot;': '\xb7',
1445
+ 'centerdot;': '\xb7',
1446
+ 'Cfr;': '\u212d',
1447
+ 'cfr;': '\U0001d520',
1448
+ 'CHcy;': '\u0427',
1449
+ 'chcy;': '\u0447',
1450
+ 'check;': '\u2713',
1451
+ 'checkmark;': '\u2713',
1452
+ 'Chi;': '\u03a7',
1453
+ 'chi;': '\u03c7',
1454
+ 'cir;': '\u25cb',
1455
+ 'circ;': '\u02c6',
1456
+ 'circeq;': '\u2257',
1457
+ 'circlearrowleft;': '\u21ba',
1458
+ 'circlearrowright;': '\u21bb',
1459
+ 'circledast;': '\u229b',
1460
+ 'circledcirc;': '\u229a',
1461
+ 'circleddash;': '\u229d',
1462
+ 'CircleDot;': '\u2299',
1463
+ 'circledR;': '\xae',
1464
+ 'circledS;': '\u24c8',
1465
+ 'CircleMinus;': '\u2296',
1466
+ 'CirclePlus;': '\u2295',
1467
+ 'CircleTimes;': '\u2297',
1468
+ 'cirE;': '\u29c3',
1469
+ 'cire;': '\u2257',
1470
+ 'cirfnint;': '\u2a10',
1471
+ 'cirmid;': '\u2aef',
1472
+ 'cirscir;': '\u29c2',
1473
+ 'ClockwiseContourIntegral;': '\u2232',
1474
+ 'CloseCurlyDoubleQuote;': '\u201d',
1475
+ 'CloseCurlyQuote;': '\u2019',
1476
+ 'clubs;': '\u2663',
1477
+ 'clubsuit;': '\u2663',
1478
+ 'Colon;': '\u2237',
1479
+ 'colon;': '\x3a',
1480
+ 'Colone;': '\u2a74',
1481
+ 'colone;': '\u2254',
1482
+ 'coloneq;': '\u2254',
1483
+ 'comma;': '\x2c',
1484
+ 'commat;': '\x40',
1485
+ 'comp;': '\u2201',
1486
+ 'compfn;': '\u2218',
1487
+ 'complement;': '\u2201',
1488
+ 'complexes;': '\u2102',
1489
+ 'cong;': '\u2245',
1490
+ 'congdot;': '\u2a6d',
1491
+ 'Congruent;': '\u2261',
1492
+ 'Conint;': '\u222f',
1493
+ 'conint;': '\u222e',
1494
+ 'ContourIntegral;': '\u222e',
1495
+ 'Copf;': '\u2102',
1496
+ 'copf;': '\U0001d554',
1497
+ 'coprod;': '\u2210',
1498
+ 'Coproduct;': '\u2210',
1499
+ 'COPY;': '\xa9',
1500
+ 'COPY': '\xa9',
1501
+ 'copy;': '\xa9',
1502
+ 'copy': '\xa9',
1503
+ 'copysr;': '\u2117',
1504
+ 'CounterClockwiseContourIntegral;': '\u2233',
1505
+ 'crarr;': '\u21b5',
1506
+ 'Cross;': '\u2a2f',
1507
+ 'cross;': '\u2717',
1508
+ 'Cscr;': '\U0001d49e',
1509
+ 'cscr;': '\U0001d4b8',
1510
+ 'csub;': '\u2acf',
1511
+ 'csube;': '\u2ad1',
1512
+ 'csup;': '\u2ad0',
1513
+ 'csupe;': '\u2ad2',
1514
+ 'ctdot;': '\u22ef',
1515
+ 'cudarrl;': '\u2938',
1516
+ 'cudarrr;': '\u2935',
1517
+ 'cuepr;': '\u22de',
1518
+ 'cuesc;': '\u22df',
1519
+ 'cularr;': '\u21b6',
1520
+ 'cularrp;': '\u293d',
1521
+ 'Cup;': '\u22d3',
1522
+ 'cup;': '\u222a',
1523
+ 'cupbrcap;': '\u2a48',
1524
+ 'CupCap;': '\u224d',
1525
+ 'cupcap;': '\u2a46',
1526
+ 'cupcup;': '\u2a4a',
1527
+ 'cupdot;': '\u228d',
1528
+ 'cupor;': '\u2a45',
1529
+ 'cups;': '\u222a\ufe00',
1530
+ 'curarr;': '\u21b7',
1531
+ 'curarrm;': '\u293c',
1532
+ 'curlyeqprec;': '\u22de',
1533
+ 'curlyeqsucc;': '\u22df',
1534
+ 'curlyvee;': '\u22ce',
1535
+ 'curlywedge;': '\u22cf',
1536
+ 'curren;': '\xa4',
1537
+ 'curren': '\xa4',
1538
+ 'curvearrowleft;': '\u21b6',
1539
+ 'curvearrowright;': '\u21b7',
1540
+ 'cuvee;': '\u22ce',
1541
+ 'cuwed;': '\u22cf',
1542
+ 'cwconint;': '\u2232',
1543
+ 'cwint;': '\u2231',
1544
+ 'cylcty;': '\u232d',
1545
+ 'Dagger;': '\u2021',
1546
+ 'dagger;': '\u2020',
1547
+ 'daleth;': '\u2138',
1548
+ 'Darr;': '\u21a1',
1549
+ 'dArr;': '\u21d3',
1550
+ 'darr;': '\u2193',
1551
+ 'dash;': '\u2010',
1552
+ 'Dashv;': '\u2ae4',
1553
+ 'dashv;': '\u22a3',
1554
+ 'dbkarow;': '\u290f',
1555
+ 'dblac;': '\u02dd',
1556
+ 'Dcaron;': '\u010e',
1557
+ 'dcaron;': '\u010f',
1558
+ 'Dcy;': '\u0414',
1559
+ 'dcy;': '\u0434',
1560
+ 'DD;': '\u2145',
1561
+ 'dd;': '\u2146',
1562
+ 'ddagger;': '\u2021',
1563
+ 'ddarr;': '\u21ca',
1564
+ 'DDotrahd;': '\u2911',
1565
+ 'ddotseq;': '\u2a77',
1566
+ 'deg;': '\xb0',
1567
+ 'deg': '\xb0',
1568
+ 'Del;': '\u2207',
1569
+ 'Delta;': '\u0394',
1570
+ 'delta;': '\u03b4',
1571
+ 'demptyv;': '\u29b1',
1572
+ 'dfisht;': '\u297f',
1573
+ 'Dfr;': '\U0001d507',
1574
+ 'dfr;': '\U0001d521',
1575
+ 'dHar;': '\u2965',
1576
+ 'dharl;': '\u21c3',
1577
+ 'dharr;': '\u21c2',
1578
+ 'DiacriticalAcute;': '\xb4',
1579
+ 'DiacriticalDot;': '\u02d9',
1580
+ 'DiacriticalDoubleAcute;': '\u02dd',
1581
+ 'DiacriticalGrave;': '\x60',
1582
+ 'DiacriticalTilde;': '\u02dc',
1583
+ 'diam;': '\u22c4',
1584
+ 'Diamond;': '\u22c4',
1585
+ 'diamond;': '\u22c4',
1586
+ 'diamondsuit;': '\u2666',
1587
+ 'diams;': '\u2666',
1588
+ 'die;': '\xa8',
1589
+ 'DifferentialD;': '\u2146',
1590
+ 'digamma;': '\u03dd',
1591
+ 'disin;': '\u22f2',
1592
+ 'div;': '\xf7',
1593
+ 'divide;': '\xf7',
1594
+ 'divide': '\xf7',
1595
+ 'divideontimes;': '\u22c7',
1596
+ 'divonx;': '\u22c7',
1597
+ 'DJcy;': '\u0402',
1598
+ 'djcy;': '\u0452',
1599
+ 'dlcorn;': '\u231e',
1600
+ 'dlcrop;': '\u230d',
1601
+ 'dollar;': '\x24',
1602
+ 'Dopf;': '\U0001d53b',
1603
+ 'dopf;': '\U0001d555',
1604
+ 'Dot;': '\xa8',
1605
+ 'dot;': '\u02d9',
1606
+ 'DotDot;': '\u20dc',
1607
+ 'doteq;': '\u2250',
1608
+ 'doteqdot;': '\u2251',
1609
+ 'DotEqual;': '\u2250',
1610
+ 'dotminus;': '\u2238',
1611
+ 'dotplus;': '\u2214',
1612
+ 'dotsquare;': '\u22a1',
1613
+ 'doublebarwedge;': '\u2306',
1614
+ 'DoubleContourIntegral;': '\u222f',
1615
+ 'DoubleDot;': '\xa8',
1616
+ 'DoubleDownArrow;': '\u21d3',
1617
+ 'DoubleLeftArrow;': '\u21d0',
1618
+ 'DoubleLeftRightArrow;': '\u21d4',
1619
+ 'DoubleLeftTee;': '\u2ae4',
1620
+ 'DoubleLongLeftArrow;': '\u27f8',
1621
+ 'DoubleLongLeftRightArrow;': '\u27fa',
1622
+ 'DoubleLongRightArrow;': '\u27f9',
1623
+ 'DoubleRightArrow;': '\u21d2',
1624
+ 'DoubleRightTee;': '\u22a8',
1625
+ 'DoubleUpArrow;': '\u21d1',
1626
+ 'DoubleUpDownArrow;': '\u21d5',
1627
+ 'DoubleVerticalBar;': '\u2225',
1628
+ 'DownArrow;': '\u2193',
1629
+ 'Downarrow;': '\u21d3',
1630
+ 'downarrow;': '\u2193',
1631
+ 'DownArrowBar;': '\u2913',
1632
+ 'DownArrowUpArrow;': '\u21f5',
1633
+ 'DownBreve;': '\u0311',
1634
+ 'downdownarrows;': '\u21ca',
1635
+ 'downharpoonleft;': '\u21c3',
1636
+ 'downharpoonright;': '\u21c2',
1637
+ 'DownLeftRightVector;': '\u2950',
1638
+ 'DownLeftTeeVector;': '\u295e',
1639
+ 'DownLeftVector;': '\u21bd',
1640
+ 'DownLeftVectorBar;': '\u2956',
1641
+ 'DownRightTeeVector;': '\u295f',
1642
+ 'DownRightVector;': '\u21c1',
1643
+ 'DownRightVectorBar;': '\u2957',
1644
+ 'DownTee;': '\u22a4',
1645
+ 'DownTeeArrow;': '\u21a7',
1646
+ 'drbkarow;': '\u2910',
1647
+ 'drcorn;': '\u231f',
1648
+ 'drcrop;': '\u230c',
1649
+ 'Dscr;': '\U0001d49f',
1650
+ 'dscr;': '\U0001d4b9',
1651
+ 'DScy;': '\u0405',
1652
+ 'dscy;': '\u0455',
1653
+ 'dsol;': '\u29f6',
1654
+ 'Dstrok;': '\u0110',
1655
+ 'dstrok;': '\u0111',
1656
+ 'dtdot;': '\u22f1',
1657
+ 'dtri;': '\u25bf',
1658
+ 'dtrif;': '\u25be',
1659
+ 'duarr;': '\u21f5',
1660
+ 'duhar;': '\u296f',
1661
+ 'dwangle;': '\u29a6',
1662
+ 'DZcy;': '\u040f',
1663
+ 'dzcy;': '\u045f',
1664
+ 'dzigrarr;': '\u27ff',
1665
+ 'Eacute;': '\xc9',
1666
+ 'Eacute': '\xc9',
1667
+ 'eacute;': '\xe9',
1668
+ 'eacute': '\xe9',
1669
+ 'easter;': '\u2a6e',
1670
+ 'Ecaron;': '\u011a',
1671
+ 'ecaron;': '\u011b',
1672
+ 'ecir;': '\u2256',
1673
+ 'Ecirc;': '\xca',
1674
+ 'Ecirc': '\xca',
1675
+ 'ecirc;': '\xea',
1676
+ 'ecirc': '\xea',
1677
+ 'ecolon;': '\u2255',
1678
+ 'Ecy;': '\u042d',
1679
+ 'ecy;': '\u044d',
1680
+ 'eDDot;': '\u2a77',
1681
+ 'Edot;': '\u0116',
1682
+ 'eDot;': '\u2251',
1683
+ 'edot;': '\u0117',
1684
+ 'ee;': '\u2147',
1685
+ 'efDot;': '\u2252',
1686
+ 'Efr;': '\U0001d508',
1687
+ 'efr;': '\U0001d522',
1688
+ 'eg;': '\u2a9a',
1689
+ 'Egrave;': '\xc8',
1690
+ 'Egrave': '\xc8',
1691
+ 'egrave;': '\xe8',
1692
+ 'egrave': '\xe8',
1693
+ 'egs;': '\u2a96',
1694
+ 'egsdot;': '\u2a98',
1695
+ 'el;': '\u2a99',
1696
+ 'Element;': '\u2208',
1697
+ 'elinters;': '\u23e7',
1698
+ 'ell;': '\u2113',
1699
+ 'els;': '\u2a95',
1700
+ 'elsdot;': '\u2a97',
1701
+ 'Emacr;': '\u0112',
1702
+ 'emacr;': '\u0113',
1703
+ 'empty;': '\u2205',
1704
+ 'emptyset;': '\u2205',
1705
+ 'EmptySmallSquare;': '\u25fb',
1706
+ 'emptyv;': '\u2205',
1707
+ 'EmptyVerySmallSquare;': '\u25ab',
1708
+ 'emsp;': '\u2003',
1709
+ 'emsp13;': '\u2004',
1710
+ 'emsp14;': '\u2005',
1711
+ 'ENG;': '\u014a',
1712
+ 'eng;': '\u014b',
1713
+ 'ensp;': '\u2002',
1714
+ 'Eogon;': '\u0118',
1715
+ 'eogon;': '\u0119',
1716
+ 'Eopf;': '\U0001d53c',
1717
+ 'eopf;': '\U0001d556',
1718
+ 'epar;': '\u22d5',
1719
+ 'eparsl;': '\u29e3',
1720
+ 'eplus;': '\u2a71',
1721
+ 'epsi;': '\u03b5',
1722
+ 'Epsilon;': '\u0395',
1723
+ 'epsilon;': '\u03b5',
1724
+ 'epsiv;': '\u03f5',
1725
+ 'eqcirc;': '\u2256',
1726
+ 'eqcolon;': '\u2255',
1727
+ 'eqsim;': '\u2242',
1728
+ 'eqslantgtr;': '\u2a96',
1729
+ 'eqslantless;': '\u2a95',
1730
+ 'Equal;': '\u2a75',
1731
+ 'equals;': '\x3d',
1732
+ 'EqualTilde;': '\u2242',
1733
+ 'equest;': '\u225f',
1734
+ 'Equilibrium;': '\u21cc',
1735
+ 'equiv;': '\u2261',
1736
+ 'equivDD;': '\u2a78',
1737
+ 'eqvparsl;': '\u29e5',
1738
+ 'erarr;': '\u2971',
1739
+ 'erDot;': '\u2253',
1740
+ 'Escr;': '\u2130',
1741
+ 'escr;': '\u212f',
1742
+ 'esdot;': '\u2250',
1743
+ 'Esim;': '\u2a73',
1744
+ 'esim;': '\u2242',
1745
+ 'Eta;': '\u0397',
1746
+ 'eta;': '\u03b7',
1747
+ 'ETH;': '\xd0',
1748
+ 'ETH': '\xd0',
1749
+ 'eth;': '\xf0',
1750
+ 'eth': '\xf0',
1751
+ 'Euml;': '\xcb',
1752
+ 'Euml': '\xcb',
1753
+ 'euml;': '\xeb',
1754
+ 'euml': '\xeb',
1755
+ 'euro;': '\u20ac',
1756
+ 'excl;': '\x21',
1757
+ 'exist;': '\u2203',
1758
+ 'Exists;': '\u2203',
1759
+ 'expectation;': '\u2130',
1760
+ 'ExponentialE;': '\u2147',
1761
+ 'exponentiale;': '\u2147',
1762
+ 'fallingdotseq;': '\u2252',
1763
+ 'Fcy;': '\u0424',
1764
+ 'fcy;': '\u0444',
1765
+ 'female;': '\u2640',
1766
+ 'ffilig;': '\ufb03',
1767
+ 'fflig;': '\ufb00',
1768
+ 'ffllig;': '\ufb04',
1769
+ 'Ffr;': '\U0001d509',
1770
+ 'ffr;': '\U0001d523',
1771
+ 'filig;': '\ufb01',
1772
+ 'FilledSmallSquare;': '\u25fc',
1773
+ 'FilledVerySmallSquare;': '\u25aa',
1774
+ 'fjlig;': '\x66\x6a',
1775
+ 'flat;': '\u266d',
1776
+ 'fllig;': '\ufb02',
1777
+ 'fltns;': '\u25b1',
1778
+ 'fnof;': '\u0192',
1779
+ 'Fopf;': '\U0001d53d',
1780
+ 'fopf;': '\U0001d557',
1781
+ 'ForAll;': '\u2200',
1782
+ 'forall;': '\u2200',
1783
+ 'fork;': '\u22d4',
1784
+ 'forkv;': '\u2ad9',
1785
+ 'Fouriertrf;': '\u2131',
1786
+ 'fpartint;': '\u2a0d',
1787
+ 'frac12;': '\xbd',
1788
+ 'frac12': '\xbd',
1789
+ 'frac13;': '\u2153',
1790
+ 'frac14;': '\xbc',
1791
+ 'frac14': '\xbc',
1792
+ 'frac15;': '\u2155',
1793
+ 'frac16;': '\u2159',
1794
+ 'frac18;': '\u215b',
1795
+ 'frac23;': '\u2154',
1796
+ 'frac25;': '\u2156',
1797
+ 'frac34;': '\xbe',
1798
+ 'frac34': '\xbe',
1799
+ 'frac35;': '\u2157',
1800
+ 'frac38;': '\u215c',
1801
+ 'frac45;': '\u2158',
1802
+ 'frac56;': '\u215a',
1803
+ 'frac58;': '\u215d',
1804
+ 'frac78;': '\u215e',
1805
+ 'frasl;': '\u2044',
1806
+ 'frown;': '\u2322',
1807
+ 'Fscr;': '\u2131',
1808
+ 'fscr;': '\U0001d4bb',
1809
+ 'gacute;': '\u01f5',
1810
+ 'Gamma;': '\u0393',
1811
+ 'gamma;': '\u03b3',
1812
+ 'Gammad;': '\u03dc',
1813
+ 'gammad;': '\u03dd',
1814
+ 'gap;': '\u2a86',
1815
+ 'Gbreve;': '\u011e',
1816
+ 'gbreve;': '\u011f',
1817
+ 'Gcedil;': '\u0122',
1818
+ 'Gcirc;': '\u011c',
1819
+ 'gcirc;': '\u011d',
1820
+ 'Gcy;': '\u0413',
1821
+ 'gcy;': '\u0433',
1822
+ 'Gdot;': '\u0120',
1823
+ 'gdot;': '\u0121',
1824
+ 'gE;': '\u2267',
1825
+ 'ge;': '\u2265',
1826
+ 'gEl;': '\u2a8c',
1827
+ 'gel;': '\u22db',
1828
+ 'geq;': '\u2265',
1829
+ 'geqq;': '\u2267',
1830
+ 'geqslant;': '\u2a7e',
1831
+ 'ges;': '\u2a7e',
1832
+ 'gescc;': '\u2aa9',
1833
+ 'gesdot;': '\u2a80',
1834
+ 'gesdoto;': '\u2a82',
1835
+ 'gesdotol;': '\u2a84',
1836
+ 'gesl;': '\u22db\ufe00',
1837
+ 'gesles;': '\u2a94',
1838
+ 'Gfr;': '\U0001d50a',
1839
+ 'gfr;': '\U0001d524',
1840
+ 'Gg;': '\u22d9',
1841
+ 'gg;': '\u226b',
1842
+ 'ggg;': '\u22d9',
1843
+ 'gimel;': '\u2137',
1844
+ 'GJcy;': '\u0403',
1845
+ 'gjcy;': '\u0453',
1846
+ 'gl;': '\u2277',
1847
+ 'gla;': '\u2aa5',
1848
+ 'glE;': '\u2a92',
1849
+ 'glj;': '\u2aa4',
1850
+ 'gnap;': '\u2a8a',
1851
+ 'gnapprox;': '\u2a8a',
1852
+ 'gnE;': '\u2269',
1853
+ 'gne;': '\u2a88',
1854
+ 'gneq;': '\u2a88',
1855
+ 'gneqq;': '\u2269',
1856
+ 'gnsim;': '\u22e7',
1857
+ 'Gopf;': '\U0001d53e',
1858
+ 'gopf;': '\U0001d558',
1859
+ 'grave;': '\x60',
1860
+ 'GreaterEqual;': '\u2265',
1861
+ 'GreaterEqualLess;': '\u22db',
1862
+ 'GreaterFullEqual;': '\u2267',
1863
+ 'GreaterGreater;': '\u2aa2',
1864
+ 'GreaterLess;': '\u2277',
1865
+ 'GreaterSlantEqual;': '\u2a7e',
1866
+ 'GreaterTilde;': '\u2273',
1867
+ 'Gscr;': '\U0001d4a2',
1868
+ 'gscr;': '\u210a',
1869
+ 'gsim;': '\u2273',
1870
+ 'gsime;': '\u2a8e',
1871
+ 'gsiml;': '\u2a90',
1872
+ 'GT;': '\x3e',
1873
+ 'GT': '\x3e',
1874
+ 'Gt;': '\u226b',
1875
+ 'gt;': '\x3e',
1876
+ 'gt': '\x3e',
1877
+ 'gtcc;': '\u2aa7',
1878
+ 'gtcir;': '\u2a7a',
1879
+ 'gtdot;': '\u22d7',
1880
+ 'gtlPar;': '\u2995',
1881
+ 'gtquest;': '\u2a7c',
1882
+ 'gtrapprox;': '\u2a86',
1883
+ 'gtrarr;': '\u2978',
1884
+ 'gtrdot;': '\u22d7',
1885
+ 'gtreqless;': '\u22db',
1886
+ 'gtreqqless;': '\u2a8c',
1887
+ 'gtrless;': '\u2277',
1888
+ 'gtrsim;': '\u2273',
1889
+ 'gvertneqq;': '\u2269\ufe00',
1890
+ 'gvnE;': '\u2269\ufe00',
1891
+ 'Hacek;': '\u02c7',
1892
+ 'hairsp;': '\u200a',
1893
+ 'half;': '\xbd',
1894
+ 'hamilt;': '\u210b',
1895
+ 'HARDcy;': '\u042a',
1896
+ 'hardcy;': '\u044a',
1897
+ 'hArr;': '\u21d4',
1898
+ 'harr;': '\u2194',
1899
+ 'harrcir;': '\u2948',
1900
+ 'harrw;': '\u21ad',
1901
+ 'Hat;': '\x5e',
1902
+ 'hbar;': '\u210f',
1903
+ 'Hcirc;': '\u0124',
1904
+ 'hcirc;': '\u0125',
1905
+ 'hearts;': '\u2665',
1906
+ 'heartsuit;': '\u2665',
1907
+ 'hellip;': '\u2026',
1908
+ 'hercon;': '\u22b9',
1909
+ 'Hfr;': '\u210c',
1910
+ 'hfr;': '\U0001d525',
1911
+ 'HilbertSpace;': '\u210b',
1912
+ 'hksearow;': '\u2925',
1913
+ 'hkswarow;': '\u2926',
1914
+ 'hoarr;': '\u21ff',
1915
+ 'homtht;': '\u223b',
1916
+ 'hookleftarrow;': '\u21a9',
1917
+ 'hookrightarrow;': '\u21aa',
1918
+ 'Hopf;': '\u210d',
1919
+ 'hopf;': '\U0001d559',
1920
+ 'horbar;': '\u2015',
1921
+ 'HorizontalLine;': '\u2500',
1922
+ 'Hscr;': '\u210b',
1923
+ 'hscr;': '\U0001d4bd',
1924
+ 'hslash;': '\u210f',
1925
+ 'Hstrok;': '\u0126',
1926
+ 'hstrok;': '\u0127',
1927
+ 'HumpDownHump;': '\u224e',
1928
+ 'HumpEqual;': '\u224f',
1929
+ 'hybull;': '\u2043',
1930
+ 'hyphen;': '\u2010',
1931
+ 'Iacute;': '\xcd',
1932
+ 'Iacute': '\xcd',
1933
+ 'iacute;': '\xed',
1934
+ 'iacute': '\xed',
1935
+ 'ic;': '\u2063',
1936
+ 'Icirc;': '\xce',
1937
+ 'Icirc': '\xce',
1938
+ 'icirc;': '\xee',
1939
+ 'icirc': '\xee',
1940
+ 'Icy;': '\u0418',
1941
+ 'icy;': '\u0438',
1942
+ 'Idot;': '\u0130',
1943
+ 'IEcy;': '\u0415',
1944
+ 'iecy;': '\u0435',
1945
+ 'iexcl;': '\xa1',
1946
+ 'iexcl': '\xa1',
1947
+ 'iff;': '\u21d4',
1948
+ 'Ifr;': '\u2111',
1949
+ 'ifr;': '\U0001d526',
1950
+ 'Igrave;': '\xcc',
1951
+ 'Igrave': '\xcc',
1952
+ 'igrave;': '\xec',
1953
+ 'igrave': '\xec',
1954
+ 'ii;': '\u2148',
1955
+ 'iiiint;': '\u2a0c',
1956
+ 'iiint;': '\u222d',
1957
+ 'iinfin;': '\u29dc',
1958
+ 'iiota;': '\u2129',
1959
+ 'IJlig;': '\u0132',
1960
+ 'ijlig;': '\u0133',
1961
+ 'Im;': '\u2111',
1962
+ 'Imacr;': '\u012a',
1963
+ 'imacr;': '\u012b',
1964
+ 'image;': '\u2111',
1965
+ 'ImaginaryI;': '\u2148',
1966
+ 'imagline;': '\u2110',
1967
+ 'imagpart;': '\u2111',
1968
+ 'imath;': '\u0131',
1969
+ 'imof;': '\u22b7',
1970
+ 'imped;': '\u01b5',
1971
+ 'Implies;': '\u21d2',
1972
+ 'in;': '\u2208',
1973
+ 'incare;': '\u2105',
1974
+ 'infin;': '\u221e',
1975
+ 'infintie;': '\u29dd',
1976
+ 'inodot;': '\u0131',
1977
+ 'Int;': '\u222c',
1978
+ 'int;': '\u222b',
1979
+ 'intcal;': '\u22ba',
1980
+ 'integers;': '\u2124',
1981
+ 'Integral;': '\u222b',
1982
+ 'intercal;': '\u22ba',
1983
+ 'Intersection;': '\u22c2',
1984
+ 'intlarhk;': '\u2a17',
1985
+ 'intprod;': '\u2a3c',
1986
+ 'InvisibleComma;': '\u2063',
1987
+ 'InvisibleTimes;': '\u2062',
1988
+ 'IOcy;': '\u0401',
1989
+ 'iocy;': '\u0451',
1990
+ 'Iogon;': '\u012e',
1991
+ 'iogon;': '\u012f',
1992
+ 'Iopf;': '\U0001d540',
1993
+ 'iopf;': '\U0001d55a',
1994
+ 'Iota;': '\u0399',
1995
+ 'iota;': '\u03b9',
1996
+ 'iprod;': '\u2a3c',
1997
+ 'iquest;': '\xbf',
1998
+ 'iquest': '\xbf',
1999
+ 'Iscr;': '\u2110',
2000
+ 'iscr;': '\U0001d4be',
2001
+ 'isin;': '\u2208',
2002
+ 'isindot;': '\u22f5',
2003
+ 'isinE;': '\u22f9',
2004
+ 'isins;': '\u22f4',
2005
+ 'isinsv;': '\u22f3',
2006
+ 'isinv;': '\u2208',
2007
+ 'it;': '\u2062',
2008
+ 'Itilde;': '\u0128',
2009
+ 'itilde;': '\u0129',
2010
+ 'Iukcy;': '\u0406',
2011
+ 'iukcy;': '\u0456',
2012
+ 'Iuml;': '\xcf',
2013
+ 'Iuml': '\xcf',
2014
+ 'iuml;': '\xef',
2015
+ 'iuml': '\xef',
2016
+ 'Jcirc;': '\u0134',
2017
+ 'jcirc;': '\u0135',
2018
+ 'Jcy;': '\u0419',
2019
+ 'jcy;': '\u0439',
2020
+ 'Jfr;': '\U0001d50d',
2021
+ 'jfr;': '\U0001d527',
2022
+ 'jmath;': '\u0237',
2023
+ 'Jopf;': '\U0001d541',
2024
+ 'jopf;': '\U0001d55b',
2025
+ 'Jscr;': '\U0001d4a5',
2026
+ 'jscr;': '\U0001d4bf',
2027
+ 'Jsercy;': '\u0408',
2028
+ 'jsercy;': '\u0458',
2029
+ 'Jukcy;': '\u0404',
2030
+ 'jukcy;': '\u0454',
2031
+ 'Kappa;': '\u039a',
2032
+ 'kappa;': '\u03ba',
2033
+ 'kappav;': '\u03f0',
2034
+ 'Kcedil;': '\u0136',
2035
+ 'kcedil;': '\u0137',
2036
+ 'Kcy;': '\u041a',
2037
+ 'kcy;': '\u043a',
2038
+ 'Kfr;': '\U0001d50e',
2039
+ 'kfr;': '\U0001d528',
2040
+ 'kgreen;': '\u0138',
2041
+ 'KHcy;': '\u0425',
2042
+ 'khcy;': '\u0445',
2043
+ 'KJcy;': '\u040c',
2044
+ 'kjcy;': '\u045c',
2045
+ 'Kopf;': '\U0001d542',
2046
+ 'kopf;': '\U0001d55c',
2047
+ 'Kscr;': '\U0001d4a6',
2048
+ 'kscr;': '\U0001d4c0',
2049
+ 'lAarr;': '\u21da',
2050
+ 'Lacute;': '\u0139',
2051
+ 'lacute;': '\u013a',
2052
+ 'laemptyv;': '\u29b4',
2053
+ 'lagran;': '\u2112',
2054
+ 'Lambda;': '\u039b',
2055
+ 'lambda;': '\u03bb',
2056
+ 'Lang;': '\u27ea',
2057
+ 'lang;': '\u27e8',
2058
+ 'langd;': '\u2991',
2059
+ 'langle;': '\u27e8',
2060
+ 'lap;': '\u2a85',
2061
+ 'Laplacetrf;': '\u2112',
2062
+ 'laquo;': '\xab',
2063
+ 'laquo': '\xab',
2064
+ 'Larr;': '\u219e',
2065
+ 'lArr;': '\u21d0',
2066
+ 'larr;': '\u2190',
2067
+ 'larrb;': '\u21e4',
2068
+ 'larrbfs;': '\u291f',
2069
+ 'larrfs;': '\u291d',
2070
+ 'larrhk;': '\u21a9',
2071
+ 'larrlp;': '\u21ab',
2072
+ 'larrpl;': '\u2939',
2073
+ 'larrsim;': '\u2973',
2074
+ 'larrtl;': '\u21a2',
2075
+ 'lat;': '\u2aab',
2076
+ 'lAtail;': '\u291b',
2077
+ 'latail;': '\u2919',
2078
+ 'late;': '\u2aad',
2079
+ 'lates;': '\u2aad\ufe00',
2080
+ 'lBarr;': '\u290e',
2081
+ 'lbarr;': '\u290c',
2082
+ 'lbbrk;': '\u2772',
2083
+ 'lbrace;': '\x7b',
2084
+ 'lbrack;': '\x5b',
2085
+ 'lbrke;': '\u298b',
2086
+ 'lbrksld;': '\u298f',
2087
+ 'lbrkslu;': '\u298d',
2088
+ 'Lcaron;': '\u013d',
2089
+ 'lcaron;': '\u013e',
2090
+ 'Lcedil;': '\u013b',
2091
+ 'lcedil;': '\u013c',
2092
+ 'lceil;': '\u2308',
2093
+ 'lcub;': '\x7b',
2094
+ 'Lcy;': '\u041b',
2095
+ 'lcy;': '\u043b',
2096
+ 'ldca;': '\u2936',
2097
+ 'ldquo;': '\u201c',
2098
+ 'ldquor;': '\u201e',
2099
+ 'ldrdhar;': '\u2967',
2100
+ 'ldrushar;': '\u294b',
2101
+ 'ldsh;': '\u21b2',
2102
+ 'lE;': '\u2266',
2103
+ 'le;': '\u2264',
2104
+ 'LeftAngleBracket;': '\u27e8',
2105
+ 'LeftArrow;': '\u2190',
2106
+ 'Leftarrow;': '\u21d0',
2107
+ 'leftarrow;': '\u2190',
2108
+ 'LeftArrowBar;': '\u21e4',
2109
+ 'LeftArrowRightArrow;': '\u21c6',
2110
+ 'leftarrowtail;': '\u21a2',
2111
+ 'LeftCeiling;': '\u2308',
2112
+ 'LeftDoubleBracket;': '\u27e6',
2113
+ 'LeftDownTeeVector;': '\u2961',
2114
+ 'LeftDownVector;': '\u21c3',
2115
+ 'LeftDownVectorBar;': '\u2959',
2116
+ 'LeftFloor;': '\u230a',
2117
+ 'leftharpoondown;': '\u21bd',
2118
+ 'leftharpoonup;': '\u21bc',
2119
+ 'leftleftarrows;': '\u21c7',
2120
+ 'LeftRightArrow;': '\u2194',
2121
+ 'Leftrightarrow;': '\u21d4',
2122
+ 'leftrightarrow;': '\u2194',
2123
+ 'leftrightarrows;': '\u21c6',
2124
+ 'leftrightharpoons;': '\u21cb',
2125
+ 'leftrightsquigarrow;': '\u21ad',
2126
+ 'LeftRightVector;': '\u294e',
2127
+ 'LeftTee;': '\u22a3',
2128
+ 'LeftTeeArrow;': '\u21a4',
2129
+ 'LeftTeeVector;': '\u295a',
2130
+ 'leftthreetimes;': '\u22cb',
2131
+ 'LeftTriangle;': '\u22b2',
2132
+ 'LeftTriangleBar;': '\u29cf',
2133
+ 'LeftTriangleEqual;': '\u22b4',
2134
+ 'LeftUpDownVector;': '\u2951',
2135
+ 'LeftUpTeeVector;': '\u2960',
2136
+ 'LeftUpVector;': '\u21bf',
2137
+ 'LeftUpVectorBar;': '\u2958',
2138
+ 'LeftVector;': '\u21bc',
2139
+ 'LeftVectorBar;': '\u2952',
2140
+ 'lEg;': '\u2a8b',
2141
+ 'leg;': '\u22da',
2142
+ 'leq;': '\u2264',
2143
+ 'leqq;': '\u2266',
2144
+ 'leqslant;': '\u2a7d',
2145
+ 'les;': '\u2a7d',
2146
+ 'lescc;': '\u2aa8',
2147
+ 'lesdot;': '\u2a7f',
2148
+ 'lesdoto;': '\u2a81',
2149
+ 'lesdotor;': '\u2a83',
2150
+ 'lesg;': '\u22da\ufe00',
2151
+ 'lesges;': '\u2a93',
2152
+ 'lessapprox;': '\u2a85',
2153
+ 'lessdot;': '\u22d6',
2154
+ 'lesseqgtr;': '\u22da',
2155
+ 'lesseqqgtr;': '\u2a8b',
2156
+ 'LessEqualGreater;': '\u22da',
2157
+ 'LessFullEqual;': '\u2266',
2158
+ 'LessGreater;': '\u2276',
2159
+ 'lessgtr;': '\u2276',
2160
+ 'LessLess;': '\u2aa1',
2161
+ 'lesssim;': '\u2272',
2162
+ 'LessSlantEqual;': '\u2a7d',
2163
+ 'LessTilde;': '\u2272',
2164
+ 'lfisht;': '\u297c',
2165
+ 'lfloor;': '\u230a',
2166
+ 'Lfr;': '\U0001d50f',
2167
+ 'lfr;': '\U0001d529',
2168
+ 'lg;': '\u2276',
2169
+ 'lgE;': '\u2a91',
2170
+ 'lHar;': '\u2962',
2171
+ 'lhard;': '\u21bd',
2172
+ 'lharu;': '\u21bc',
2173
+ 'lharul;': '\u296a',
2174
+ 'lhblk;': '\u2584',
2175
+ 'LJcy;': '\u0409',
2176
+ 'ljcy;': '\u0459',
2177
+ 'Ll;': '\u22d8',
2178
+ 'll;': '\u226a',
2179
+ 'llarr;': '\u21c7',
2180
+ 'llcorner;': '\u231e',
2181
+ 'Lleftarrow;': '\u21da',
2182
+ 'llhard;': '\u296b',
2183
+ 'lltri;': '\u25fa',
2184
+ 'Lmidot;': '\u013f',
2185
+ 'lmidot;': '\u0140',
2186
+ 'lmoust;': '\u23b0',
2187
+ 'lmoustache;': '\u23b0',
2188
+ 'lnap;': '\u2a89',
2189
+ 'lnapprox;': '\u2a89',
2190
+ 'lnE;': '\u2268',
2191
+ 'lne;': '\u2a87',
2192
+ 'lneq;': '\u2a87',
2193
+ 'lneqq;': '\u2268',
2194
+ 'lnsim;': '\u22e6',
2195
+ 'loang;': '\u27ec',
2196
+ 'loarr;': '\u21fd',
2197
+ 'lobrk;': '\u27e6',
2198
+ 'LongLeftArrow;': '\u27f5',
2199
+ 'Longleftarrow;': '\u27f8',
2200
+ 'longleftarrow;': '\u27f5',
2201
+ 'LongLeftRightArrow;': '\u27f7',
2202
+ 'Longleftrightarrow;': '\u27fa',
2203
+ 'longleftrightarrow;': '\u27f7',
2204
+ 'longmapsto;': '\u27fc',
2205
+ 'LongRightArrow;': '\u27f6',
2206
+ 'Longrightarrow;': '\u27f9',
2207
+ 'longrightarrow;': '\u27f6',
2208
+ 'looparrowleft;': '\u21ab',
2209
+ 'looparrowright;': '\u21ac',
2210
+ 'lopar;': '\u2985',
2211
+ 'Lopf;': '\U0001d543',
2212
+ 'lopf;': '\U0001d55d',
2213
+ 'loplus;': '\u2a2d',
2214
+ 'lotimes;': '\u2a34',
2215
+ 'lowast;': '\u2217',
2216
+ 'lowbar;': '\x5f',
2217
+ 'LowerLeftArrow;': '\u2199',
2218
+ 'LowerRightArrow;': '\u2198',
2219
+ 'loz;': '\u25ca',
2220
+ 'lozenge;': '\u25ca',
2221
+ 'lozf;': '\u29eb',
2222
+ 'lpar;': '\x28',
2223
+ 'lparlt;': '\u2993',
2224
+ 'lrarr;': '\u21c6',
2225
+ 'lrcorner;': '\u231f',
2226
+ 'lrhar;': '\u21cb',
2227
+ 'lrhard;': '\u296d',
2228
+ 'lrm;': '\u200e',
2229
+ 'lrtri;': '\u22bf',
2230
+ 'lsaquo;': '\u2039',
2231
+ 'Lscr;': '\u2112',
2232
+ 'lscr;': '\U0001d4c1',
2233
+ 'Lsh;': '\u21b0',
2234
+ 'lsh;': '\u21b0',
2235
+ 'lsim;': '\u2272',
2236
+ 'lsime;': '\u2a8d',
2237
+ 'lsimg;': '\u2a8f',
2238
+ 'lsqb;': '\x5b',
2239
+ 'lsquo;': '\u2018',
2240
+ 'lsquor;': '\u201a',
2241
+ 'Lstrok;': '\u0141',
2242
+ 'lstrok;': '\u0142',
2243
+ 'LT;': '\x3c',
2244
+ 'LT': '\x3c',
2245
+ 'Lt;': '\u226a',
2246
+ 'lt;': '\x3c',
2247
+ 'lt': '\x3c',
2248
+ 'ltcc;': '\u2aa6',
2249
+ 'ltcir;': '\u2a79',
2250
+ 'ltdot;': '\u22d6',
2251
+ 'lthree;': '\u22cb',
2252
+ 'ltimes;': '\u22c9',
2253
+ 'ltlarr;': '\u2976',
2254
+ 'ltquest;': '\u2a7b',
2255
+ 'ltri;': '\u25c3',
2256
+ 'ltrie;': '\u22b4',
2257
+ 'ltrif;': '\u25c2',
2258
+ 'ltrPar;': '\u2996',
2259
+ 'lurdshar;': '\u294a',
2260
+ 'luruhar;': '\u2966',
2261
+ 'lvertneqq;': '\u2268\ufe00',
2262
+ 'lvnE;': '\u2268\ufe00',
2263
+ 'macr;': '\xaf',
2264
+ 'macr': '\xaf',
2265
+ 'male;': '\u2642',
2266
+ 'malt;': '\u2720',
2267
+ 'maltese;': '\u2720',
2268
+ 'Map;': '\u2905',
2269
+ 'map;': '\u21a6',
2270
+ 'mapsto;': '\u21a6',
2271
+ 'mapstodown;': '\u21a7',
2272
+ 'mapstoleft;': '\u21a4',
2273
+ 'mapstoup;': '\u21a5',
2274
+ 'marker;': '\u25ae',
2275
+ 'mcomma;': '\u2a29',
2276
+ 'Mcy;': '\u041c',
2277
+ 'mcy;': '\u043c',
2278
+ 'mdash;': '\u2014',
2279
+ 'mDDot;': '\u223a',
2280
+ 'measuredangle;': '\u2221',
2281
+ 'MediumSpace;': '\u205f',
2282
+ 'Mellintrf;': '\u2133',
2283
+ 'Mfr;': '\U0001d510',
2284
+ 'mfr;': '\U0001d52a',
2285
+ 'mho;': '\u2127',
2286
+ 'micro;': '\xb5',
2287
+ 'micro': '\xb5',
2288
+ 'mid;': '\u2223',
2289
+ 'midast;': '\x2a',
2290
+ 'midcir;': '\u2af0',
2291
+ 'middot;': '\xb7',
2292
+ 'middot': '\xb7',
2293
+ 'minus;': '\u2212',
2294
+ 'minusb;': '\u229f',
2295
+ 'minusd;': '\u2238',
2296
+ 'minusdu;': '\u2a2a',
2297
+ 'MinusPlus;': '\u2213',
2298
+ 'mlcp;': '\u2adb',
2299
+ 'mldr;': '\u2026',
2300
+ 'mnplus;': '\u2213',
2301
+ 'models;': '\u22a7',
2302
+ 'Mopf;': '\U0001d544',
2303
+ 'mopf;': '\U0001d55e',
2304
+ 'mp;': '\u2213',
2305
+ 'Mscr;': '\u2133',
2306
+ 'mscr;': '\U0001d4c2',
2307
+ 'mstpos;': '\u223e',
2308
+ 'Mu;': '\u039c',
2309
+ 'mu;': '\u03bc',
2310
+ 'multimap;': '\u22b8',
2311
+ 'mumap;': '\u22b8',
2312
+ 'nabla;': '\u2207',
2313
+ 'Nacute;': '\u0143',
2314
+ 'nacute;': '\u0144',
2315
+ 'nang;': '\u2220\u20d2',
2316
+ 'nap;': '\u2249',
2317
+ 'napE;': '\u2a70\u0338',
2318
+ 'napid;': '\u224b\u0338',
2319
+ 'napos;': '\u0149',
2320
+ 'napprox;': '\u2249',
2321
+ 'natur;': '\u266e',
2322
+ 'natural;': '\u266e',
2323
+ 'naturals;': '\u2115',
2324
+ 'nbsp;': '\xa0',
2325
+ 'nbsp': '\xa0',
2326
+ 'nbump;': '\u224e\u0338',
2327
+ 'nbumpe;': '\u224f\u0338',
2328
+ 'ncap;': '\u2a43',
2329
+ 'Ncaron;': '\u0147',
2330
+ 'ncaron;': '\u0148',
2331
+ 'Ncedil;': '\u0145',
2332
+ 'ncedil;': '\u0146',
2333
+ 'ncong;': '\u2247',
2334
+ 'ncongdot;': '\u2a6d\u0338',
2335
+ 'ncup;': '\u2a42',
2336
+ 'Ncy;': '\u041d',
2337
+ 'ncy;': '\u043d',
2338
+ 'ndash;': '\u2013',
2339
+ 'ne;': '\u2260',
2340
+ 'nearhk;': '\u2924',
2341
+ 'neArr;': '\u21d7',
2342
+ 'nearr;': '\u2197',
2343
+ 'nearrow;': '\u2197',
2344
+ 'nedot;': '\u2250\u0338',
2345
+ 'NegativeMediumSpace;': '\u200b',
2346
+ 'NegativeThickSpace;': '\u200b',
2347
+ 'NegativeThinSpace;': '\u200b',
2348
+ 'NegativeVeryThinSpace;': '\u200b',
2349
+ 'nequiv;': '\u2262',
2350
+ 'nesear;': '\u2928',
2351
+ 'nesim;': '\u2242\u0338',
2352
+ 'NestedGreaterGreater;': '\u226b',
2353
+ 'NestedLessLess;': '\u226a',
2354
+ 'NewLine;': '\x0a',
2355
+ 'nexist;': '\u2204',
2356
+ 'nexists;': '\u2204',
2357
+ 'Nfr;': '\U0001d511',
2358
+ 'nfr;': '\U0001d52b',
2359
+ 'ngE;': '\u2267\u0338',
2360
+ 'nge;': '\u2271',
2361
+ 'ngeq;': '\u2271',
2362
+ 'ngeqq;': '\u2267\u0338',
2363
+ 'ngeqslant;': '\u2a7e\u0338',
2364
+ 'nges;': '\u2a7e\u0338',
2365
+ 'nGg;': '\u22d9\u0338',
2366
+ 'ngsim;': '\u2275',
2367
+ 'nGt;': '\u226b\u20d2',
2368
+ 'ngt;': '\u226f',
2369
+ 'ngtr;': '\u226f',
2370
+ 'nGtv;': '\u226b\u0338',
2371
+ 'nhArr;': '\u21ce',
2372
+ 'nharr;': '\u21ae',
2373
+ 'nhpar;': '\u2af2',
2374
+ 'ni;': '\u220b',
2375
+ 'nis;': '\u22fc',
2376
+ 'nisd;': '\u22fa',
2377
+ 'niv;': '\u220b',
2378
+ 'NJcy;': '\u040a',
2379
+ 'njcy;': '\u045a',
2380
+ 'nlArr;': '\u21cd',
2381
+ 'nlarr;': '\u219a',
2382
+ 'nldr;': '\u2025',
2383
+ 'nlE;': '\u2266\u0338',
2384
+ 'nle;': '\u2270',
2385
+ 'nLeftarrow;': '\u21cd',
2386
+ 'nleftarrow;': '\u219a',
2387
+ 'nLeftrightarrow;': '\u21ce',
2388
+ 'nleftrightarrow;': '\u21ae',
2389
+ 'nleq;': '\u2270',
2390
+ 'nleqq;': '\u2266\u0338',
2391
+ 'nleqslant;': '\u2a7d\u0338',
2392
+ 'nles;': '\u2a7d\u0338',
2393
+ 'nless;': '\u226e',
2394
+ 'nLl;': '\u22d8\u0338',
2395
+ 'nlsim;': '\u2274',
2396
+ 'nLt;': '\u226a\u20d2',
2397
+ 'nlt;': '\u226e',
2398
+ 'nltri;': '\u22ea',
2399
+ 'nltrie;': '\u22ec',
2400
+ 'nLtv;': '\u226a\u0338',
2401
+ 'nmid;': '\u2224',
2402
+ 'NoBreak;': '\u2060',
2403
+ 'NonBreakingSpace;': '\xa0',
2404
+ 'Nopf;': '\u2115',
2405
+ 'nopf;': '\U0001d55f',
2406
+ 'Not;': '\u2aec',
2407
+ 'not;': '\xac',
2408
+ 'not': '\xac',
2409
+ 'NotCongruent;': '\u2262',
2410
+ 'NotCupCap;': '\u226d',
2411
+ 'NotDoubleVerticalBar;': '\u2226',
2412
+ 'NotElement;': '\u2209',
2413
+ 'NotEqual;': '\u2260',
2414
+ 'NotEqualTilde;': '\u2242\u0338',
2415
+ 'NotExists;': '\u2204',
2416
+ 'NotGreater;': '\u226f',
2417
+ 'NotGreaterEqual;': '\u2271',
2418
+ 'NotGreaterFullEqual;': '\u2267\u0338',
2419
+ 'NotGreaterGreater;': '\u226b\u0338',
2420
+ 'NotGreaterLess;': '\u2279',
2421
+ 'NotGreaterSlantEqual;': '\u2a7e\u0338',
2422
+ 'NotGreaterTilde;': '\u2275',
2423
+ 'NotHumpDownHump;': '\u224e\u0338',
2424
+ 'NotHumpEqual;': '\u224f\u0338',
2425
+ 'notin;': '\u2209',
2426
+ 'notindot;': '\u22f5\u0338',
2427
+ 'notinE;': '\u22f9\u0338',
2428
+ 'notinva;': '\u2209',
2429
+ 'notinvb;': '\u22f7',
2430
+ 'notinvc;': '\u22f6',
2431
+ 'NotLeftTriangle;': '\u22ea',
2432
+ 'NotLeftTriangleBar;': '\u29cf\u0338',
2433
+ 'NotLeftTriangleEqual;': '\u22ec',
2434
+ 'NotLess;': '\u226e',
2435
+ 'NotLessEqual;': '\u2270',
2436
+ 'NotLessGreater;': '\u2278',
2437
+ 'NotLessLess;': '\u226a\u0338',
2438
+ 'NotLessSlantEqual;': '\u2a7d\u0338',
2439
+ 'NotLessTilde;': '\u2274',
2440
+ 'NotNestedGreaterGreater;': '\u2aa2\u0338',
2441
+ 'NotNestedLessLess;': '\u2aa1\u0338',
2442
+ 'notni;': '\u220c',
2443
+ 'notniva;': '\u220c',
2444
+ 'notnivb;': '\u22fe',
2445
+ 'notnivc;': '\u22fd',
2446
+ 'NotPrecedes;': '\u2280',
2447
+ 'NotPrecedesEqual;': '\u2aaf\u0338',
2448
+ 'NotPrecedesSlantEqual;': '\u22e0',
2449
+ 'NotReverseElement;': '\u220c',
2450
+ 'NotRightTriangle;': '\u22eb',
2451
+ 'NotRightTriangleBar;': '\u29d0\u0338',
2452
+ 'NotRightTriangleEqual;': '\u22ed',
2453
+ 'NotSquareSubset;': '\u228f\u0338',
2454
+ 'NotSquareSubsetEqual;': '\u22e2',
2455
+ 'NotSquareSuperset;': '\u2290\u0338',
2456
+ 'NotSquareSupersetEqual;': '\u22e3',
2457
+ 'NotSubset;': '\u2282\u20d2',
2458
+ 'NotSubsetEqual;': '\u2288',
2459
+ 'NotSucceeds;': '\u2281',
2460
+ 'NotSucceedsEqual;': '\u2ab0\u0338',
2461
+ 'NotSucceedsSlantEqual;': '\u22e1',
2462
+ 'NotSucceedsTilde;': '\u227f\u0338',
2463
+ 'NotSuperset;': '\u2283\u20d2',
2464
+ 'NotSupersetEqual;': '\u2289',
2465
+ 'NotTilde;': '\u2241',
2466
+ 'NotTildeEqual;': '\u2244',
2467
+ 'NotTildeFullEqual;': '\u2247',
2468
+ 'NotTildeTilde;': '\u2249',
2469
+ 'NotVerticalBar;': '\u2224',
2470
+ 'npar;': '\u2226',
2471
+ 'nparallel;': '\u2226',
2472
+ 'nparsl;': '\u2afd\u20e5',
2473
+ 'npart;': '\u2202\u0338',
2474
+ 'npolint;': '\u2a14',
2475
+ 'npr;': '\u2280',
2476
+ 'nprcue;': '\u22e0',
2477
+ 'npre;': '\u2aaf\u0338',
2478
+ 'nprec;': '\u2280',
2479
+ 'npreceq;': '\u2aaf\u0338',
2480
+ 'nrArr;': '\u21cf',
2481
+ 'nrarr;': '\u219b',
2482
+ 'nrarrc;': '\u2933\u0338',
2483
+ 'nrarrw;': '\u219d\u0338',
2484
+ 'nRightarrow;': '\u21cf',
2485
+ 'nrightarrow;': '\u219b',
2486
+ 'nrtri;': '\u22eb',
2487
+ 'nrtrie;': '\u22ed',
2488
+ 'nsc;': '\u2281',
2489
+ 'nsccue;': '\u22e1',
2490
+ 'nsce;': '\u2ab0\u0338',
2491
+ 'Nscr;': '\U0001d4a9',
2492
+ 'nscr;': '\U0001d4c3',
2493
+ 'nshortmid;': '\u2224',
2494
+ 'nshortparallel;': '\u2226',
2495
+ 'nsim;': '\u2241',
2496
+ 'nsime;': '\u2244',
2497
+ 'nsimeq;': '\u2244',
2498
+ 'nsmid;': '\u2224',
2499
+ 'nspar;': '\u2226',
2500
+ 'nsqsube;': '\u22e2',
2501
+ 'nsqsupe;': '\u22e3',
2502
+ 'nsub;': '\u2284',
2503
+ 'nsubE;': '\u2ac5\u0338',
2504
+ 'nsube;': '\u2288',
2505
+ 'nsubset;': '\u2282\u20d2',
2506
+ 'nsubseteq;': '\u2288',
2507
+ 'nsubseteqq;': '\u2ac5\u0338',
2508
+ 'nsucc;': '\u2281',
2509
+ 'nsucceq;': '\u2ab0\u0338',
2510
+ 'nsup;': '\u2285',
2511
+ 'nsupE;': '\u2ac6\u0338',
2512
+ 'nsupe;': '\u2289',
2513
+ 'nsupset;': '\u2283\u20d2',
2514
+ 'nsupseteq;': '\u2289',
2515
+ 'nsupseteqq;': '\u2ac6\u0338',
2516
+ 'ntgl;': '\u2279',
2517
+ 'Ntilde;': '\xd1',
2518
+ 'Ntilde': '\xd1',
2519
+ 'ntilde;': '\xf1',
2520
+ 'ntilde': '\xf1',
2521
+ 'ntlg;': '\u2278',
2522
+ 'ntriangleleft;': '\u22ea',
2523
+ 'ntrianglelefteq;': '\u22ec',
2524
+ 'ntriangleright;': '\u22eb',
2525
+ 'ntrianglerighteq;': '\u22ed',
2526
+ 'Nu;': '\u039d',
2527
+ 'nu;': '\u03bd',
2528
+ 'num;': '\x23',
2529
+ 'numero;': '\u2116',
2530
+ 'numsp;': '\u2007',
2531
+ 'nvap;': '\u224d\u20d2',
2532
+ 'nVDash;': '\u22af',
2533
+ 'nVdash;': '\u22ae',
2534
+ 'nvDash;': '\u22ad',
2535
+ 'nvdash;': '\u22ac',
2536
+ 'nvge;': '\u2265\u20d2',
2537
+ 'nvgt;': '\x3e\u20d2',
2538
+ 'nvHarr;': '\u2904',
2539
+ 'nvinfin;': '\u29de',
2540
+ 'nvlArr;': '\u2902',
2541
+ 'nvle;': '\u2264\u20d2',
2542
+ 'nvlt;': '\x3c\u20d2',
2543
+ 'nvltrie;': '\u22b4\u20d2',
2544
+ 'nvrArr;': '\u2903',
2545
+ 'nvrtrie;': '\u22b5\u20d2',
2546
+ 'nvsim;': '\u223c\u20d2',
2547
+ 'nwarhk;': '\u2923',
2548
+ 'nwArr;': '\u21d6',
2549
+ 'nwarr;': '\u2196',
2550
+ 'nwarrow;': '\u2196',
2551
+ 'nwnear;': '\u2927',
2552
+ 'Oacute;': '\xd3',
2553
+ 'Oacute': '\xd3',
2554
+ 'oacute;': '\xf3',
2555
+ 'oacute': '\xf3',
2556
+ 'oast;': '\u229b',
2557
+ 'ocir;': '\u229a',
2558
+ 'Ocirc;': '\xd4',
2559
+ 'Ocirc': '\xd4',
2560
+ 'ocirc;': '\xf4',
2561
+ 'ocirc': '\xf4',
2562
+ 'Ocy;': '\u041e',
2563
+ 'ocy;': '\u043e',
2564
+ 'odash;': '\u229d',
2565
+ 'Odblac;': '\u0150',
2566
+ 'odblac;': '\u0151',
2567
+ 'odiv;': '\u2a38',
2568
+ 'odot;': '\u2299',
2569
+ 'odsold;': '\u29bc',
2570
+ 'OElig;': '\u0152',
2571
+ 'oelig;': '\u0153',
2572
+ 'ofcir;': '\u29bf',
2573
+ 'Ofr;': '\U0001d512',
2574
+ 'ofr;': '\U0001d52c',
2575
+ 'ogon;': '\u02db',
2576
+ 'Ograve;': '\xd2',
2577
+ 'Ograve': '\xd2',
2578
+ 'ograve;': '\xf2',
2579
+ 'ograve': '\xf2',
2580
+ 'ogt;': '\u29c1',
2581
+ 'ohbar;': '\u29b5',
2582
+ 'ohm;': '\u03a9',
2583
+ 'oint;': '\u222e',
2584
+ 'olarr;': '\u21ba',
2585
+ 'olcir;': '\u29be',
2586
+ 'olcross;': '\u29bb',
2587
+ 'oline;': '\u203e',
2588
+ 'olt;': '\u29c0',
2589
+ 'Omacr;': '\u014c',
2590
+ 'omacr;': '\u014d',
2591
+ 'Omega;': '\u03a9',
2592
+ 'omega;': '\u03c9',
2593
+ 'Omicron;': '\u039f',
2594
+ 'omicron;': '\u03bf',
2595
+ 'omid;': '\u29b6',
2596
+ 'ominus;': '\u2296',
2597
+ 'Oopf;': '\U0001d546',
2598
+ 'oopf;': '\U0001d560',
2599
+ 'opar;': '\u29b7',
2600
+ 'OpenCurlyDoubleQuote;': '\u201c',
2601
+ 'OpenCurlyQuote;': '\u2018',
2602
+ 'operp;': '\u29b9',
2603
+ 'oplus;': '\u2295',
2604
+ 'Or;': '\u2a54',
2605
+ 'or;': '\u2228',
2606
+ 'orarr;': '\u21bb',
2607
+ 'ord;': '\u2a5d',
2608
+ 'order;': '\u2134',
2609
+ 'orderof;': '\u2134',
2610
+ 'ordf;': '\xaa',
2611
+ 'ordf': '\xaa',
2612
+ 'ordm;': '\xba',
2613
+ 'ordm': '\xba',
2614
+ 'origof;': '\u22b6',
2615
+ 'oror;': '\u2a56',
2616
+ 'orslope;': '\u2a57',
2617
+ 'orv;': '\u2a5b',
2618
+ 'oS;': '\u24c8',
2619
+ 'Oscr;': '\U0001d4aa',
2620
+ 'oscr;': '\u2134',
2621
+ 'Oslash;': '\xd8',
2622
+ 'Oslash': '\xd8',
2623
+ 'oslash;': '\xf8',
2624
+ 'oslash': '\xf8',
2625
+ 'osol;': '\u2298',
2626
+ 'Otilde;': '\xd5',
2627
+ 'Otilde': '\xd5',
2628
+ 'otilde;': '\xf5',
2629
+ 'otilde': '\xf5',
2630
+ 'Otimes;': '\u2a37',
2631
+ 'otimes;': '\u2297',
2632
+ 'otimesas;': '\u2a36',
2633
+ 'Ouml;': '\xd6',
2634
+ 'Ouml': '\xd6',
2635
+ 'ouml;': '\xf6',
2636
+ 'ouml': '\xf6',
2637
+ 'ovbar;': '\u233d',
2638
+ 'OverBar;': '\u203e',
2639
+ 'OverBrace;': '\u23de',
2640
+ 'OverBracket;': '\u23b4',
2641
+ 'OverParenthesis;': '\u23dc',
2642
+ 'par;': '\u2225',
2643
+ 'para;': '\xb6',
2644
+ 'para': '\xb6',
2645
+ 'parallel;': '\u2225',
2646
+ 'parsim;': '\u2af3',
2647
+ 'parsl;': '\u2afd',
2648
+ 'part;': '\u2202',
2649
+ 'PartialD;': '\u2202',
2650
+ 'Pcy;': '\u041f',
2651
+ 'pcy;': '\u043f',
2652
+ 'percnt;': '\x25',
2653
+ 'period;': '\x2e',
2654
+ 'permil;': '\u2030',
2655
+ 'perp;': '\u22a5',
2656
+ 'pertenk;': '\u2031',
2657
+ 'Pfr;': '\U0001d513',
2658
+ 'pfr;': '\U0001d52d',
2659
+ 'Phi;': '\u03a6',
2660
+ 'phi;': '\u03c6',
2661
+ 'phiv;': '\u03d5',
2662
+ 'phmmat;': '\u2133',
2663
+ 'phone;': '\u260e',
2664
+ 'Pi;': '\u03a0',
2665
+ 'pi;': '\u03c0',
2666
+ 'pitchfork;': '\u22d4',
2667
+ 'piv;': '\u03d6',
2668
+ 'planck;': '\u210f',
2669
+ 'planckh;': '\u210e',
2670
+ 'plankv;': '\u210f',
2671
+ 'plus;': '\x2b',
2672
+ 'plusacir;': '\u2a23',
2673
+ 'plusb;': '\u229e',
2674
+ 'pluscir;': '\u2a22',
2675
+ 'plusdo;': '\u2214',
2676
+ 'plusdu;': '\u2a25',
2677
+ 'pluse;': '\u2a72',
2678
+ 'PlusMinus;': '\xb1',
2679
+ 'plusmn;': '\xb1',
2680
+ 'plusmn': '\xb1',
2681
+ 'plussim;': '\u2a26',
2682
+ 'plustwo;': '\u2a27',
2683
+ 'pm;': '\xb1',
2684
+ 'Poincareplane;': '\u210c',
2685
+ 'pointint;': '\u2a15',
2686
+ 'Popf;': '\u2119',
2687
+ 'popf;': '\U0001d561',
2688
+ 'pound;': '\xa3',
2689
+ 'pound': '\xa3',
2690
+ 'Pr;': '\u2abb',
2691
+ 'pr;': '\u227a',
2692
+ 'prap;': '\u2ab7',
2693
+ 'prcue;': '\u227c',
2694
+ 'prE;': '\u2ab3',
2695
+ 'pre;': '\u2aaf',
2696
+ 'prec;': '\u227a',
2697
+ 'precapprox;': '\u2ab7',
2698
+ 'preccurlyeq;': '\u227c',
2699
+ 'Precedes;': '\u227a',
2700
+ 'PrecedesEqual;': '\u2aaf',
2701
+ 'PrecedesSlantEqual;': '\u227c',
2702
+ 'PrecedesTilde;': '\u227e',
2703
+ 'preceq;': '\u2aaf',
2704
+ 'precnapprox;': '\u2ab9',
2705
+ 'precneqq;': '\u2ab5',
2706
+ 'precnsim;': '\u22e8',
2707
+ 'precsim;': '\u227e',
2708
+ 'Prime;': '\u2033',
2709
+ 'prime;': '\u2032',
2710
+ 'primes;': '\u2119',
2711
+ 'prnap;': '\u2ab9',
2712
+ 'prnE;': '\u2ab5',
2713
+ 'prnsim;': '\u22e8',
2714
+ 'prod;': '\u220f',
2715
+ 'Product;': '\u220f',
2716
+ 'profalar;': '\u232e',
2717
+ 'profline;': '\u2312',
2718
+ 'profsurf;': '\u2313',
2719
+ 'prop;': '\u221d',
2720
+ 'Proportion;': '\u2237',
2721
+ 'Proportional;': '\u221d',
2722
+ 'propto;': '\u221d',
2723
+ 'prsim;': '\u227e',
2724
+ 'prurel;': '\u22b0',
2725
+ 'Pscr;': '\U0001d4ab',
2726
+ 'pscr;': '\U0001d4c5',
2727
+ 'Psi;': '\u03a8',
2728
+ 'psi;': '\u03c8',
2729
+ 'puncsp;': '\u2008',
2730
+ 'Qfr;': '\U0001d514',
2731
+ 'qfr;': '\U0001d52e',
2732
+ 'qint;': '\u2a0c',
2733
+ 'Qopf;': '\u211a',
2734
+ 'qopf;': '\U0001d562',
2735
+ 'qprime;': '\u2057',
2736
+ 'Qscr;': '\U0001d4ac',
2737
+ 'qscr;': '\U0001d4c6',
2738
+ 'quaternions;': '\u210d',
2739
+ 'quatint;': '\u2a16',
2740
+ 'quest;': '\x3f',
2741
+ 'questeq;': '\u225f',
2742
+ 'QUOT;': '\x22',
2743
+ 'QUOT': '\x22',
2744
+ 'quot;': '\x22',
2745
+ 'quot': '\x22',
2746
+ 'rAarr;': '\u21db',
2747
+ 'race;': '\u223d\u0331',
2748
+ 'Racute;': '\u0154',
2749
+ 'racute;': '\u0155',
2750
+ 'radic;': '\u221a',
2751
+ 'raemptyv;': '\u29b3',
2752
+ 'Rang;': '\u27eb',
2753
+ 'rang;': '\u27e9',
2754
+ 'rangd;': '\u2992',
2755
+ 'range;': '\u29a5',
2756
+ 'rangle;': '\u27e9',
2757
+ 'raquo;': '\xbb',
2758
+ 'raquo': '\xbb',
2759
+ 'Rarr;': '\u21a0',
2760
+ 'rArr;': '\u21d2',
2761
+ 'rarr;': '\u2192',
2762
+ 'rarrap;': '\u2975',
2763
+ 'rarrb;': '\u21e5',
2764
+ 'rarrbfs;': '\u2920',
2765
+ 'rarrc;': '\u2933',
2766
+ 'rarrfs;': '\u291e',
2767
+ 'rarrhk;': '\u21aa',
2768
+ 'rarrlp;': '\u21ac',
2769
+ 'rarrpl;': '\u2945',
2770
+ 'rarrsim;': '\u2974',
2771
+ 'Rarrtl;': '\u2916',
2772
+ 'rarrtl;': '\u21a3',
2773
+ 'rarrw;': '\u219d',
2774
+ 'rAtail;': '\u291c',
2775
+ 'ratail;': '\u291a',
2776
+ 'ratio;': '\u2236',
2777
+ 'rationals;': '\u211a',
2778
+ 'RBarr;': '\u2910',
2779
+ 'rBarr;': '\u290f',
2780
+ 'rbarr;': '\u290d',
2781
+ 'rbbrk;': '\u2773',
2782
+ 'rbrace;': '\x7d',
2783
+ 'rbrack;': '\x5d',
2784
+ 'rbrke;': '\u298c',
2785
+ 'rbrksld;': '\u298e',
2786
+ 'rbrkslu;': '\u2990',
2787
+ 'Rcaron;': '\u0158',
2788
+ 'rcaron;': '\u0159',
2789
+ 'Rcedil;': '\u0156',
2790
+ 'rcedil;': '\u0157',
2791
+ 'rceil;': '\u2309',
2792
+ 'rcub;': '\x7d',
2793
+ 'Rcy;': '\u0420',
2794
+ 'rcy;': '\u0440',
2795
+ 'rdca;': '\u2937',
2796
+ 'rdldhar;': '\u2969',
2797
+ 'rdquo;': '\u201d',
2798
+ 'rdquor;': '\u201d',
2799
+ 'rdsh;': '\u21b3',
2800
+ 'Re;': '\u211c',
2801
+ 'real;': '\u211c',
2802
+ 'realine;': '\u211b',
2803
+ 'realpart;': '\u211c',
2804
+ 'reals;': '\u211d',
2805
+ 'rect;': '\u25ad',
2806
+ 'REG;': '\xae',
2807
+ 'REG': '\xae',
2808
+ 'reg;': '\xae',
2809
+ 'reg': '\xae',
2810
+ 'ReverseElement;': '\u220b',
2811
+ 'ReverseEquilibrium;': '\u21cb',
2812
+ 'ReverseUpEquilibrium;': '\u296f',
2813
+ 'rfisht;': '\u297d',
2814
+ 'rfloor;': '\u230b',
2815
+ 'Rfr;': '\u211c',
2816
+ 'rfr;': '\U0001d52f',
2817
+ 'rHar;': '\u2964',
2818
+ 'rhard;': '\u21c1',
2819
+ 'rharu;': '\u21c0',
2820
+ 'rharul;': '\u296c',
2821
+ 'Rho;': '\u03a1',
2822
+ 'rho;': '\u03c1',
2823
+ 'rhov;': '\u03f1',
2824
+ 'RightAngleBracket;': '\u27e9',
2825
+ 'RightArrow;': '\u2192',
2826
+ 'Rightarrow;': '\u21d2',
2827
+ 'rightarrow;': '\u2192',
2828
+ 'RightArrowBar;': '\u21e5',
2829
+ 'RightArrowLeftArrow;': '\u21c4',
2830
+ 'rightarrowtail;': '\u21a3',
2831
+ 'RightCeiling;': '\u2309',
2832
+ 'RightDoubleBracket;': '\u27e7',
2833
+ 'RightDownTeeVector;': '\u295d',
2834
+ 'RightDownVector;': '\u21c2',
2835
+ 'RightDownVectorBar;': '\u2955',
2836
+ 'RightFloor;': '\u230b',
2837
+ 'rightharpoondown;': '\u21c1',
2838
+ 'rightharpoonup;': '\u21c0',
2839
+ 'rightleftarrows;': '\u21c4',
2840
+ 'rightleftharpoons;': '\u21cc',
2841
+ 'rightrightarrows;': '\u21c9',
2842
+ 'rightsquigarrow;': '\u219d',
2843
+ 'RightTee;': '\u22a2',
2844
+ 'RightTeeArrow;': '\u21a6',
2845
+ 'RightTeeVector;': '\u295b',
2846
+ 'rightthreetimes;': '\u22cc',
2847
+ 'RightTriangle;': '\u22b3',
2848
+ 'RightTriangleBar;': '\u29d0',
2849
+ 'RightTriangleEqual;': '\u22b5',
2850
+ 'RightUpDownVector;': '\u294f',
2851
+ 'RightUpTeeVector;': '\u295c',
2852
+ 'RightUpVector;': '\u21be',
2853
+ 'RightUpVectorBar;': '\u2954',
2854
+ 'RightVector;': '\u21c0',
2855
+ 'RightVectorBar;': '\u2953',
2856
+ 'ring;': '\u02da',
2857
+ 'risingdotseq;': '\u2253',
2858
+ 'rlarr;': '\u21c4',
2859
+ 'rlhar;': '\u21cc',
2860
+ 'rlm;': '\u200f',
2861
+ 'rmoust;': '\u23b1',
2862
+ 'rmoustache;': '\u23b1',
2863
+ 'rnmid;': '\u2aee',
2864
+ 'roang;': '\u27ed',
2865
+ 'roarr;': '\u21fe',
2866
+ 'robrk;': '\u27e7',
2867
+ 'ropar;': '\u2986',
2868
+ 'Ropf;': '\u211d',
2869
+ 'ropf;': '\U0001d563',
2870
+ 'roplus;': '\u2a2e',
2871
+ 'rotimes;': '\u2a35',
2872
+ 'RoundImplies;': '\u2970',
2873
+ 'rpar;': '\x29',
2874
+ 'rpargt;': '\u2994',
2875
+ 'rppolint;': '\u2a12',
2876
+ 'rrarr;': '\u21c9',
2877
+ 'Rrightarrow;': '\u21db',
2878
+ 'rsaquo;': '\u203a',
2879
+ 'Rscr;': '\u211b',
2880
+ 'rscr;': '\U0001d4c7',
2881
+ 'Rsh;': '\u21b1',
2882
+ 'rsh;': '\u21b1',
2883
+ 'rsqb;': '\x5d',
2884
+ 'rsquo;': '\u2019',
2885
+ 'rsquor;': '\u2019',
2886
+ 'rthree;': '\u22cc',
2887
+ 'rtimes;': '\u22ca',
2888
+ 'rtri;': '\u25b9',
2889
+ 'rtrie;': '\u22b5',
2890
+ 'rtrif;': '\u25b8',
2891
+ 'rtriltri;': '\u29ce',
2892
+ 'RuleDelayed;': '\u29f4',
2893
+ 'ruluhar;': '\u2968',
2894
+ 'rx;': '\u211e',
2895
+ 'Sacute;': '\u015a',
2896
+ 'sacute;': '\u015b',
2897
+ 'sbquo;': '\u201a',
2898
+ 'Sc;': '\u2abc',
2899
+ 'sc;': '\u227b',
2900
+ 'scap;': '\u2ab8',
2901
+ 'Scaron;': '\u0160',
2902
+ 'scaron;': '\u0161',
2903
+ 'sccue;': '\u227d',
2904
+ 'scE;': '\u2ab4',
2905
+ 'sce;': '\u2ab0',
2906
+ 'Scedil;': '\u015e',
2907
+ 'scedil;': '\u015f',
2908
+ 'Scirc;': '\u015c',
2909
+ 'scirc;': '\u015d',
2910
+ 'scnap;': '\u2aba',
2911
+ 'scnE;': '\u2ab6',
2912
+ 'scnsim;': '\u22e9',
2913
+ 'scpolint;': '\u2a13',
2914
+ 'scsim;': '\u227f',
2915
+ 'Scy;': '\u0421',
2916
+ 'scy;': '\u0441',
2917
+ 'sdot;': '\u22c5',
2918
+ 'sdotb;': '\u22a1',
2919
+ 'sdote;': '\u2a66',
2920
+ 'searhk;': '\u2925',
2921
+ 'seArr;': '\u21d8',
2922
+ 'searr;': '\u2198',
2923
+ 'searrow;': '\u2198',
2924
+ 'sect;': '\xa7',
2925
+ 'sect': '\xa7',
2926
+ 'semi;': '\x3b',
2927
+ 'seswar;': '\u2929',
2928
+ 'setminus;': '\u2216',
2929
+ 'setmn;': '\u2216',
2930
+ 'sext;': '\u2736',
2931
+ 'Sfr;': '\U0001d516',
2932
+ 'sfr;': '\U0001d530',
2933
+ 'sfrown;': '\u2322',
2934
+ 'sharp;': '\u266f',
2935
+ 'SHCHcy;': '\u0429',
2936
+ 'shchcy;': '\u0449',
2937
+ 'SHcy;': '\u0428',
2938
+ 'shcy;': '\u0448',
2939
+ 'ShortDownArrow;': '\u2193',
2940
+ 'ShortLeftArrow;': '\u2190',
2941
+ 'shortmid;': '\u2223',
2942
+ 'shortparallel;': '\u2225',
2943
+ 'ShortRightArrow;': '\u2192',
2944
+ 'ShortUpArrow;': '\u2191',
2945
+ 'shy;': '\xad',
2946
+ 'shy': '\xad',
2947
+ 'Sigma;': '\u03a3',
2948
+ 'sigma;': '\u03c3',
2949
+ 'sigmaf;': '\u03c2',
2950
+ 'sigmav;': '\u03c2',
2951
+ 'sim;': '\u223c',
2952
+ 'simdot;': '\u2a6a',
2953
+ 'sime;': '\u2243',
2954
+ 'simeq;': '\u2243',
2955
+ 'simg;': '\u2a9e',
2956
+ 'simgE;': '\u2aa0',
2957
+ 'siml;': '\u2a9d',
2958
+ 'simlE;': '\u2a9f',
2959
+ 'simne;': '\u2246',
2960
+ 'simplus;': '\u2a24',
2961
+ 'simrarr;': '\u2972',
2962
+ 'slarr;': '\u2190',
2963
+ 'SmallCircle;': '\u2218',
2964
+ 'smallsetminus;': '\u2216',
2965
+ 'smashp;': '\u2a33',
2966
+ 'smeparsl;': '\u29e4',
2967
+ 'smid;': '\u2223',
2968
+ 'smile;': '\u2323',
2969
+ 'smt;': '\u2aaa',
2970
+ 'smte;': '\u2aac',
2971
+ 'smtes;': '\u2aac\ufe00',
2972
+ 'SOFTcy;': '\u042c',
2973
+ 'softcy;': '\u044c',
2974
+ 'sol;': '\x2f',
2975
+ 'solb;': '\u29c4',
2976
+ 'solbar;': '\u233f',
2977
+ 'Sopf;': '\U0001d54a',
2978
+ 'sopf;': '\U0001d564',
2979
+ 'spades;': '\u2660',
2980
+ 'spadesuit;': '\u2660',
2981
+ 'spar;': '\u2225',
2982
+ 'sqcap;': '\u2293',
2983
+ 'sqcaps;': '\u2293\ufe00',
2984
+ 'sqcup;': '\u2294',
2985
+ 'sqcups;': '\u2294\ufe00',
2986
+ 'Sqrt;': '\u221a',
2987
+ 'sqsub;': '\u228f',
2988
+ 'sqsube;': '\u2291',
2989
+ 'sqsubset;': '\u228f',
2990
+ 'sqsubseteq;': '\u2291',
2991
+ 'sqsup;': '\u2290',
2992
+ 'sqsupe;': '\u2292',
2993
+ 'sqsupset;': '\u2290',
2994
+ 'sqsupseteq;': '\u2292',
2995
+ 'squ;': '\u25a1',
2996
+ 'Square;': '\u25a1',
2997
+ 'square;': '\u25a1',
2998
+ 'SquareIntersection;': '\u2293',
2999
+ 'SquareSubset;': '\u228f',
3000
+ 'SquareSubsetEqual;': '\u2291',
3001
+ 'SquareSuperset;': '\u2290',
3002
+ 'SquareSupersetEqual;': '\u2292',
3003
+ 'SquareUnion;': '\u2294',
3004
+ 'squarf;': '\u25aa',
3005
+ 'squf;': '\u25aa',
3006
+ 'srarr;': '\u2192',
3007
+ 'Sscr;': '\U0001d4ae',
3008
+ 'sscr;': '\U0001d4c8',
3009
+ 'ssetmn;': '\u2216',
3010
+ 'ssmile;': '\u2323',
3011
+ 'sstarf;': '\u22c6',
3012
+ 'Star;': '\u22c6',
3013
+ 'star;': '\u2606',
3014
+ 'starf;': '\u2605',
3015
+ 'straightepsilon;': '\u03f5',
3016
+ 'straightphi;': '\u03d5',
3017
+ 'strns;': '\xaf',
3018
+ 'Sub;': '\u22d0',
3019
+ 'sub;': '\u2282',
3020
+ 'subdot;': '\u2abd',
3021
+ 'subE;': '\u2ac5',
3022
+ 'sube;': '\u2286',
3023
+ 'subedot;': '\u2ac3',
3024
+ 'submult;': '\u2ac1',
3025
+ 'subnE;': '\u2acb',
3026
+ 'subne;': '\u228a',
3027
+ 'subplus;': '\u2abf',
3028
+ 'subrarr;': '\u2979',
3029
+ 'Subset;': '\u22d0',
3030
+ 'subset;': '\u2282',
3031
+ 'subseteq;': '\u2286',
3032
+ 'subseteqq;': '\u2ac5',
3033
+ 'SubsetEqual;': '\u2286',
3034
+ 'subsetneq;': '\u228a',
3035
+ 'subsetneqq;': '\u2acb',
3036
+ 'subsim;': '\u2ac7',
3037
+ 'subsub;': '\u2ad5',
3038
+ 'subsup;': '\u2ad3',
3039
+ 'succ;': '\u227b',
3040
+ 'succapprox;': '\u2ab8',
3041
+ 'succcurlyeq;': '\u227d',
3042
+ 'Succeeds;': '\u227b',
3043
+ 'SucceedsEqual;': '\u2ab0',
3044
+ 'SucceedsSlantEqual;': '\u227d',
3045
+ 'SucceedsTilde;': '\u227f',
3046
+ 'succeq;': '\u2ab0',
3047
+ 'succnapprox;': '\u2aba',
3048
+ 'succneqq;': '\u2ab6',
3049
+ 'succnsim;': '\u22e9',
3050
+ 'succsim;': '\u227f',
3051
+ 'SuchThat;': '\u220b',
3052
+ 'Sum;': '\u2211',
3053
+ 'sum;': '\u2211',
3054
+ 'sung;': '\u266a',
3055
+ 'Sup;': '\u22d1',
3056
+ 'sup;': '\u2283',
3057
+ 'sup1;': '\xb9',
3058
+ 'sup1': '\xb9',
3059
+ 'sup2;': '\xb2',
3060
+ 'sup2': '\xb2',
3061
+ 'sup3;': '\xb3',
3062
+ 'sup3': '\xb3',
3063
+ 'supdot;': '\u2abe',
3064
+ 'supdsub;': '\u2ad8',
3065
+ 'supE;': '\u2ac6',
3066
+ 'supe;': '\u2287',
3067
+ 'supedot;': '\u2ac4',
3068
+ 'Superset;': '\u2283',
3069
+ 'SupersetEqual;': '\u2287',
3070
+ 'suphsol;': '\u27c9',
3071
+ 'suphsub;': '\u2ad7',
3072
+ 'suplarr;': '\u297b',
3073
+ 'supmult;': '\u2ac2',
3074
+ 'supnE;': '\u2acc',
3075
+ 'supne;': '\u228b',
3076
+ 'supplus;': '\u2ac0',
3077
+ 'Supset;': '\u22d1',
3078
+ 'supset;': '\u2283',
3079
+ 'supseteq;': '\u2287',
3080
+ 'supseteqq;': '\u2ac6',
3081
+ 'supsetneq;': '\u228b',
3082
+ 'supsetneqq;': '\u2acc',
3083
+ 'supsim;': '\u2ac8',
3084
+ 'supsub;': '\u2ad4',
3085
+ 'supsup;': '\u2ad6',
3086
+ 'swarhk;': '\u2926',
3087
+ 'swArr;': '\u21d9',
3088
+ 'swarr;': '\u2199',
3089
+ 'swarrow;': '\u2199',
3090
+ 'swnwar;': '\u292a',
3091
+ 'szlig;': '\xdf',
3092
+ 'szlig': '\xdf',
3093
+ 'Tab;': '\x09',
3094
+ 'target;': '\u2316',
3095
+ 'Tau;': '\u03a4',
3096
+ 'tau;': '\u03c4',
3097
+ 'tbrk;': '\u23b4',
3098
+ 'Tcaron;': '\u0164',
3099
+ 'tcaron;': '\u0165',
3100
+ 'Tcedil;': '\u0162',
3101
+ 'tcedil;': '\u0163',
3102
+ 'Tcy;': '\u0422',
3103
+ 'tcy;': '\u0442',
3104
+ 'tdot;': '\u20db',
3105
+ 'telrec;': '\u2315',
3106
+ 'Tfr;': '\U0001d517',
3107
+ 'tfr;': '\U0001d531',
3108
+ 'there4;': '\u2234',
3109
+ 'Therefore;': '\u2234',
3110
+ 'therefore;': '\u2234',
3111
+ 'Theta;': '\u0398',
3112
+ 'theta;': '\u03b8',
3113
+ 'thetasym;': '\u03d1',
3114
+ 'thetav;': '\u03d1',
3115
+ 'thickapprox;': '\u2248',
3116
+ 'thicksim;': '\u223c',
3117
+ 'ThickSpace;': '\u205f\u200a',
3118
+ 'thinsp;': '\u2009',
3119
+ 'ThinSpace;': '\u2009',
3120
+ 'thkap;': '\u2248',
3121
+ 'thksim;': '\u223c',
3122
+ 'THORN;': '\xde',
3123
+ 'THORN': '\xde',
3124
+ 'thorn;': '\xfe',
3125
+ 'thorn': '\xfe',
3126
+ 'Tilde;': '\u223c',
3127
+ 'tilde;': '\u02dc',
3128
+ 'TildeEqual;': '\u2243',
3129
+ 'TildeFullEqual;': '\u2245',
3130
+ 'TildeTilde;': '\u2248',
3131
+ 'times;': '\xd7',
3132
+ 'times': '\xd7',
3133
+ 'timesb;': '\u22a0',
3134
+ 'timesbar;': '\u2a31',
3135
+ 'timesd;': '\u2a30',
3136
+ 'tint;': '\u222d',
3137
+ 'toea;': '\u2928',
3138
+ 'top;': '\u22a4',
3139
+ 'topbot;': '\u2336',
3140
+ 'topcir;': '\u2af1',
3141
+ 'Topf;': '\U0001d54b',
3142
+ 'topf;': '\U0001d565',
3143
+ 'topfork;': '\u2ada',
3144
+ 'tosa;': '\u2929',
3145
+ 'tprime;': '\u2034',
3146
+ 'TRADE;': '\u2122',
3147
+ 'trade;': '\u2122',
3148
+ 'triangle;': '\u25b5',
3149
+ 'triangledown;': '\u25bf',
3150
+ 'triangleleft;': '\u25c3',
3151
+ 'trianglelefteq;': '\u22b4',
3152
+ 'triangleq;': '\u225c',
3153
+ 'triangleright;': '\u25b9',
3154
+ 'trianglerighteq;': '\u22b5',
3155
+ 'tridot;': '\u25ec',
3156
+ 'trie;': '\u225c',
3157
+ 'triminus;': '\u2a3a',
3158
+ 'TripleDot;': '\u20db',
3159
+ 'triplus;': '\u2a39',
3160
+ 'trisb;': '\u29cd',
3161
+ 'tritime;': '\u2a3b',
3162
+ 'trpezium;': '\u23e2',
3163
+ 'Tscr;': '\U0001d4af',
3164
+ 'tscr;': '\U0001d4c9',
3165
+ 'TScy;': '\u0426',
3166
+ 'tscy;': '\u0446',
3167
+ 'TSHcy;': '\u040b',
3168
+ 'tshcy;': '\u045b',
3169
+ 'Tstrok;': '\u0166',
3170
+ 'tstrok;': '\u0167',
3171
+ 'twixt;': '\u226c',
3172
+ 'twoheadleftarrow;': '\u219e',
3173
+ 'twoheadrightarrow;': '\u21a0',
3174
+ 'Uacute;': '\xda',
3175
+ 'Uacute': '\xda',
3176
+ 'uacute;': '\xfa',
3177
+ 'uacute': '\xfa',
3178
+ 'Uarr;': '\u219f',
3179
+ 'uArr;': '\u21d1',
3180
+ 'uarr;': '\u2191',
3181
+ 'Uarrocir;': '\u2949',
3182
+ 'Ubrcy;': '\u040e',
3183
+ 'ubrcy;': '\u045e',
3184
+ 'Ubreve;': '\u016c',
3185
+ 'ubreve;': '\u016d',
3186
+ 'Ucirc;': '\xdb',
3187
+ 'Ucirc': '\xdb',
3188
+ 'ucirc;': '\xfb',
3189
+ 'ucirc': '\xfb',
3190
+ 'Ucy;': '\u0423',
3191
+ 'ucy;': '\u0443',
3192
+ 'udarr;': '\u21c5',
3193
+ 'Udblac;': '\u0170',
3194
+ 'udblac;': '\u0171',
3195
+ 'udhar;': '\u296e',
3196
+ 'ufisht;': '\u297e',
3197
+ 'Ufr;': '\U0001d518',
3198
+ 'ufr;': '\U0001d532',
3199
+ 'Ugrave;': '\xd9',
3200
+ 'Ugrave': '\xd9',
3201
+ 'ugrave;': '\xf9',
3202
+ 'ugrave': '\xf9',
3203
+ 'uHar;': '\u2963',
3204
+ 'uharl;': '\u21bf',
3205
+ 'uharr;': '\u21be',
3206
+ 'uhblk;': '\u2580',
3207
+ 'ulcorn;': '\u231c',
3208
+ 'ulcorner;': '\u231c',
3209
+ 'ulcrop;': '\u230f',
3210
+ 'ultri;': '\u25f8',
3211
+ 'Umacr;': '\u016a',
3212
+ 'umacr;': '\u016b',
3213
+ 'uml;': '\xa8',
3214
+ 'uml': '\xa8',
3215
+ 'UnderBar;': '\x5f',
3216
+ 'UnderBrace;': '\u23df',
3217
+ 'UnderBracket;': '\u23b5',
3218
+ 'UnderParenthesis;': '\u23dd',
3219
+ 'Union;': '\u22c3',
3220
+ 'UnionPlus;': '\u228e',
3221
+ 'Uogon;': '\u0172',
3222
+ 'uogon;': '\u0173',
3223
+ 'Uopf;': '\U0001d54c',
3224
+ 'uopf;': '\U0001d566',
3225
+ 'UpArrow;': '\u2191',
3226
+ 'Uparrow;': '\u21d1',
3227
+ 'uparrow;': '\u2191',
3228
+ 'UpArrowBar;': '\u2912',
3229
+ 'UpArrowDownArrow;': '\u21c5',
3230
+ 'UpDownArrow;': '\u2195',
3231
+ 'Updownarrow;': '\u21d5',
3232
+ 'updownarrow;': '\u2195',
3233
+ 'UpEquilibrium;': '\u296e',
3234
+ 'upharpoonleft;': '\u21bf',
3235
+ 'upharpoonright;': '\u21be',
3236
+ 'uplus;': '\u228e',
3237
+ 'UpperLeftArrow;': '\u2196',
3238
+ 'UpperRightArrow;': '\u2197',
3239
+ 'Upsi;': '\u03d2',
3240
+ 'upsi;': '\u03c5',
3241
+ 'upsih;': '\u03d2',
3242
+ 'Upsilon;': '\u03a5',
3243
+ 'upsilon;': '\u03c5',
3244
+ 'UpTee;': '\u22a5',
3245
+ 'UpTeeArrow;': '\u21a5',
3246
+ 'upuparrows;': '\u21c8',
3247
+ 'urcorn;': '\u231d',
3248
+ 'urcorner;': '\u231d',
3249
+ 'urcrop;': '\u230e',
3250
+ 'Uring;': '\u016e',
3251
+ 'uring;': '\u016f',
3252
+ 'urtri;': '\u25f9',
3253
+ 'Uscr;': '\U0001d4b0',
3254
+ 'uscr;': '\U0001d4ca',
3255
+ 'utdot;': '\u22f0',
3256
+ 'Utilde;': '\u0168',
3257
+ 'utilde;': '\u0169',
3258
+ 'utri;': '\u25b5',
3259
+ 'utrif;': '\u25b4',
3260
+ 'uuarr;': '\u21c8',
3261
+ 'Uuml;': '\xdc',
3262
+ 'Uuml': '\xdc',
3263
+ 'uuml;': '\xfc',
3264
+ 'uuml': '\xfc',
3265
+ 'uwangle;': '\u29a7',
3266
+ 'vangrt;': '\u299c',
3267
+ 'varepsilon;': '\u03f5',
3268
+ 'varkappa;': '\u03f0',
3269
+ 'varnothing;': '\u2205',
3270
+ 'varphi;': '\u03d5',
3271
+ 'varpi;': '\u03d6',
3272
+ 'varpropto;': '\u221d',
3273
+ 'vArr;': '\u21d5',
3274
+ 'varr;': '\u2195',
3275
+ 'varrho;': '\u03f1',
3276
+ 'varsigma;': '\u03c2',
3277
+ 'varsubsetneq;': '\u228a\ufe00',
3278
+ 'varsubsetneqq;': '\u2acb\ufe00',
3279
+ 'varsupsetneq;': '\u228b\ufe00',
3280
+ 'varsupsetneqq;': '\u2acc\ufe00',
3281
+ 'vartheta;': '\u03d1',
3282
+ 'vartriangleleft;': '\u22b2',
3283
+ 'vartriangleright;': '\u22b3',
3284
+ 'Vbar;': '\u2aeb',
3285
+ 'vBar;': '\u2ae8',
3286
+ 'vBarv;': '\u2ae9',
3287
+ 'Vcy;': '\u0412',
3288
+ 'vcy;': '\u0432',
3289
+ 'VDash;': '\u22ab',
3290
+ 'Vdash;': '\u22a9',
3291
+ 'vDash;': '\u22a8',
3292
+ 'vdash;': '\u22a2',
3293
+ 'Vdashl;': '\u2ae6',
3294
+ 'Vee;': '\u22c1',
3295
+ 'vee;': '\u2228',
3296
+ 'veebar;': '\u22bb',
3297
+ 'veeeq;': '\u225a',
3298
+ 'vellip;': '\u22ee',
3299
+ 'Verbar;': '\u2016',
3300
+ 'verbar;': '\x7c',
3301
+ 'Vert;': '\u2016',
3302
+ 'vert;': '\x7c',
3303
+ 'VerticalBar;': '\u2223',
3304
+ 'VerticalLine;': '\x7c',
3305
+ 'VerticalSeparator;': '\u2758',
3306
+ 'VerticalTilde;': '\u2240',
3307
+ 'VeryThinSpace;': '\u200a',
3308
+ 'Vfr;': '\U0001d519',
3309
+ 'vfr;': '\U0001d533',
3310
+ 'vltri;': '\u22b2',
3311
+ 'vnsub;': '\u2282\u20d2',
3312
+ 'vnsup;': '\u2283\u20d2',
3313
+ 'Vopf;': '\U0001d54d',
3314
+ 'vopf;': '\U0001d567',
3315
+ 'vprop;': '\u221d',
3316
+ 'vrtri;': '\u22b3',
3317
+ 'Vscr;': '\U0001d4b1',
3318
+ 'vscr;': '\U0001d4cb',
3319
+ 'vsubnE;': '\u2acb\ufe00',
3320
+ 'vsubne;': '\u228a\ufe00',
3321
+ 'vsupnE;': '\u2acc\ufe00',
3322
+ 'vsupne;': '\u228b\ufe00',
3323
+ 'Vvdash;': '\u22aa',
3324
+ 'vzigzag;': '\u299a',
3325
+ 'Wcirc;': '\u0174',
3326
+ 'wcirc;': '\u0175',
3327
+ 'wedbar;': '\u2a5f',
3328
+ 'Wedge;': '\u22c0',
3329
+ 'wedge;': '\u2227',
3330
+ 'wedgeq;': '\u2259',
3331
+ 'weierp;': '\u2118',
3332
+ 'Wfr;': '\U0001d51a',
3333
+ 'wfr;': '\U0001d534',
3334
+ 'Wopf;': '\U0001d54e',
3335
+ 'wopf;': '\U0001d568',
3336
+ 'wp;': '\u2118',
3337
+ 'wr;': '\u2240',
3338
+ 'wreath;': '\u2240',
3339
+ 'Wscr;': '\U0001d4b2',
3340
+ 'wscr;': '\U0001d4cc',
3341
+ 'xcap;': '\u22c2',
3342
+ 'xcirc;': '\u25ef',
3343
+ 'xcup;': '\u22c3',
3344
+ 'xdtri;': '\u25bd',
3345
+ 'Xfr;': '\U0001d51b',
3346
+ 'xfr;': '\U0001d535',
3347
+ 'xhArr;': '\u27fa',
3348
+ 'xharr;': '\u27f7',
3349
+ 'Xi;': '\u039e',
3350
+ 'xi;': '\u03be',
3351
+ 'xlArr;': '\u27f8',
3352
+ 'xlarr;': '\u27f5',
3353
+ 'xmap;': '\u27fc',
3354
+ 'xnis;': '\u22fb',
3355
+ 'xodot;': '\u2a00',
3356
+ 'Xopf;': '\U0001d54f',
3357
+ 'xopf;': '\U0001d569',
3358
+ 'xoplus;': '\u2a01',
3359
+ 'xotime;': '\u2a02',
3360
+ 'xrArr;': '\u27f9',
3361
+ 'xrarr;': '\u27f6',
3362
+ 'Xscr;': '\U0001d4b3',
3363
+ 'xscr;': '\U0001d4cd',
3364
+ 'xsqcup;': '\u2a06',
3365
+ 'xuplus;': '\u2a04',
3366
+ 'xutri;': '\u25b3',
3367
+ 'xvee;': '\u22c1',
3368
+ 'xwedge;': '\u22c0',
3369
+ 'Yacute;': '\xdd',
3370
+ 'Yacute': '\xdd',
3371
+ 'yacute;': '\xfd',
3372
+ 'yacute': '\xfd',
3373
+ 'YAcy;': '\u042f',
3374
+ 'yacy;': '\u044f',
3375
+ 'Ycirc;': '\u0176',
3376
+ 'ycirc;': '\u0177',
3377
+ 'Ycy;': '\u042b',
3378
+ 'ycy;': '\u044b',
3379
+ 'yen;': '\xa5',
3380
+ 'yen': '\xa5',
3381
+ 'Yfr;': '\U0001d51c',
3382
+ 'yfr;': '\U0001d536',
3383
+ 'YIcy;': '\u0407',
3384
+ 'yicy;': '\u0457',
3385
+ 'Yopf;': '\U0001d550',
3386
+ 'yopf;': '\U0001d56a',
3387
+ 'Yscr;': '\U0001d4b4',
3388
+ 'yscr;': '\U0001d4ce',
3389
+ 'YUcy;': '\u042e',
3390
+ 'yucy;': '\u044e',
3391
+ 'Yuml;': '\u0178',
3392
+ 'yuml;': '\xff',
3393
+ 'yuml': '\xff',
3394
+ 'Zacute;': '\u0179',
3395
+ 'zacute;': '\u017a',
3396
+ 'Zcaron;': '\u017d',
3397
+ 'zcaron;': '\u017e',
3398
+ 'Zcy;': '\u0417',
3399
+ 'zcy;': '\u0437',
3400
+ 'Zdot;': '\u017b',
3401
+ 'zdot;': '\u017c',
3402
+ 'zeetrf;': '\u2128',
3403
+ 'ZeroWidthSpace;': '\u200b',
3404
+ 'Zeta;': '\u0396',
3405
+ 'zeta;': '\u03b6',
3406
+ 'Zfr;': '\u2128',
3407
+ 'zfr;': '\U0001d537',
3408
+ 'ZHcy;': '\u0416',
3409
+ 'zhcy;': '\u0436',
3410
+ 'zigrarr;': '\u21dd',
3411
+ 'Zopf;': '\u2124',
3412
+ 'zopf;': '\U0001d56b',
3413
+ 'Zscr;': '\U0001d4b5',
3414
+ 'zscr;': '\U0001d4cf',
3415
+ 'zwj;': '\u200d',
3416
+ 'zwnj;': '\u200c',
3417
+ }
3418
+
3419
+
3420
+ # A hint for the TypeError of the call of a callback itself, not of its body, which a function of Python raises when it
3421
+ # is called with other arguments than its parameters, where a sub of Perl takes any of @_
3422
+ def _call_hint(e: TypeError, hint: str) -> None: # pyright: ignore[reportUnusedFunction]
3423
+ if e.__traceback__ is not None and e.__traceback__.tb_next is None:
3424
+ e.args = (f'{e}: {hint}', *e.args[1:])
3425
+
3426
+
3427
+ def _entity(point: str | None, name: str | None, attr: bool) -> str:
3428
+
3429
+ # Code point
3430
+ if name is None:
3431
+ assert point is not None
3432
+ code = int(point[1:], 16) if point.startswith('x') else int(point)
3433
+ if code == 0 or code > 0x10FFFF or 0xD800 <= code <= 0xDFFF:
3434
+ return '\ufffd'
3435
+ return chr(_C1.get(code, code))
3436
+
3437
+ # Named character reference
3438
+ rest = last = ''
3439
+ while len(name):
3440
+ if name in ENTITIES and (not attr or name.endswith(';') or not re.match(r'[A-Za-z0-9=]', last)):
3441
+ return ENTITIES[name] + rest[::-1]
3442
+ last = name[-1]
3443
+ rest += last
3444
+ name = name[:-1]
3445
+ return '&' + rest[::-1]
3446
+
3447
+
3448
+ class _Guard:
3449
+ def __init__(self, cb: Callable[[], object]) -> None:
3450
+ self.cb = cb
3451
+
3452
+ def __enter__(self) -> None:
3453
+ pass
3454
+
3455
+ def __exit__(
3456
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None
3457
+ ) -> None:
3458
+ self.cb()
3459
+
3460
+
3461
+ def _header(string: str, cookie: bool) -> list[list[tuple[str, str | None]]]:
3462
+ tree: list[list[tuple[str, str | None]]] = []
3463
+ part: list[tuple[str, str | None]] = []
3464
+ pos = 0
3465
+ while m := _RE_HEADER_NAME.match(string, pos):
3466
+ name, value, pos = m[1], None, m.end()
3467
+ expires = cookie and len(part) > 0 and name.lower() == 'expires'
3468
+
3469
+ # Special "expires" value
3470
+ if expires and (v := _RE_EXPIRES.match(string, pos)):
3471
+ value, pos = v[1], v.end()
3472
+
3473
+ # Quoted value
3474
+ elif v := _RE_QUOTED_VALUE.match(string, pos):
3475
+ value, pos = unquote(v[1]), v.end()
3476
+
3477
+ # Unquoted value
3478
+ elif v := _RE_UNQUOTED_VALUE.match(string, pos):
3479
+ value, pos = v[1], v.end()
3480
+
3481
+ part.append((name, value))
3482
+
3483
+ # Separator
3484
+ if not (m := _RE_HEADER_SEPARATOR.match(string, pos)):
3485
+ continue
3486
+ pos = m.end()
3487
+ tree.append(part)
3488
+ part = []
3489
+
3490
+ # Take care of final part
3491
+ if part:
3492
+ tree.append(part)
3493
+ return tree
3494
+
3495
+
3496
+ def _html(string: str, attr: bool) -> str:
3497
+ return _RE_ENTITY.sub(lambda m: _entity(m[1], m[2], attr), string)
3498
+
3499
+
3500
+ # The name and the values of a call: none, a dict of values, a name, or a name and a dict, with the keyword arguments
3501
+ # as the pairs of names and values that Mojo takes after them; the modules of Pyjoyment use it, as Mojo::Util::_options
3502
+ def _options(args: tuple[Any, ...], values: dict[str, Any]) -> tuple[Any, dict[str, Any]]: # pyright: ignore[reportUnusedFunction]
3503
+ name: Any = None
3504
+ if len(args) == 2 or (len(args) == 1 and not isinstance(args[0], dict)):
3505
+ name, args = args[0], args[1:]
3506
+ if len(args) > 1 or (args and not isinstance(args[0], dict)):
3507
+ raise TypeError(f'Unexpected positional arguments: {args!r}')
3508
+ return name, ({**cast(dict[str, Any], args[0]), **values} if args else dict(values))
3509
+
3510
+
3511
+ def _round(number: float) -> str:
3512
+ return f'{int(number * 10 + 0.5) / 10 if number < 10 else int(number + 0.5):.15g}'
3513
+
3514
+
3515
+ # The values of a dict that an object keeps under a name, as the stash of a controller and the config and the defaults
3516
+ # of an application: the dict, a value, or the object after it merges a dict or keyword arguments
3517
+ def _stash(name: str, obj: Any, args: tuple[Any, ...], values: dict[str, Any]) -> Any: # pyright: ignore[reportUnusedFunction]
3518
+ attr = f'_{name}'
3519
+ stash: dict[Any, Any] | None = getattr(obj, attr, None)
3520
+ if stash is None:
3521
+ setattr(obj, attr, stash := {})
3522
+
3523
+ # Dict
3524
+ if not args and not values:
3525
+ return stash
3526
+
3527
+ # Get
3528
+ if len(args) == 1 and not isinstance(args[0], dict) and not values:
3529
+ return stash.get(args[0])
3530
+
3531
+ # Set
3532
+ for arg in args:
3533
+ stash.update(cast(dict[Any, Any], arg))
3534
+ stash.update(values)
3535
+
3536
+ return obj
3537
+
3538
+
3539
+ # Perl's split, which drops the empty fields at the end
3540
+ def _split(pattern: str, string: str) -> list[str]:
3541
+ fields = re.split(pattern, string)
3542
+ while fields and fields[-1] == '':
3543
+ fields.pop()
3544
+ return fields
3545
+
3546
+
3547
+ # The name that a mistake of the name of an attribute most likely meant: one that the name starts with or that starts
3548
+ # with the name, of three letters at least, as "req" for "request", or else the closest one
3549
+ def _suggest(name: str, names: Iterable[str]) -> str | None: # pyright: ignore[reportUnusedFunction]
3550
+ import difflib
3551
+
3552
+ others = [n for n in names if n != name and not n.startswith('_')]
3553
+ if prefixes := [n for n in others if len(n) > 2 and (name.startswith(n) or n.startswith(name))]:
3554
+ return difflib.get_close_matches(name, prefixes, n=1, cutoff=0)[0]
3555
+ return next(iter(difflib.get_close_matches(name, others, n=1)), None)
3556
+
3557
+
3558
+ # Perl's ucfirst
3559
+ def _ucfirst(string: str) -> str:
3560
+ return string[:1].title() + string[1:]
3561
+
3562
+
3563
+ @functools.cache
3564
+ def _url_pattern[T: (str, bytes)](pattern: T) -> re.Pattern[T]:
3565
+ if isinstance(pattern, str):
3566
+ return re.compile('[' + pattern.replace('[', '\\[') + ']')
3567
+ return re.compile(b'[' + pattern.replace(b'[', b'\\[') + b']')