ruyi 0.44.0b20251219__py3-none-any.whl → 0.45.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.
- ruyi/__main__.py +16 -4
- ruyi/cli/cmd.py +6 -5
- ruyi/cli/config_cli.py +14 -11
- ruyi/cli/main.py +14 -4
- ruyi/cli/oobe.py +7 -3
- ruyi/cli/self_cli.py +48 -34
- ruyi/cli/user_input.py +42 -12
- ruyi/cli/version_cli.py +11 -5
- ruyi/config/__init__.py +26 -2
- ruyi/config/errors.py +19 -7
- ruyi/device/provision.py +116 -55
- ruyi/device/provision_cli.py +6 -3
- ruyi/i18n/__init__.py +129 -0
- ruyi/log/__init__.py +6 -5
- ruyi/mux/runtime.py +19 -6
- ruyi/mux/venv/maker.py +93 -35
- ruyi/mux/venv/venv_cli.py +13 -10
- ruyi/pluginhost/plugin_cli.py +4 -3
- ruyi/resource_bundle/__init__.py +22 -8
- ruyi/resource_bundle/__main__.py +6 -5
- ruyi/resource_bundle/data.py +13 -9
- ruyi/ruyipkg/admin_checksum.py +4 -1
- ruyi/ruyipkg/admin_cli.py +9 -6
- ruyi/ruyipkg/augmented_pkg.py +15 -14
- ruyi/ruyipkg/checksum.py +8 -2
- ruyi/ruyipkg/distfile.py +33 -9
- ruyi/ruyipkg/entity.py +12 -2
- ruyi/ruyipkg/entity_cli.py +20 -12
- ruyi/ruyipkg/entity_provider.py +11 -2
- ruyi/ruyipkg/fetcher.py +38 -9
- ruyi/ruyipkg/install.py +143 -42
- ruyi/ruyipkg/install_cli.py +18 -15
- ruyi/ruyipkg/list.py +27 -20
- ruyi/ruyipkg/list_cli.py +12 -7
- ruyi/ruyipkg/news.py +23 -11
- ruyi/ruyipkg/news_cli.py +10 -7
- ruyi/ruyipkg/profile_cli.py +8 -2
- ruyi/ruyipkg/repo.py +22 -8
- ruyi/ruyipkg/unpack.py +42 -8
- ruyi/ruyipkg/unpack_method.py +5 -1
- ruyi/ruyipkg/update_cli.py +8 -3
- ruyi/telemetry/provider.py +74 -29
- ruyi/telemetry/telemetry_cli.py +9 -8
- ruyi/utils/git.py +18 -11
- ruyi/utils/prereqs.py +10 -5
- ruyi/utils/ssl_patch.py +2 -1
- ruyi/version.py +9 -3
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/METADATA +2 -1
- ruyi-0.45.0.dist-info/RECORD +103 -0
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/WHEEL +1 -1
- ruyi-0.44.0b20251219.dist-info/RECORD +0 -102
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/entry_points.txt +0 -0
- {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/licenses/LICENSE-Apache.txt +0 -0
ruyi/resource_bundle/__init__.py
CHANGED
|
@@ -1,20 +1,34 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import zlib
|
|
3
3
|
|
|
4
|
-
from .data import RESOURCES,
|
|
4
|
+
from .data import RESOURCES, TEMPLATE_NAME_MAP
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def _unpack_payload(x: bytes) ->
|
|
8
|
-
return zlib.decompress(base64.b64decode(x))
|
|
7
|
+
def _unpack_payload(x: bytes) -> bytes:
|
|
8
|
+
return zlib.decompress(base64.b64decode(x))
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
_CACHE: dict[str, bytes] = {}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_resource_blob(name: str) -> bytes | None:
|
|
15
|
+
if t := RESOURCES.get(name):
|
|
16
|
+
if name not in _CACHE:
|
|
17
|
+
# In our use cases, the program is short-lived and involved resources
|
|
18
|
+
# are small in size, so it is fine to just store the decompressed
|
|
19
|
+
# blobs without eviction.
|
|
20
|
+
_CACHE[name] = _unpack_payload(t)
|
|
21
|
+
return _CACHE[name]
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_resource_str(name: str) -> str | None:
|
|
26
|
+
if blob := get_resource_blob(name):
|
|
27
|
+
return blob.decode("utf-8")
|
|
14
28
|
return None
|
|
15
29
|
|
|
16
30
|
|
|
17
31
|
def get_template_str(template_name: str) -> str | None:
|
|
18
|
-
if t :=
|
|
19
|
-
return
|
|
32
|
+
if t := TEMPLATE_NAME_MAP.get(template_name):
|
|
33
|
+
return get_resource_str(t)
|
|
20
34
|
return None
|
ruyi/resource_bundle/__main__.py
CHANGED
|
@@ -20,15 +20,16 @@ def main() -> None:
|
|
|
20
20
|
|
|
21
21
|
resources: dict[str, str] = {}
|
|
22
22
|
template_names: dict[str, str] = {}
|
|
23
|
-
for f in bundled_resource_root.
|
|
23
|
+
for f in bundled_resource_root.glob("**/*"):
|
|
24
24
|
if not f.is_file():
|
|
25
25
|
continue
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
rel_path = f.relative_to(bundled_resource_root)
|
|
28
|
+
resources[str(rel_path)] = make_payload_from_file(f)
|
|
28
29
|
|
|
29
30
|
if f.suffix.lower() == ".jinja":
|
|
30
31
|
# strip the .jinja suffix for the template name
|
|
31
|
-
template_names[
|
|
32
|
+
template_names[str(rel_path.with_suffix(""))] = str(rel_path)
|
|
32
33
|
|
|
33
34
|
with open(self_path / "data.py", "w", encoding="utf-8") as fp:
|
|
34
35
|
|
|
@@ -45,9 +46,9 @@ def main() -> None:
|
|
|
45
46
|
p(f' "{filename}": b"{payload}", # fmt: skip')
|
|
46
47
|
p("}\n")
|
|
47
48
|
|
|
48
|
-
p("
|
|
49
|
+
p("TEMPLATE_NAME_MAP: Final = {")
|
|
49
50
|
for stem, full_filename in sorted(template_names.items()):
|
|
50
|
-
p(f' "{stem}":
|
|
51
|
+
p(f' "{stem}": "{full_filename}",')
|
|
51
52
|
p("}")
|
|
52
53
|
|
|
53
54
|
|
ruyi/resource_bundle/data.py
CHANGED
|
@@ -7,20 +7,24 @@ from typing import Final
|
|
|
7
7
|
RESOURCES: Final = {
|
|
8
8
|
"_ruyi_completion": b"eNqlVntP40YQ/z+fYmosCC0h5KpKd7Tmjoc5ogYSxUlPd4FaTryJV9i7kXdNLofoZ++M7TgmmOiutVDYncdvZuexOzsTGc19NoU4WfJabQc6/J6FS/BAxzyKmA++XAh4YLHiUoCcQqD1XB03mzOug2R8iPrNe67CZXLf9OIZwYVMs+Y4lONm5HHxnOqpwM13iHfoN935UgdSNEpS6MUVC+doEgLvgYHPleZiomEesyn/CtNYRqADBjLmMy68ENQk5nMNWoL3ILkPwosYTGQYcvJa0bn6iQAlI6YDLmYHECUIOQOZ6HmiEQhi5vOYTVIqT6HIgs/GyQyUjpkXIYjP5kz4qaJI+Q9emDCKinvaP3cv7LPhx0OUa0/htP/xvHvd69gD2x06tjuwr3uX7Y7tAFegmD6ARDHQLJpPecgUTNGHdu/8sObmEXFLEXEpO/gj6vvwWAP8+BRGI2h8A8N8fNVU48mAu7vfyVORatG3Fd/lQrAYQT8YhULMdBJn+lOe/gvlBIOuM9ctw6xH93SQ/UzHLfvjDC66w4FL7tycXtsonKsZP+ZIZnIifWaZ71PKxNOwRktJcfSCkvoOJinWnmq177BZGeJ1eitjSm7C25PdFrw72X0DrZOmzx6aIglDeEPUP4p9qsJCxV7X3X1XqYSxpwO84n/hdRao9qVjmXu3R61f90rUrE0swyifT9D5vjhX7l9232l3byoPuAr/qnFVwSnRrLpZJ8OGib8G3BYy9FFFuJ32DZXA2fDy0u5XSvS67ZsBipwP+073hUi5tKzWFqbrXNmdjmV8U4GxVWzY6/Vtx3Gd3ul5BeS2cgHzMQvoccN8XMjYV6PW3dPT/v5G1IRUMtZWvYI+9yaszMCUcNXwdCNkntLw2+HbjTzQt8JryHy51s/7c51dTG0pQZRa6x+oj/6+vb3bP4bdXRKIPD0JyPWUObKax3cvCyA3nDnccGBvr9Kq6zOKyZgRcBETo1wlxMn8Hn0gVrYl4HT/skGyaG1k6qh8UEKXeP//Ilc+YgdB0T4VR9nMe9VZqEj6dq/z+bvrulhvL+1iUyk2+NwrsGi9tX5TqU/d/sVZ3z7901nprSn/u4Poxf6RFjKf7/9rQ7XKfZTVsvkefrLgqLo2E4Fv6jplBY+FufKGW5YFLUSiFsjapFBtlPrArDa2qrdGUW+b9ZNe1uUHZNsFuwpECY+WOJV5SZhaoSQU20t4LYjZEFc0zw58QhP4UIYhznHjZTqxfFFBqRtBLRU+3QfIwrFkwfHBwgEHFzrIIYxQer6XaDlNxMSABSFywTVHVBwTkUECiO+hnqGCTCz0NL7eUhyARKPxgit2kAOSE/m8tvDwNmA4QxkNBFAyHYmMfCI1aAaL2QyHP4TCkYycIu0c58UhUvrKofI9WnrtTHwQXLKIQ6jQ7KumQviZfK4YlF6LcjGUPLumVoP09uTkxYF//wJg/FlP", # fmt: skip
|
|
9
9
|
"binfmt.conf.jinja": b"eNpNUrlu20AQ7f0VAwgCbECk+wQp06Zwl0oakUNyoD2UvQRC1r/7LUkhrkjuzjuHOzqrG2w6Wo0ddd4NOlLMmvhshAYfKE0a6SPPSkVDymxIHN68s+LS4WVH6ijOMYntm5XrSQO05fSTogidLLtNqu1PC3EvidXEFhR/faY4+Wx6CjIqyAJ5J+QHyFcbxvibuhGYznDgpN69xrdD1Wa68QwOvl6DvwbltPqefYYGuIKecwXQO5yEop0QzPAIjXeS1B0I0+qKvwhoqp7YbDjhFIY5zAtVFDOgAV4GTgF9NP8wd6JbgLKE9uW+X3ThYqy+gkRvivTHehBp/wD57yfx/b7MtfB3NTwfHVuhxwMcDemw3qHmBQXcH49QaeJUrRCb6GnigiNfR4v29VaifF8NFUYZ2GIk4EJ2Sa1s+/KhR3qguZtUQNT5EKRLW/JaVhSUlLSLP+Bgi3Y5UKnwp71WsfX4+gabO2pqqAtC/MKz0CcWuiaCqb6i92s8fCFhU7+2Er79gUes69nDf9gX9HfteA==", # fmt: skip
|
|
10
|
+
"locale/zh_CN/LC_MESSAGES/argparse.mo": b"eNqFldtPFFcYwMeCt92mD7ZW2/RyaqWw6tBlWytZvKBILQ23ItGmD7XD7tndsbMz65wZAV+KIioigqlENHhBQdGKGE10gaUkpu0f0PStSZuYmdndl/LePtjvzJllFwQl2fzOnO873/lu5+OvVfl9HPx9AL934Ld+CTfnb9MrHLcGyANfBVYC3wU2AN8HxoCfAnuBbwF/BRYC/wZuAC7PA5tAXx7b3w/cCvwhj8kTwPeAyTxmd1k+x62mfuQzf/z5zLf9wE+ArcCvgKOO/BnwQ+r7UqZXDlwObAK+ATwI/Ijet5T5exPoBv4GLAL+vpTZ+Q+4Gbh2Gce9DaxdxuyIwBuQk0PO/uoVHPca0AtEwB0OvwOWAY8C3wROAQuAfwA9wH9XsHjXr2R5qFvJ4m0Gvg7sA64F3nb4BLgK+Kfz/Q9wHc2nC3IC3AD8FohdLK5fXCwOw8X0lkCQy+h9bla3jW6Wn1I3y/c3wJU0n27mR7ubxTHoZvV54mZ1f+boraFGUFEQhwRd0vyoILP0EA9XUBTBQlCUwx7ih4+YqtAVwqqqqFQ1igkRwthDXDnSZkGV4cg8uRBtEsO6ohOkxDRRkamYrTwEBRRdCqKooAUi9BQlJh7CCWpYj2JZQ+v4dahZ1CIoqgQxKlCzkoKizPKALEQxdSDn3qxe4XOKhUgkKIhjKg4IGg5yAUEu1MA7LFPlkChhR49atEMGewElGhXkINWICSrB6mLGFDkkiQENEuEEjIimsrSQxaXEFuOWGA6AFVijjNMLbuaoChqSsEAgABkvcAjEUWVR6ZxdMSwrKmyCFHwUNZTNtcqJ8mFBEsGJIq01BnEj+NQxzZC98GQ1AhFFDORKUBFsKQSjkKpEYZspENpni57ZBE3R2oRRq6KjKBbsegckMEI0j7rjBRZlBfyWJKUZArEbJxsE4Wi4SghpkWzUhKYVSqjiQ7oI0XNOWQqzXTq/xGyfcDGFiHQlSDl1IRGlGdEXoQrRQsgSVgk1J+vRJqwi2kG4RdSYmhYBwxEsxZDTtzlyvcnpOMJRd0MKjYm2TdZxQcWzbtvto8vZwj7Xerr8vaw0y4h1L5rbxqxGNIf+uenUZYhaCcviEdpLmauZQeqxH3H1qnIQLuWrgvw+Fq0f1TfUfVlZ0Yj2VTbsraqrdTXgmKJqfA0Ji0F+lx4mfKPiR5U1O6uqy3fu3t1QuXevq76uka9QsUAd53dDpv3I5/V9xntL+JItyOfz+3wbvaVeLyjyDfiwSBbV28z0quFV8I2qIBNJ0Ojc2r+zdg/6WseqLqKtzYIcbrHX5SIJCKRYCBQH5O1wSg7rdmRHIge+gMMHKmpnN/lGLERzJWhrdXW5JBYrani7q17SVUHiP1fUKKRIjtmfZFtJGWLLbd4yV01VTWU2TyXFXleFImuQVL4R3pUfabhF+zgmCaJcBq+ClkjbpmshvjSrRyMKYZWvlANK0C5vaZOoufZgGau0QfldrX60S2jCEvIVl2yBK7iZxKn0ZH/q3tBM4lLOoJ9JdOaOehDOjnNYp89fTI2N2ScWmPewnxq9aZ47PV8h3daZHhw3R/vNgRFr9JYx3mlrzM58s2cs1T5lnhlPd3Qb8dNz5n6BiqyRQTPRk7x03Ow5ZvXdp/8BOGf5kpHPtJ62DTyn97Ttsvn4Qer6T2aiPXl+hLMuXLMe9lmdP5qJNls/O/RB1XY3M/fNc1PG5JCtNGfuzzdpnniQvHMU3M6EfyF596YRf0CNES59/bg1cJWOGiN+h/np7KVOPjTvnzPibQsJhi4tIDC7+7Jb5vRUsm/YmDhh9f8MacskSrVD7DsF/mQnttmWsEPLDOxZFXDZ6rqdK4N+MSbP5o4CcHw0o9f54qNnrGMjrMhW+0PQsfrHcqf3TOLqy8wb8W4wYMTP0oyxmKzeUdovU9N2sqc70oOTRuKa2TFsq3Q8gvPGeBeky+k/u2KZnptfLKbCGVPdyal7TiYhgcmhieRIlzlBuy/ZecoauGv2PDbHH6Xb2syTE45GamzYjN8zT982pgeto2NZsTnay5pljnvG5LAR72J3OL1w+Qor5GLNYg3cSV4dBmnq1g3rSq95cWT+tKbPOZv42SQ6qYNnnho7MfuCmFEIG1qevvD/AXszg6A=", # fmt: skip
|
|
11
|
+
"locale/zh_CN/LC_MESSAGES/ruyi.mo": b"eNq1fQt8VNd5523SdpPZbJq2aZN2N+110hpwJAF2HrawjTFgrISXecR2HAePNFfShNHMZO4MQqZqBFgggYQECIQeIIQlkHlIYJ6SEEoTJ91uN32m7bab7baJ7mi026y33c223T52v+/7n3PvnZeQk938XI7mnvd3vvf3ndM//5kfP2HQ/xp/zDD+DZU//j4j639/9SHDWETl96n8eSp/7sOGQf8ZJfRPksq1VH6Oygkqy6j86V8wjD9+l2E8ReV6+v0hGvTTVIaoXE5lP5X9NNfvUPnL9HvnLxnGVioPU7mayv9MZTmVH6PKj1D5RSo/SuWvURmhcoDKxVS+zzSMt99tGJ80Me+v/ophfJvKbVRaVC75Vayrg8o2KtNU0n+G/aBhbKCyh8ouKmepfJjHp03+Aa17jMo4rW/fQ7QG+v4/Pm4Yb9HvD5UYxr+m398pBZz+rhT1P0eT/0sqH6PyZxgeVFIX4wUq11L5Whn2/e/KsI9/LMO+f3mpYdAQxsql+F2nfrdTuYzKSSofY/hT+QEqH1yG+T5N5QoqX6LyJ6nspvJfUXlmGc7l8jKc0zeWYT3fofL9VL5NJR2J8c9UPkDlh5fjXMupJJAZlcvR/uBytOtdjnXfWI59pJdj/pKH0W/zw2i3m8qdVB6j8kM8/sOAz88/QmdE5SOP4PdTVL6XypepHKSy5hHgx3s/gfP+3Ccw/1Eqn6Jy6BPYp0Mlgd544JMoN1L5KSr3ULmKyjep/Dkq/yeV1NQIUOVnGa6fAhwPfwrne5fKRxgOVC5h/Po09rWBynVUHqfyM1T+x08DDx98lP6Pyi89ivUdpZLQw/j3VNZS+btUEooYjz6G8aseA7xOPobx/+1jwJe/eQzzvr8c63m6HOdlU7mZyr1U/hSVf1AOOH23HP3+j/r9r1fg/B5dAXpYtwLnZ6nfB1dgvcMrsN65FVjXBx43jAoqVzyO+Zsfx/rHqfxZhgeVRD7G76l2334cePBLT2D9q58APtZRuYfKvicA3w89CTxb/yTms54EXrQ9if1dVb//8kmM8xMrDYO6GktXgv7qqDQZ3iuBPzdWAl9/h8r38HmvBJ38NyqfYX7yFPDthacApyYqn6ay8ymM94dPAe/+/ims56cJKVYyvFZhP7tXYR2n1fffXAV4/IunMf+KpwHn7U9j3sTTGO/E04DvladBz7/9NOb7WyqJJI2PrAacN6wGXu5eDXgNUxnidVH5PK9jDfhf5xrQ+b9dg3Ez6vdPrgU/enAt8DhJJbFA49BajPcGlf9M5Z9Q+RNU/vEz2Md3qPxFKv/NOuDHp9dhH8+sQ7+qdag/ug7wvrwO5/KtdRj/r1W7DzwLPHjyWfDLp58F/vU8i3Y3n0W/bz+L9f4dlR9k/lEBPCqtANxWVWC+UAX6faUCcDxTAbjdqgAcvlMBPPngZ4BPaz8D/v3qZwDXNz4D+v9TVf+LRMwEKmPTZ8EPuqj8JOMtlc8y/NZj3E2qPEglobIxvR584h/XA38e2YD1v7QB57VnA8YZ2QC6/fMNGP8DGyFf1m8EHFIbQW9HN4I/XVe/v7kR+8psBP28fxP6v7AJfLtrE9b/O5sw/7s3Y76Pb8b6glQ+yvDZjH3/yWbg2b94Duf1yHM4n23PgU/sew749ofPYf3/TOXLPN4Ww3iRytYtmO97WzDeh7dCbjy7Ffj24lacY/1WwLdvK9b9na2gpwe34Xd4G+ji+jbs81vbAMe3twEuD27HejZtx/7Pbocc+iaVP83tt2N9/3k75n0fIdUOKh/6HOjxEJVxKsc+B/ownwf/3P081nXpefCBv3oecPrB88Cnh1/AfM+9ADqvfwF86MwLwJ/bqkxTGWa5/CL4+1dexP6+8SL4+Hs+j/Ne/XmMn/o8zuFNKollGf/18+DvD72E8vhLgNs/vgT8Nr+Afa34AvDyxS9g/We+ALnz+1/Auj7yMvSaT70MuB16GfNepzLAfO5lwOu/v4zx3vVFwM1U5YYvQq6++kXIufQXsd7FOwBHawfw89AO0OP5HRhnUn3/0x2gyx/sAJ1++BXw2ZJXsL7VrwCOX3gFcNtP5UaGwyvAgx8LGgaBwvhsEPs7HwR8/jSI9h+uhN6zrhL73kpljPWzSsz/U1WY9+NV4L8rq0AH4Srwx6NVOJf/ReV21p/o43Osr4Uw31+GwBc/aAF+lRbw7rAFuv8OlUHW+6oBl4PVoJ+3qoGv/70a+LSoBvhXS+WXmP/WgH8+Ugs+ZKtyqBb48Ue1ig8QEr3CfCCM/V0JA+5/H8Z6fokGu8v7p/JV1pvpsLYwvu2EvPuDnTiXD0Qgx341AvhHIsCD/RGMdzOC8/izCM7jf0QA31+sQ1lRB761uw7tTtVBX7lWh3X+fR34xCej2H9VFPUdVFYxX4kCTitiOJfTMbQfj4GfzFL5m6Tf/kwc8//ZlyGHfiyB+T5EZQOVyxIY52ICePU+m2iKFIqlNvS4H6Sghzy9C/s7QOUlGvfOLuw7tBt4tWc31v2N3Wj3Yw3A20casM/nGoD3X24Avf8nKh0SUO9+FXJ7rpH2zvT16xjvd38d5/YLXwFebfsK9jH+FZzDX1B5mM+rCe0uUnmR1u1Q+RVaX+d+zPuBZloz/V7WjP3fPgA+/K0DwNv/dgD0+vMHIdcWH8T5PHUQ+Bg7CPjfOYh+M6rdT7SAf3ykBb9Xqd8vtkBeNbYAHjdbwK//ugXrf1crxl3SCr050Qp6+7VWyPmWVvCj4Vbw66+2Qj79mfr+/Vac03sOYb6PHwKcNx3CedQdQvvXDmGe7kOgl784hP0+cBj8v0KVOw/D/mo/DLz436r82Tbo+SVt+P2Y+r2FSjYsd7Rh/B+0gS6fbMe8qXacZ0s7+N3dduDjXDvm++AR/P7UEdBr8Aj6DR4B3G4cAV3+1RG0f7QDeP35Dqy/pQP0dbUDfOaPOsAnPtwJ+by9E/N2dGLddzuxnj/qhJ783qPgF+87inHqj2Lc/qPgF28dxfn/4Cjk068cw/fVx7Du5DHAYeAY+OrlY1j/PxzDeb3/OPotPg66XHcc7avU77iqbzuO+S8dB3/8PfX7o11Yz7ouyKFYF/Z/owv63He7ME66C/L5XSdwDu89gf1uOYH+O09g38dPgH//pvr+6ZOYf8dJ7PPXT2JfV06i/X84iXHe1w08+9VuwPdZ9bu5G/Uj3dj3b6jy293Y52w31vc/uyFXPnAK8206hXbRU9Bj9p7Cfq+dQvs/PoX5/+YU5PuDPYB7pAffT/fgvH+/B/j2wV6M99lejPPFXuxvfy/486Ve0P9X1fe/6MU63tMHfrGoD3yqUpVdfdCzvknlLj7PPugJiX7Q8X/sR/8PnUb5qdOYd/tprPfV01jPV0/DnvnD08Df71H5H5hfnMF5/fkZwNM5A7r96ADo/6kBwGH9AOBTO4B5Tg1gnNsDmO9PBwD/f1a/P3QWePE4ldt4nLPY94mz6HfzLPjtn5zF+f3TWchvcxDlJwYx37OD4C+7B8GHjg/iPK8OYp6pQdDVdwdxHj91Dv1Wn8N3+xzw6Ow57O+3z2H+97yO9g+9jvovvo7vra9Dvlx6HXT/d6pcNoTzqVXl60Pgc38+BHi8axh48cAw5t8yjPPbN4z2N4bBZ98eBr/++fMoP30efP6F84Dfl85jX/vPQ76PnIc+9g/ngV9PXIBen7yAeS9ewLy/dQHz/G9VfnIE8iE+Av2hfwTn+o0R7Pd7I9C33v8G1hl+A9/73gDdf/MN4NuHL2I/HRdB79MXgUffvwi59U8XAbefvaTmvQS5HL8EurhxCXj4t5dwjk9cRvsXLmM/r17G+Ncug7/+5WXgRekV4OXWK5jv81ewvi9dQf8TV0AHt9V3c5RomOluFPxvYhT09d4xnPPyMdit1hjoPzkGvL4+hvF+V5V/rb7/7FXQ3dKr6B+iMsF4fxX8/F9ewz7XqrL1Gs7lW9cg1979JvBmzZv4/Wtvwk849SbskY9dBz50Xce5T6ry29exvu9fx7mV3IBeuv0G6Ph19fvWDeDJj98EHiy5Cbn97E3Qz5ab2E/1TeyjRZVTN+FH/Zub0INrbkHeTd3CeP90C+N99DbWs+o2vn/+Nsb7ym3AdVDV/5Yqv6vq/+E26Pkjd8CfnroDO//X7uCc//AO9MV3jwM/PzsOPKhXv0fHsY8/Gwfez4xDD3hwAnLs+ATw7soE1vl1VX6Xyi+yfjcJ++qVSdhDPZPQh78/CTz5xF3IpR13QX8jd8EXfkt9/8kp0P+mKdBV/xTG/9YUzvOvpnDOv3KPbBmGiypv3wN8PziNfVSoMjUNuhmcRv3vTgPPf/yrNAf7Nb8K+J79KvD+G1T+BtuFX4UdceU3AK9/9TXg4favAQ9avwb/Yf/XQB+zX4Pd87dfwzgPfF3J+a8Dj6q+jv20fR18cUrV/5evQ0/8ybeAd7/0Ftq9/Bbw/Mhbyr/4FvTfv3kL+338Gyh7vgH4/K9vYL8/903sc9U32Tka+KzVUBkLJkJmOJq0EolUPGkmrCorvMsKlZjW7nAyHK0pMwIf+9jHzKfD0WCiwQwmkuHqYFXSDuDztlgsUlUbDEfNOisZDAWTQapYVRlLSFdzW61lhqxd4SrLDNtmNJY0k7FUVa0VKjNfjKXMumADTVhqJ2lYM0lt68Ov8nKC1bQc86UGKxKJ1b9cHbSTlbFY8qWlL/Mo1eHdVsisjiWkB0anRa6KRMyoVW+b4aRVZ5u1wV2WWWlZUZogSNNti5m2ZZlBMxK2k2asmjvXlZiJVNSdJ5FqCGMIbkOzldFe1sTMBlppfTDKazdjBKJYCgNYEYs2TUCxoslwwoo0rOTm9dFILBiyzapg1KwOhiOy0qBZl4okw8lUyOKutCQ7FrVLzLoYFlNfG66qNe3aWCoSEjAFo6EAjcB/Vlq0mWgoQpuubHAXu4UWy0s0n6HhaYUJsyoW3WVFw1aUgJ2kkWpqS8x4hGayTIJ41U4zXE3DNgSwd1oWj0OnRB3r6mJYbCph0ZpClm0G4/FIA++Yxi7h5ZjJ4E6CX1UyTCsPBKuqYokQ9aZGNG4sammYmslUImoLkKh3pSWTVdEqygOBh8yng3a4ipcatWikXeFkgxlPxCoJknbApP+V8gnrPXLPmmDSqg828NkT1AjVqO3K/KZqD/VWpU3nbxdrTiPSDgkKbsc1G7eacQJFivdF3VbSIjclaoJRwkT+FIzw5pcSkCu2bqYx7WQirGCQO6qHsIQN9cFIhFcRT1i7GD8IznxkZjIRrK4OV/nWJIenu/IklZFY1U7uQEC2bNkLQZKBYVNTOl21S17q52IRWmbEMlNxWpkVrMtaFVMzHRNhjh4/Eo7uJJoJhnir5uLNwI9EMGzzBmSFYdtOWQpridYeWEJUsFbxguIELSjSMB9NJ+mvSKTMNxizACYThmpO6/paK0qASzD84sEEMYycfpim0Cx8uslwHfMEpo1wlHoFI+FXGaLJWgZMMFFjJdU2SmTRivLikVQN88IYGtYyddoNdpKxutYKoMci27STsUSwxjIXW2W0nK1raBc0N0228XMbLHPr1jVLSkyBX8Kqi+1iHDRDYXsnCCKQsAi4dcFkkqFmC0siXlVnhcKpOpAawZV6ECsB8cQStNN4LMr0pqFfTYceiAeTtYttmkzWsZSqltqhF0rwV3RXnfVC9EU5yframFqDvcJt92J2w/jnA9w0zixeENwDNNGrTfwL02hUslMh2k5kZ5hxiek9TiSv2HIgHMUeaRzwz7CwZ9ox4auVYM76bKzerBewc4NImLgLwYe4QRU1I1ZakWSOXQdEro4E7Vrefm2QJUA0bDPO1YeZ0dG6EolYwi4LBF6qjNF4NQni+y8/SyysgdoLLT1Aa6Q5N1r1hFOEVzZvkKQZYf0u4ntyRLx2O1YnnEyIMhwl/Iow443TKHTgdjmNsRl/my9hmj3BZKyukUEQjLCoaZAlqikEckTyhFN2mdeVW+yM8vkKsdER+gd2lwf+6a6i3AhssUrzJJaqV2wiFa9JBEMW8CgUiy6SM2F8p0qiyypaYxIskBgRMWErFNgVTiRTxOisKP0Vi9ZZWO0WkBdDPZ69NDqYiCVcsKyMG9Iytq75LG02wp9ts44OqI4GTNncibUCWqWSOok6hu+XUkRaQXe8aKqukg4mVh0QrpWKRnlW/zZpeyW8hVorEqdxzXAdYcouSBf6K5SqSpaZzxM+uMLQpm2UuGtjNA5G7JhepBkMhcKKwUdj0VI6JDBd35oBfx4nELcS4VgoXEWAblBckFsIWlf6+CxDXVDYtiGv9fxJ4s2ldcEoDR4K2FaCd24Sow8yF1DAWU0YbhEjriN8idBIjNBAsqcbiO6rg0SDJcwchBKIde+ylPioI1FHfQVImhEzZ5Uds0Am0Fb7thZQUACK1pGaQNoMbWxTlOR5vQtFa3c8Eq4KJ03afh2hqjQPRnMPxoMpaS2pOOs/pEUQbXkTesyeZHK0xgKDta2koJePazPM1PkHstDcU7fU2cpmizRhwEakQSxRrA2pcsQ7RM0LPG8tooMUaNHQdIys0ZTKekj+kDZrVididVh07uYV/YHX2RDU6tQDfOqy60raKSF4Khqic0/yEQVDtACmILOiGudVG4vZlk/JLMF8GseYrcb4fHy4KSISEGeVXfNDAaZG6Io11JL/qCGRmhB8I6DstOLJssA2OoydMjszP6YtkIBZmQpHGAxq3YQIRACsWj5AjGqrhdN2yRdyI5stxnfWMFckprWVearLwEmaxm2C6pdTpCUJsFK2sFyt5pcwOUAhZrXRJ58DFokh1SlLiLMuQk1Em03FuX+u7aAa2p6oKgso5Qcyg1QMUiKjpLrVkQUh6E9MrpoAUM2YTFWEqSmaQNkFPkWIuXm2Rr0rmAgHmYmam7UCjrOlVqwqZ0t0KNkEflIi6kUkJUXjL8cs3rBqDyK2qlI0RDRJ6GCn4oyhYDeeKuTOHQ9X7cQE6J83rNJwZPc1qYQgMibJG1rtWO1PjkYMLdof0U32lHqzfvOJVYRY1DcUFsN7pv/bUxVLRZONbIN5ppwSjs+jSbBSGRYhZWgJu1MkmHMKWmgLemYD4HlLrRuMizZoRdliMv16izYxyeZh6agxuMzcSJqLUrDpWzLALK3K0gJWbC1elUYnJtIG8GwxX6OWcJys/fAWPXWYtiWmlHDifPM0Af2dJK9f39GSZg3OaDOJxzBTJ8/7vKAE60AEblpEmNVOpTKz7R9l2aWlKx+YhmhQYwf0uISrfiqQsk5sRWJx1hlgbRKBkgyirrJ4Rgfa7YtaBSSeFbR30mSiaH05RSoGVJ1aRnqeXW/Dxyr9E7OIUcqZWnYW3jLPJJvfPXo9qUYXJaLlLJkRkq4f8LDRU+1LsCKfbcALJhZDonanX2kUbFa2QkDBCjyjFhJ8I+vyPFgJK/tiYYViFmwn1uWCPokCJ0cJcV+qC9bDaIAVyEcm3gJWlCMNAcVA5dRkN6QR8Y6z7JlgIgFhG+MTocUL5IjfJnhUJrYsbZ7xQ81GPchCp7+CInDrSHMJEXDI1PMvOBSCC0LzdoG2OD3CcDLkjp51Vh4csDg7xozVj9LJcJxFiKvOWFGBOR0do2YqGStVyosm11zZTGyL9Tyx/KyAxxrIDLZcvqslll2bqGoU+IerPdbG6wsxH7NjWsMvD4iVrfpZu4g5fPRXFgt9lpZCuvgWVmpXJcLx5BN7ZNmNSz4qZKiJnWwoGthTegr4ZhhhxWrKmZeYrrnoHc++yHzySdPdqwb4y9r4+JwS6Vu1hk+NdBvRFsBOlVmVzaGUT+oBj88IQqSimpvkNIRSJWcAyoHyCr2INW3Yz1aA++YbY9I4Z0itYovaZoeJmJW+BITU+nRpaTxF+gOvwaYtEFbTYmvIDI9q8eQCHuzapeJsd43eSCELCorFy4oDVTG/S0VJIyYMFmuRfZyB3IFoGS64tJdUEbP436pYaJCyVBeuYSKiE6DDqCK+KwqzCA2lurBtIHOQShEi29iAY2iVy7nUDlm+qro1YTvJfgW73NwT0n83qsqt4Vetcnfje2z62QgSS1o2tVkcjYm8XqL+TkVFjulPpS6ZsclTQ+fSuHRPNFiHQRbvUapkI9qS2lnPvls/M9jD6vgOMUlhbvPp59WTTOBaGYW9P9WumFZ6MhipaJFsIJk0D/HYmhj1WCVoFiK2WpUU5IsEoRqwQ4SgQk3WSCWTAIMVTViA1Ybj969nEAQbXiZz08ISVWt2ESbYDVdktML1uaOVus7pfLWhgNVjAECM9NqimrejZ3UZi5eX7okmdhD/YXWUjiznQ4k2VgmN8Ae32bhpm/nMpi3mlrXrtq9ftcXcvnXtlq1LzK2pSle+MGcRAnbdpULGxmLe5BLjIXM18TX6k6irnH49y/JQoxULYBgcD5mamRGLChEuc9FIn58jibmTkfvL8gd/2hpJ1ZSb7vj46SE5/RRsesjcJvZGeQ7GyUfVYrvywOY4NWh0VmWqIuzFXDJfO5cGqNHnCI9j+MZ/NBoSU9FKUNyn2xmrSGLHGU8V+MvNF5c2LG1Qeu+La7eWmBuXRpcSSfLvjZvKCvQgSmB9qoYUA+WKEda8nGWk/2SpVVUkZRPO0ChiLbNKFLKSxFQwnyi7mkUbqxI1KTBDVv6DUCzEZUOQDUc1DzJW2Ta1M3nR0FV4i3XxpG34PR/1YqX7lX0mUMIBDUlijOz019NDkQorDY2NyRIzxs56vXFRwQnB7DAxxDJjtXxdSXYIqnewQtpoGquJvyZcu56XRpTHdj3p+sZqJSiYl9EPMRMtUBL1sD13nlu5kv6KNyTCNbVJc/HqJWYFbSicTCWFsW+NVSfZCCxxvUGrqkhtrWuQyiqxwW1zccXW1au2LimTqJeMxFaBuJVI51xPiydiLTdXxXmJpQ+XLTMfr00miWksXVpfX18WlO9lsUTN0gja2kvXV6xeu3HrWm78ZMBYzW5Ccc1gzwUEnPlSVUMw+vIicPFFwpI9cgxZIEd2EC5ksCJd11isv7DVEAUfbuBPNJjSIIVBspAntLF2s89DrMPFCmfKtZ3stVxirImJUieoHLJg4rDqzZgVDsFKZxO0wDJ1X+XtDkaVXSdqC/tm6oKJnawhs+zTjeMJoi0ExGrdCJryycStBBucljRmnTPIQBBTjJVPnzPS74F0XT4rjbWbnmFnScSSKeG9sdg6iqfE4iC60lqlZsrAbnMPUVs01mishVJNaFrJPgpsjb4ysJUOswegh6h2jylsxyPBhh1aiC8JGGvVCYjiYLqCVCIqa9Zu3bamYouQLysGhM6KTEt5BDKwUpX6kEjYGs9YrJaxGkLnn4KGL8PGtI8yJsQtzID5IDqoMUmrgSXoQ4o8VDCeiXE4jYz2LB8erUy78vOVTu5TF5SgrVj6BF/TVp5UmkP72KinbilRTFEttYuM+EW4mrDYBmTIDCBOTgdrbtu0YT2LPCblaFJWY6zTIwbzJ1ShZTWcwMcLjGsFDpP7xylIf6GgiATFn+HQoV86vK8c2zyisc6vM/B8rsqqABg1V6+vWMG2jVh3ZMRyK+UPUdvyj8GTuoqGYDsQ3EYz8SSB4ngl2ttdWs0uyRBhA3w2EuCOs8eaEdqoWGMoz2qlFQlbu5TPWVwflakaNzguUAuqUIzwISX+NcesIVxLVZaRrFrKOpEd2inlUulgiyOmQskkfcIiPbWxm2WNUFPSyRkj4R9RuiUTJ23JDQW4HZgUdBeChVCTCmjyUfrcMT5PjFFh+911xNRomuow/HiuSzQaq19prOd0CPH0uIaBOmZb1UVD8Ej5kivm2R76eGNpW02EfdEOGgzZv5BSwJ62hrhVZq4WP7qoETSEJFTQEUrUQNQGyezwPlMfuwwDekvHb6bwvCWWB1CpDBffdpnjGBvEsBQBtshWe1Gmqq0rlYNW/5S1b/HF6ID6qlYMSQ/tieirrYSId2rBUiQ7oSVoq5VRrY/R6cOAUcVAq2fSFXVElpkMKpQCD2J2Ssyy0BheI22i5TbTFpFiEmisxHJOU7GVWb3yWOjihqXJRMpaunwJGx4QwW5ddGl1MGJbS5ctyR1pQfvZuMnYGAO42DkM0VRG38oMiWmxA0dridkR39x4BYm8SMp6mZVxOINI3WXPbZJGKpT8I+zKC6cZmzgthpgrZ4ysUUkxWd5s+InpHDelkqWx6lK46ERgu6xn0R6W4jvko6hWZdyafth5Q8FZEbVjwsVCrgaiRRjSn9ZA9tt6clFaxDfjWuk2809lxEtAJW5VCdOguQkYCa0aSVZEMEEsOElb5ngMmU/Ef1kv4UPlKD9bOlmd6Kh2EdmIkqytM2UU2a7fX4ssuPw3B1mFjmUJM7VnLQjna+NKYk88xhJZHYrpeCqIUaNFD2dZuGJ1D+dH7CAVtaqxXLeEl4rDRjRXSgChPIgIojAVJNm3HKy2ELtgX6exOcGeVoV/j6/duG3tlidVFF+Fm0iFp1asPcopK/3Fwz1O1VINNHL7AhhUI7kUkDQ6fLFSf5Y/RPhx2oDtaQ5ZsNCWJicD0Xpr34Gy7GrhS4znUmwVCiIEibqEAYMzZjFSY0s256XfiifqMwOzESBqyyAns2DR48z5nyx/nHHtyUU0hBxO0FOaNK/zze+2IpKooj9YRjFh+7/7QgieboWcOn+zIn5KnsznnKxjfyJLwbxpCvTPnySH/bjpB9kt3U3KRFTnyzfIWo9Ysj69WPXnMF8sZWe31dHnBFNvuE4HTAyVLZKfImJs4SiHsvhLtZ6jLX9Zn+v5CAS2KDc4jQCXTpmhI0Kab0B+Jgy4qK2Qy7IJcXbobByqzaUU0RE4McDHriHbpe19kXOrYogJczHPskQzd4t0DvrghcBBT0Xb+5qpw15IU1fHvl9jxf+U4VO0tabj7EVL8L06uCuW0O1cE6BUGcwhEabZurY7CU8u89gykSwiZ2HBUMiLKN2HFRcbN2thOeMz6Ldlk7c6Qa79MnOieeptKzlPbSqq6z3vOYmvupDdaHrputUkzoTINq/a9myJymXIDU2pAJ4to/ksSAgT+Qp0xKJgBOmFGNsKqpYstgnkVgLRE0WyZWidZUpzErLaIStUrFkRgShnUAjBV5lyQ+zVMI1ubk5VUq12LZnsUlqsDaQ6tBF/0obN65dS5dIlXsZPjVo9pzxI1q8VIBPjS/Qny0RJafVpoF5VjBNlydLR+a6SQFxlcQZ6uNwsYJ2puqXxhmRtLFqqfgaMbeEk4ej2LeuRey28d5Hr5MUH5hmLvIhkneignPYi2QHU19hONjoBaY3F/67ljMNy1gaStY1cpQxAPhLfzwL+g+1R5tY1UbLfQvfX+rYzwhVmR9vjIVZvNHcUHi9WujsT4h1ejkOO1CD7z3g+LwHTzc+y6lYWqk4Rw69j4zMnJ9Beaby4dqvxoo6bMtoK9TawTc2KUlIYh8+lQAOJ0kELKcnOLX2lNGnWRFOl8UhV8hXm5n63+0ZXPyiXCKa/jtUf/mruqePgdY3VaHj5PdW06wiSRQu2UZPXBxPR3HqM7qkAsiY9pTpj+cbzkBJdiesS4vZQahtLM2mi3QvZSaD+qkTBaKARVPcqcvwthkpQUZhNuFrOUWKN36lERASigNv96matShp3tNTaTeyBl5lrmqEf2oMshETc4891WDQYnNlcFxevDoldRgCi6SRQsCYSqxQBLJi802rg8IzV0GhUvhqOPyy5AyzQdfSVWWsjjc0pjNSbaDXJ1NdoKGbLvoxSL9hGFCCJC/XsE2J3ILsVk0QlbvuwJPetW73adf8+SKhd6XMkq/3kiMesAbjeo2yvq6tAK4XgHXVy8+f0CSmD9B0N8qOs2iVM3Qu3Toom9nkYYJuVMbIwFM7zuYGr64HATIP5Ejs74OjiZE6/aL6GtdB+on5oZLpfZ0ncYvv+FRGNbLK+okKSuI5RUEVxnbLBCDtgkrV1KuAoZORm0BkcpE9oV3yBOFJVBKlaIggltMK5Bzuf0PLJL6iMKveikBxtMdxhZPYRJtLBmeeYHLbzhUyKnHGBHgopmGWo815431SU28Lhuof/ldCBobkq7164lUQdJQpkKHvPJ6yrZWOydVazDHVNzQxBVVq76ZncTzvzbrkZrmD388w8jpmNPuBFYc+zIlHYEJyqwSgCi27kMQbvB1xHhoKzJLV74ROxIWU78yTQGuCJojkjN0vBxCasqQvaPkqmMRON5cwl+XpZo68n5wMx1NFHplMxnR1sLRfuVDDF0tUuvHbCac1FOLFF5WKgqptKBFd1FQ/N83ppKHgsf5HAeVEhpl+8s2BTo5oEoewEwwjEmddRaMw7emBdQRjI/hXApRc0voJt+SKbpfMlSahUJRripKoLui/y8N2nZZIFJZFlyeVmZ36l4RGH4BpnChPClpucb8MMlLhsjbl4D0T+DsnCMZ+kw1QIjS9LVtBYnP8VrSk6oN6Av6nO48OeStS9QX/6JaLWrDm6ipvE41zWboO9ktivtsDukKidTMXZXyI3AGLQNpg7iV+2AFfVQonVskqLMCgMi5R9/Np3qVGxxGSVNittRUIBpRJYLHVNnEWLhJxiiSoeAfgFu4cOYPkKf16CSrliW45vmxo1qSjpJwtXT3J0O9vgrSbEpiIWCr8Zi02SV1ai3GDiSqRUYJhdVIyl7n01QXFDmFfU1V85wsaISTgWY/9fPbt8QyHfpaigT98nFpg3ALRUYhn+wIzAHSfmskP83iEnKBypwEg6HZ/tChmDG7+DIcg2jEV2eX4hOF3Dco9U/IzaxyE815fdTSORmRwOFVApc2qUjU216q+8FrC3q+V2RAMGMRczfzT30L+NSzgdJxjxuunboJJnpOKsQlj47DVUhjyP5AZCa7xJqI+iZ2HGJZ7d6dG11BjhJPT1ep/NqOyLHW4ahoGsGFWUxhNWwhJHtRF59RMLx+G6YASpCXq9itXC5M2tLc+9G8DygI6K2F3QVkmjntVgS2aOEqBiTihTi6RbmAWpb3w3piqs208qC2pUrrRr1rSQaLBoj/yilYW9rBlk7LsuHCW+Q1aUpbsr9LwJPdoSA6OclBsJKYlnOsnXpaKWzjjkFMNQsMG7uqWz0nUah4G4ARQUuS4MX44/yTu4KxZW3tdIzKaluIFOj/W6IZsiTNgftTEI6PR110r+w8cBVFP4sICtQBSXmCsJlXTSB3eWNXlTV/syUrkahlhCNKfstHktLKVVOMIXjbKHwV39uIV8ccOPP1keUapwNRu9bB0pILopz1Z2uLl7x9LrUNQAK3A90H9NJPcweWnKKeM6zfWFP7+rAplyexRyqJmS8P9l2QmswZebIP0cdgi3UI73niWlilQWvlh3/9zQIrqoF7LLdnFISBDu+oV1jcQqXe1tQT104wVPxxJYnn9QqjfORXTUHawmF+3omcjU3sUF9XUHW9eN80FI34pnfMH9bndAlY6Y9KvsRScwRUWyy4pOhLvReYlKK0zObNc5aV5qk88e0nOWliYs7/Jw0Yn8Hqh40QvMbpUv8Z7dV+Kk5I5sc6uEQZEgixS0vItsdRwFtkL+FJxFOsVW5Fo898IeKWeu80lfJrXdIKyvCxTZLLSX233Cp0hR5ERPbYd5T4RILW7vc5BOjeVejRL4Et3kVkgWPvKlfAHetczbVXhXuuFuKipXJxORj69WtWy+6jETwmmzbiSofKWcyb1LFrvCQXihVCBAhQESOvPO8CkE6qoyLhdGkkHbUMk/LCTxF4lJz9hG4rQK8YtkRCN2OO6pTXGo/VVSVhiAjbqx30xWSFKe3ziBm+0qKcqLIYs6UenXJal2B31VRIBXWfD8iNtEpysrgNrhmii7IVW6Gc1VjaQOPgpcwbJTcUvdjNIsdnWF976AvgftjcAxCs2l3QQBDvAnG1bozLJQLEUr990IxInlPwrB0feg7X8HwnDFQH6416sTzh/K+VgoTu3VFohIuHWSCsLqTEpSr3xxeLeJKDs5E2YJquzPQmkQFIbHGBhb1P88Ycs8gSlWYOTfB1m7ikc08qr2JGO0gUakfS0x7KoY6+dSNJarYOJu97KHCOdatrOzL5MkYyE8KiM5L0nNR3OHy5f2tb7UGpEvrtjnC4IiaGXsH2UkGYDZ4B6+t7ID33eo6y8kuuyd4XgcuFEkTTXPieR2yQoXVlpVQcY9TyKIslbKJr1iz0gedbsTrEoXMJt3p8Nw80WgzpL8ZVagkqHzdC43LOH6SHTKF9H4/GPle5fdMbSvn8Yoybo1Vlqqhixl545PfmPbED6LbJgMJIW811G8Ou9tFaNoVpi22qgVE37IKJjuqm4Ic0PcdPS3kwyGsCtO9AsH5SqlAfiVk9jAFoeqUQm1xMr47gejvQvB4pPEqqtlCtbPUwlxcGY9e+GGFhcyhHsD2gtIxrzHC9SbX5LJE07Ycjd2nlGjMqj/fRH/uCJ8C+XLFxiRoa3ESOHkEF7SIm3TL6LVWZGQP2WKGXQdbDY/yy2hUdnOTGprTCjCcJmlh651qd0a7Tw7zFB5fWx4u9TBzgKFpF5ITT0YRGtQl+zzzFcWa7Z/QPY85A3qGb8MDnF2ijxEnxUmCdGY5FoWzHmZh4JVDMT/bENW+qDXlb4K/wAW+IQ+3KfQGb118g1gZqY4YxXwM4o8AuCaaVHfgzpyh0hOXVRIEeRaP9VnEqy0Y5FU0sqBQDEpw3dCWAoUiOVau62qVFLPuwDIKdVJbDhX/dMGkNtP33wryV7gOzgZpBrr697VVpDPxfZCk0on9IS1Ug31WRWfSsNQKQGl+f67Bfb3opML6+AFDbM6+K8FayjAGe2zOva4Vh6MCy1ptCdDX3lCfL6YU8OH7vfxhvqDSjr7sLLB96iebw2et0cm2MPotYMQq1Fhlyykjl9XqrSUv0vxzEWeM9G7sUXsoj6W2GngCTsBnqf3gW9VrKENsyNvvkYxlfW8h/5oNO7/To68mZLNixUT1t2ExZd4t3xxkVoR3H1cMsU9MLyQ+zhhCi++UGJVCRxNu4N82b5Ev5GiAkC+yz5sfZXkOBh9bEg98xNsKDo1dBhTXsMIRhgveK8LH7JQci6fAJ3jc2s3bC+V5B6XpfuFUCIYtatZlnBKZiUbPbaRivrCpxJEEc82G8x1IR1BIS4qf+xgzCkXBzoCUNhLsszIuTQkyS00Zc5nfUVIXc7n945Wr68o0e/EMbNze/hfKstVSVNR+CtyadhI+fOxWHpLRFDciWyn4o9GakZUu3Dn+TuMF4mbxDMnc3mVoV93pAXpP3fwBQVD2bFFJKt+6lMUKpdzlBeQuO57dYU84GLa8tOISCHKeffOqCeUqtFJDzodohxv/uzhfxtLzBoadA/902jsfnXhYNHvfvyo7swG92WdUvWESFH9+51N4bsUv8A57B9pBjcOWx/Men3TeNVOhhYOVy3AmTjNxezNsc0v68vx/HOHuiG/hC8AMNWXVoRK1dMg5bDql5V94pNly0qDkXhtsOzhZQ9/cvnDjywLbJHX0ko32DXhUOnTqRq7dFusYK5mgZt0gc2btpXirjCx9zUSVaGBP1W6bHnp8k+Zy5aVf+Kxjy97dNkyali6xcK9hALtlpcv+yTaref0sG3Mw4SzlZvPr9q4znwhZSVSYfNxQs2a3fL3U2G7KmiXBavKqqJPUq9oTUq8VK/W7niWOu9YvdH9WLpNyNCrMR9fv/6pSJiTX58MbI6kEsFIKd/8JFBG4/LTfmI5u4T4zyeWrQhsqNiw1oPl8rJlAb4YTmdcuk1iFUkSNEvjESLUFZyMmSC5+EQqWV36qNdOceXStVE6T0KecvPRynAyoK94EuQbys2ng5VWxHy4bPmnaQojEEh33XJars51jc72nZq5cyV98srbd9vSV153+kfmmpqcgxPfa9qHR51nJtoy031Oyy3+7/he9dSzc3vYab49d/ye07w/feJqun2UKtCfh7tyjvpnRu85QwfT/Redju6ZycmZqRPp4+0zU/1Uld434nSMcePpUWeoveDrzs7YOEaY7X0tM3bAabnkdB6ZO9iePnnV6RxzDo04nUedsbuy0HRrU7q/lWrmuifTpwdn+0ad0wed229mxiap3jm/L3N+b3pgeLb/MNo4Ld2ZwRHacmbsdrr1QmawrfjDzzQA723fSPrUmNN5nkZyRtuc5pHZ83tnu0bm9g6nbx5+++6AEZi5czgzNUX7yuyfcvrOOp2vzV5od44M8N9D1zI3hmk+p/kWwccZuuAM9RLcUDtzp92Z6JoZH0h3n01fPzHbdS3vTRln6LXZzgO0FGqbmT44c2diZuIArYlgkz45PnNvmsYO8GZeb6J9zkwO02KcO3cyF/Zi6vTIoHO3AzuYa9pHC5+ZOEL9AZ30mX411v5m58BN59oZqsVSZ8YPz9xpAqTmDh50Ok7O9t2h1aaPXEyfnHr7bi/nYTsDE+n+y5npM+kjw3Pdo3PnTqmXh/U2ZqeOOs3XaSNYAcNodJpaEtxyWtKiaXHUfvZSz3ztVcWVU4REWe83p68NpgeOU1du+5CJfc1O0n8HTedY29J0y0nC6WOZvV00Sbq/3Tk0mO7onLnX55w48Pbdloqtm9++2zrX00nonjUV4ZeeZu7Um7N7LznneIFzpyYJ22HFp2/unTvY4VujzJ31nLM6AemELfEmJzqxYSx55s6h9J07dNCzI9ed0V41WGZqbGbyltNGB9ScGb3qTJ2gsd0VHb9HoFcwGbqWPtECANCG6NyIUNQCOzozN28z7bafIBzJHLlN5wkSfPvuXtq3S8QuEyhGxMwuhIjnejvS3beK0WX2YC7ZOwcP0KjO8BShF1P1Ox5j3h5Moi2nnQuHnbaT6StDxA3SZw9iI1j2XH8Tt7l6wGUwjEj7iCG0zNw7TATBzzg77YOMK/oVZ4JOuuOY0zxMI87cmUz3T8xen5ydHOARW06iiihl5s7FAK1v9sIkrWn29b3MXyeHsdD02btEhLQoIl/492cmD2dujGBXmelTWBedLlEvyIx5H07g5EE6/sztMefeazSlWqZ+vfl7TXsD/uebaSPEPpmIR9vSJ25gHbQDLDbT1Oy9+0xdsx9+9vq2HHDaJqiXd3Id+zJN+zF3oPDTz5l7x+mcZq/wVC6KzY608ynL7mhHM9OD6b1jHl/df53GI8yanbwwO3lFeClxasLU9Olhp+W2c6CHWPnc4ITT8gbvp6WTu2a9DTc7fJoHOnQq03wv/VqrM32J8BkvminGNtA5M9FHwzijrZnXm11iYqnRd4POhplEawtxMT68rhFmbIRIuln++840FPVFl9mJ8+nTA0A938DUZHZA8UM1HssOnp+PVbfEGNTACABNMtOdeQLJ964zQdVpPzg7cYEhTNKAxpw+RTijiKGlz5mcAKUSyTpT15zj7TxdT2/68MDskTHn3H4/WUmnuabW9OE3claU9ZrzzGQzn6asjKSCn61p01SD7zaT09Q5Fjf9Tc5Vxnen/yopHHN9B2ampgm4UBiAIM6dUefQG+mWozOTl9Nd46RqzExcID2D1jg72cFo19mWfq2DxZu7lvEBOiTqizGd8VvEJdP99PHoXNPeuXOnnaGTtAZCbC1PRwiRMtNTmTsX+e+W25mJSzOTU4yJvvUwlssfxBdIBM9MtRMNsLDumyJ5PddzAGMGWDzrpTh95+ZODcyODpJwZiwTceL0jIBBk7hx5bb7evPcJB3WEAQtkblAQQ7rQLMzOj63f4rOdOZu7+z5SeduE5A3Q+OPdhPHEabODMwFAVQRkq1z3Vf8mwFoeF6n4yLjYv+IaDBHZgdHM6N3GMm1tMcick8z3dpOaoyC8DE680Mzd88SqZDekgs04qfCjbGiuaZeUlCAKfd/tJkAep9Xm01mwvcz6kwm4i7WF2enRgXawCjiqXSaxJbTlwfTTRdmJo8Iil7NQ15n9AyzNcEgEo6kQ6YPDTtHDwFJSb/KOXKGxPAU4TWpYZkLrwNngVzOuTNzJ6YBWzkArV+ByljZ0yidme6h5bHY7hqjRWbOXQKc+Ww0jWAjCim6Boj7EUBmpk/PnugB4mRuDwCxmSgr1vCU6dcGMlfPMfH4qevMa8QZsHBB/eNzF9uITQZYc53IZ3U65UMvm0Wbj1cpjirsOX1leO7iEPg7sMN9WETMiiHCDl7NRFdmbJgwkJfvE8rpU/dmhybYGpg4MO9jzRAuRJO8SVJsvOQP6gzpwQoFoe7Bi4R+gutDrgpPc2ttplVpyB2nCAys5ZByq7bJWnG69bAzdGN28DKDZGwcchiNZe/QNZyp4zRVumss3bYX42F4/3iagKF79Io4Qo37GeP6h4FiMdfcDmT2D0ay0hUiZMBhBEIaGg3tMUl/q/uOAqOyiDe/dQRxBLxw2q8LjUz416psGWatPJXansYRTILeJAfBJGg1t647QwOugBeB0UfnQMQ119MMVGG+fuQqs6NTYwwLKEZitdBsODHCNaflIAsoMgaYqzaR9kDsjyjAR9tYt9qJqCe8SmBf0WeSzblTzWQUOqP7aQSRg4VeTVYHAyjLWcsrpkSutGpQAj4zdnePsEILQiMtbopAfI8YAziuD6CHlOjXWiP4Cq/8bpNz7QT/23GUdHNYyAHZE2mo5y6RBuF0HOJpJ/q0NtFGRJwZfcPlSmBD/gmJaTN8zxH4BnzY0Tb72i2CJkbzbAqZyDt0KF0tVwM5CjLtQimDE12E/jMTh53mFpbEA51k65IlyvotbTxbRWaDRkwsokCMIxtkc/X6CDghC9yJI7BSmNdNDuc+hGxCaEOwMPimDmCZcyf6nf0dzr0WWN5Kt5844Vzbx0qdqL4wtdKn95Nhx6qEuwOARRmJ0A3IeB8a8C9AvVdsYns52pm8emySvutMN/O8R6dmJoey2L5fV9JDYVoWBc2k7t8R7b9nZrzF2dvLZwvwE1He7YX5DgbmR1UCKPFMUQh6PewnuyJPsnWyPqueSSa+SGZJZnCYlAbG3ZPjpB+x/KG5hMQDtLf0bbJdztLp5z2GTEdzhc0DvTUSr+k3B1m9oC/E5cUyJOkvlkbv/4OHkf2qDowIpYKCfQuw2UgQq8BdQd7c/08fR85Rk8GafQ8ju8IX5kiuK0cZJgHIftZIgHyKUq447XeICHN7uX4a5jWybSjioCui/vy3jJmhNF9nbHdaeqAIkVU61zMEZ5lrC/ltEWIaea4nau4quDOTx52J467ql03DObiu+/stD7+zxnVtyYbpaAE+WiZJROdeN1OH2GLYvmu2Q7ZnpvfSbtjyvtNMa3MZun7aGF0BKhZX6iuZtB1HYUvT1/yHjdkzd5U205v/trFzpTtzaB+1Iwpgw0IJ0lbvS/9FEi3+7/O+c0yd3KdOdHOBzzzPHMv5FHvmmAyjw3PdN2QgWPlAGWa/lweVBi/yyzk6Aj2J2gIbqF/6yDD7HJqvM7cndRi6jmnO9nEVS+NOduIRos7zHc8QEzDwpjHaOB3tBfvmfM/uW2oWkuWFdH+4huCINbD3+bt65ocJiwgK+X2fMiYch/XGfNb3mjGd5NzpM3Onz9Fu0j2jZAFBlNCpwvYE/UC6gdbSE52gJRGtncBTA3jEyPCQOTv5GpDUYI8gC9O8141R6fGja5POmcOM1vqN49nW8fToIf636QJ/z37qmL74J1SfCr13DGWdK4s+eAynpesDUGP3X3Rev0owxgz5bfyvHLMLlmQye2F73ZeOEUyAFgKhaRBlC+50s1YjzJc66FeOM4MjJBa/29RPiP7dptN0Yuq5Y7fC6TxPFaz6FRyIGLK53GThl33yLU7nJT5PtsxuEAPkAdhjL6ZLeqyDVELvIDhgcX528gD0YIOtortn55qOzU72pDuOcSM5bzEq2OlgkJGh6ZD1uzkOIJyZvdLlbsSAzgv0YxYprDt/XtKVWdtXZpooSSQwYKj4XQ5aqGLf7OqfPIlevDENjYHcx4+J2ZIAASLPjhx2Jjpm7x4nlc7AhpzO9tkLVw1liQkNsl4z0YehVYXT2c0+Pv52ej+2LU8fw9cye+Goc+X8XM85bGv2bNfsG7eolX7mmEUBoj2n9zstb8ASFntQ3BqZsb10ij/8W8dwiwkfznmB2IRrTVGxT6jlv33MrrMfYiDp1kEa5lVlYwgXNkixd44cnr3LJiMokeNbUyeY7Qg+0IZxxPp7q8HCk/Twk1ezpO+BZjje4TjQD0RwY2VZ+zxRYqE0i41Oy5lAMM2AwzHdetxpv4odInzGqCe6l+EGysBU4ZqD8Mn2/g0YbIh1nFRqNy2geZh9+nsPMvWt3fQMa8L0Y7SXpsdGgZfuY8VQbUFq2IABkOW/VEywKvZUsYQ9lEC6ekD52TXIeSn6seJME/tbWYKSYetqTEenGOuFiwP8BoENbm+/toHOIFsD1R7diknmn9N/nIZzd8Jp0bEO2KSFdDdmM3AQ8WGcHZy72Ebqwuyls6S1yBcOPhisBU72EFD9XYms2eklC/UHKTIXXsu07WelTh4jBg8lHJhwh/BvUXmnfGPlL8RAozlSZye6iI7pRL1Q8rE2ZY7pzn7kNZiU+J4zu5/FTQDF8e27fUBKVkXJ5JMqA8gHtINmALQzJJS0D96ybCfsMIlYp4csqcNkWwMKCqkq1hieySPS3+8J4PeDER/1a7cLfi+Yg3IzE0MIyolUmTzC4Q6BhKty54Qt6AyOoIKZJlHIeKurtcHnQkPOTBwzUIhVftWNibEG2KF8R2Rx+j0PhjL4tYuGA97nLnnPBKtAfAv7PZmEnZZugpCyKETl9h+hqnbGbym3i96Xu/Ycv5TuUKgdhvdAoJqCR6p1XBmCEuQCg+0mOMqHelkJFjHMbgiRu/wdowz1zl5oR19RDTBc1sqyp9cZBL0BpnfVXgyA3C35lT/XEUN4Pzc4ruoAc/VDkT7RnPRQHAN1sLCAz87eDvEB3ROdiBhXXgqEy7uxLsNpG6fJEdwnnZAJmDbccsnPCdwUBbcKMp133HkJZE+G0Ozl8zN33lQjcnDl4EXFEvJGVI0IYVVvORfX0sht57I24tj6iWAOvZKhR8qkrnEfCIZKljdI8TVnt+48b8CBwUJNVDn39AQNJjpITzVyPeIsF/tblYwWtHD94tnvBYsGxjKPzFPoVZ6l4o/TMOsGheQgD8Nf1sBBEGJDkJLF34gzM7eaWY5PHCC+7fTdkE2wUPOGBE8jCk4fucApPR1XiTxzxLyXFdJ/Md3GIlhbtcoAwmIVyvnMXp4vc/7AbN9JmCzpM7fSZ15jRaWp15GcBtjwrKKwL0o4l5/LuwhneJQmFgSHBrRLm8GebcgHDImbS5jMFTg50okOXqLnoo7TTpsLC6zCfXI0Kd9gJHXEB+N/DNh069lE8zyBZGmO9rLsOHub+C6z19FWziTqfU2nIh3mrAHx0iiUSbe1FngVmBO3VLC814A2Br6hcEuyfVQFcFObInDIw8Go9PAspVy2B6R1mZVm5vMolKCF5hGEGrRm6dNQW430wHBmTGXK+Jkgw7zprqFQys852Qmb84KvqSLLoiUrVkL973KQlyWnf3jmYgJKFbtSCojPB5a+dZNQ1Y22GTn+MfjDc3DC83/2t/K2FuRTA/PXXrTMdD8ZT35f2v1Huc+0bkaQn7kU7qOc7DK72wLR7px1wraDFiqe3+wh3HbIdMOcLFL7znB6oTj6Z9+YYJeUP81A2YI6l0COpuuasotdVQYOkdwHgVnDgvuDnQkYgqnEDc6SMg0p2jNisBahzfpCjwITY/STC0QpZ9Yxe7/s5iwYqCiKuG7Mk/FoZJCIgxMA/DZ520ESLKQtZO7do3kI5rwEYYEF80Hu10PU3nfSw29ALriTuyf3HcEFdWUF4vw+V6d3+7DH4UAzA5gYuR7RaemZuXuKQ0icqbRfdtd5NIfJIByBtA7tz22DW8F1o6jwUlPz/ddo5+4vf6337QtW5upoWSqdCKJ70N0LV5Ppl77VMl8jRnN+LphtnXTrPTYcySaUzaoHhhkETW15wR4V/BJnuNiDQq5MymB+/sUJ7o4N+9VMokMC8tz+ERCRyJ8rQ0jxJgbIdn5rC9Qq5s3ZzwGbC38OOJA+wr4fsW5enz27100w47jO5BGkvjGFQQMaPZU+OY4MSVjO4j3ljir/9Id5B5hUnrlzpwzmxZfGirwAzKfDF0BIeUUgVf1qh5qpXwE2icBoMXNdPZmxMfZe4lUkFcmRV4BVkKOg3S4K1AGlNt9HwYPf0sOfAsiDfDYvMgz78VibN+HspcNO+3UYx278Pkd2GEg2QF4NEEqpCCoLwdXessNhutGpMQMaiUoUnW6eG5zMHLzuXD0KjVKpDJr4TPAPndi4oLd/czIa8t7/hc6DmkJPAGcO3iCico+tQCu1isyV85ILlNMEs7iKgu8ZYPeg3WeA091n4XcAVFR4nAhAp9dDu/A//JuTQljoTWCW/OcuAbmUr1z9QM6Fwm7CWlr8gl8BZgSXxGq/1ZX19C/n6mjTnFvA6mq5qjKSPAPr6unM2ImZ6VFGoo7umenT6UPi9m4eca41KbQdHJeYhffwr86W7/WxvEYzM91FmoXv4gqy+ySIKig+fUkgeZXlB9JOie8dupVu2isMNU+M+Zlr1ivArLHxO8DK/+Y2cqW7MCWh45wWviyq3NdXi/YpKF8LN3WpJa8FQCHJhkUzuQignsev+xw7sNy7HR0tnI8z0UlszINrAdRwTzpfai6sX76GtLB+fvmf5fKEM92vMChbiujmVHPW871wOihLyLMTyWa8fkJfWWNf4rlLyMZSPvyiQQ9134Wsjvs80Ws6zYfneg8YWWRzf5xhbzuT/97jyI929biiR5zbAxgxd/qMe9IL7sqTEZ4IV/I/0KviGnRaeJ5XJQ1xwAyv9LLPoPUeo4jyxec812uoXB/+f1YIh4j64r+NpCogde/DxfyooXgF803SZTrOQ8p4aC96IuuonOOreLFbC88FK6L6JtM8aZHuYRR8xNdAxhFAoFzqsG2EYPE6r8fo3AdkZ++ezIwdzX6iznS99xgiv5c6g2NtOW5itFThBvdRXm9aZQVPdIpew1em/B38Vm8uS9YP8+Yx5Xm6qndnCT+QI4y7CemmC0j/Y/PdO2mFXWq4/B0T0UHNMsk44M0LpPMbpo+cI50G2TXeO7wKBIZC76wHeWfu3p0ZP6IiPfDw81u8fkoQBEu3DzinOVUEOSO8oaELnNQx/3O8YiP1AcGgns8ztN5Gbg/lwNG3yojdQacDJP1pgAYjnGhhSASHX9hp3s/5BZKK7DeBkPUtqC904+OqHGhoviaxXu3P4vBe3z2nayhz8zZH1n2xABcFv9t0mm21O03p7gukIYrJP6HCJaIcLPitXoTBgHAkpjgHYznBhUwY+A5UdtC5S7jQpJ/pXagyka1siUe2tzPT3cFZZSMXnXFJelXmB31y41uSQpcZuyFZDr0GGblkP7k6JQesSKF3Ol6f3TfOgkWMWU6HJiroec25cx5KN/G+3K7UgiNtcl7zPBcBJUq93ps7Bs55nt7wfmMSHSHLHoLTSs90ui4ZuDUznVNI1843vDmxaOikCPUTLV68w6fk5VTBNOVa9ylet496dxfWKvoRhSFOg3d4kWqIp3jZ1S/Ob3cG71VeLznGe5G38DRoqSfrzXqNl42Z/oF0f1/eY7xarZ+Y9zFepGeogoQbx93u7JeneBeIp36PLIRCrtmpWmTX+VPNfa/xsul5fm/62r4cxR2pI5zfQsIcKYa4+3erhXQnmj092GL4FGkWPyqZVOpciYUFvIOmDIBOgaP3Mq+bJ8McWfI94DFnXW3/FFFVjgQ3QFPQ/XMGV1eCxBOvUuK6h6EQ4I6FSlXAdkER4Cxwzjvjb9K5cebM3mmnWTs975yj4zOUYTtxgJipBIiEvYK2JBDCGxnq9Zu9Kvwkj++K3ewqT8CCgu/smt6dBDdQLLMYbryG1yuJkT7ntpuLjZRdBK7IAoXy0DNigOOAByObQw02fTA9OSQtdLAs3wmZq/e5gVi/6sJYmvX67gItJxyH8hwv8FYX9Hw+14kjWV6DsQ7/Ey/KDdLieUzyr3OJblzAed7LjhG/59zvMFd8sOWkuqvquxf1DnMaiyuhODGVHqgc4zpeluVXmH8MdMFLvH7V7YeaeYEDwHWRlWjje5B33n7u9Zd5nuSdd+U8sQRMEPHUYWVJl2u+pVT04q/xwjkisc6is0CL9A3eByOHca152H/RKfvlXWWbIAQ4np31wx7y+8BTeX+KXUP1MWL4TEjXFHehAble4FVelTzYto+ZhC/BJfs13qzLWOCUUMMwD5yVqiEeYljAA7zu/WHO1BRuBlYmETlOv+DRFGwEVKRO+j7BaTl+yxdCdd/dRWN1d9ur957eVQYoa9akVeCigaA09UTOF16jUFMqi8bnPOdUIhyGJ+eV7SxGuX5j12Wbfsnuf3E3W+S3NpE0ksvK2am9uU/oehhA+rhI8AV0YsxDYLfj1NzBjsLP7NJZ+tWcrPd1/W9GcEbVqSOcHSvOfkQsOM+NH5Lw7jkb6cPHZidPs5+F+LqvA58yIR6tXRK0JP2rwlTGCCf0nGT8uNU8O3HBz//VqKIIHkF8PdO0P9NzlA9bpuJ7gCo438fYcqCdb4fiCpp7pixApvr9t/RVCpLvYr/ht8lU/m6WC91XDdFQoKJYnDm3UaHAgX/67KwRlU0C72fWOqAU5a/DL9myK1T4ZwyZnmp1rggs/nive8OcXcCyRdbdL182vSd8CfCScXTNfclXvrQa6qlck9MPT9zgvOw7h91LDJz/3D+S5emfPOQM8SUc5De4YW3nKswo7lV4WL+qQCdBDFqGQpeFty/yOq+pxsncvs7sodAp5vnhisuJrPd4mZ4wqsoplR+uu37eKbwbB0ia9MVkIQULaGNZ0YHOS27ycJERCkSAs/qJ+t7lu8p0iJYOA674g7yGSn8mw859hBevVHg16RM3OKMABMp5jCT+fC/1ZKVdoRppxP5qdWr+7ABhedmv7cptAZUqoFKbZVjOkuoZmjt9Rl23FwBxQnRLj9/EKDY8HskVlOfGGBM377U9cp+Oc+dPEq2AERJHg6XkDdVy0m/jINu76JhRtRZf6jaNJC7yrMcXCuwJ0C2aVqGcjx2dTsehHN7n5R743tp1rnSnR2/KXdGRuaZup+M2dZnr7UKSiJdFPNTOnt2eEfaw4MaIZ+fnvXPrBqUAJL6hfu6SEifKlizeGfYnB6ylg19Oi4Z5T27c9jnTU7Mnho38yEdh4kLswW82FniiT6kJhZ7EhXnEC8BlT9GgjJwb3LjwqzOoew2FxVDWoAq6T9/CFcoyemzcFZxqU/lsucBzWljVLc2be435IJBlWOQ9a2v6vRFuNojaDZa0IPBmKVuFnrI1XU0LeAql6X4r19cxi9peBVd0aszzB9y/rYtHucq8G7ZlezT/+VqVH+8z+L23a91LQtpJoK5eGn538TxuRGXM6Rnc7UATRJqd4XueNkebQHaA8vvM90Kt8k8jVV25TirWmHAXiUcrN/taOTskiVau7fGbtPmt+i8XeBJEydGsJDFN134VWDHJNvcC60I8Du/MizGve0JOl53sF/Mf5cnO6HETLHDO/nQ5158mzzb0eNf5lUVZeHzFGw5f5viDpGbeZ5z0laGs5C7NpJnErk2S1SyP0Jr+XDodjbt7lrNjiAddG1T3zeD4z35eVnJvESQQDxTfzJQoIhs8iKdh0fzwrKTT6NQc5E8gbZCtObltom4xKUbZhmRZcDJtzkldcWWL9RqyeAvRXVbqDwQgInHtHZnRUXGJqxdn+b1ZRGQX6Cd+Z8EPxLnFE5DDVAxc2uS1ZD82y2gOMX35ck4uj3tPTad/9+YLsOJPyJruU1xKkik5LB5c1glUpN4N4EusBE55eWCWBcK9bnEo6UdmFwoHnYT2I7vsfoiRfLeS3XftCuul/78H9x6dy3v2D2/LLhCc/pdlORUc77/l+CD8L8yyifZ/ASw/BAk=", # fmt: skip
|
|
10
12
|
"meson-cross.ini.jinja": b"eNptkMFqwzAQRO/6ioUQ0tJazaU9FPoLvfUUglDkNV4iS0IrB4eQf69k2ZRCbmI083Z3NvDDCJbO+Ck2YgMDsnfAmMYATWOiZ246sgi3G1zQXVT0PsH9/jYbq0GSI5BSzoBvxJYXzF6+f8j9ixTicCKnIyEfhYEv2GWaMRmzEyaEVZimWbltG+h8hDNeXyHo1AO5ClS6bSmRd9qqFSgp4cBPz7C9iwzJoQxZiHN4RaJrC7XYtkAd8JXnW7JwOI1kU5PH+FDwecsQsaNp4azWiiqknC+5EH3AmOpdgz6jSt5b02tyam5tuezR17rX/1Xys1b8aHLzN1oces9JDdr05PBYcrmHkrLkxqn0OqpOD2SvaxnRG2TOFdTax4d6GaBd5aRkcSd+ATx1t64=", # fmt: skip
|
|
11
|
-
"prompt.venv-created.txt.jinja": b"eNp1kTFvwkAMhff7FV7YStjbqWLqwMZSISSOO4dYJOfIvoRGiP9eXwR0KTedbOv5e8/bBmEkyYNvAZP9OHWYMpBC4gsEQZ8xVs598wCdn8CHTKPVgDIcJ1AeJFA6QTYh3/fCvVBp3+eIE2gQ6k0ylSG3Owli2h8p7VZ7iCQYMsv0Bj5FiPjUN3FKI5+L+EGGiZZ/zUPlroslUA06qTBnWNyc+4RaUJtHbdUL1vRTrPhWGYxtJDUgjHeW/5xX7mt270dPrT+25iTPszW3LV8KTe9z8+4c2LubuV6fILeb2ZrpMEUDLGDbFyHPWIE7VLhQbsDCjtOy8xFhvfFnhMzchsYbbU0tupLQBtUiDcKqc7GCdYPh/MoOzFA1iw2w4oezdaWujwiKhhpEymXNoP5kp02aZQjleFq5Xz21xdY=", # fmt: skip
|
|
13
|
+
"prompt.venv-created.en.txt.jinja": b"eNp1kTFvwkAMhff7FV7YStjbqWLqwMZSISSOO4dYJOfIvoRGiP9eXwR0KTedbOv5e8/bBmEkyYNvAZP9OHWYMpBC4gsEQZ8xVs598wCdn8CHTKPVgDIcJ1AeJFA6QTYh3/fCvVBp3+eIE2gQ6k0ylSG3Owli2h8p7VZ7iCQYMsv0Bj5FiPjUN3FKI5+L+EGGiZZ/zUPlroslUA06qTBnWNyc+4RaUJtHbdUL1vRTrPhWGYxtJDUgjHeW/5xX7mt270dPrT+25iTPszW3LV8KTe9z8+4c2LubuV6fILeb2ZrpMEUDLGDbFyHPWIE7VLhQbsDCjtOy8xFhvfFnhMzchsYbbU0tupLQBtUiDcKqc7GCdYPh/MoOzFA1iw2w4oezdaWujwiKhhpEymXNoP5kp02aZQjleFq5Xz21xdY=", # fmt: skip
|
|
14
|
+
"prompt.venv-created.zh_CN.txt.jinja": b"eNpdkc1O20AUhfd+itmwRDwEa54gyiINk2IR2ZUdoFEUycofTgkmqG0KSapWKEFeFDtARFzbSd6Fzp2xV36FXmNAiFnO3DnnO/cIawaLOzBHEPjg9LjZjy6H/OSXsFy4av4zGpLEGzacuY/GWFcPtCJ9NH6S3EeNUiX/QVZyW3kiRg4svzPvBPpmbDTEsMXXBp8HUWvIx3+SsMeCaXbDbyZv5ZNwFBk96FuoH82a4ptNclVaLqtHee2gKm/u0kKxIh8WKhRtJFQR1wGEBjhPYLWNTSKXiF7VNVWtkI26JMHYfmfBvJsUwL9mno9B4qYtlnfM7zDPgOkPPpgh7YvE1ieNluTPBFXQC/NECxdWrSQcShLB85y6Vnv1rNdTsBSEKrvIkjK8A4jWF9Bpi6WDpsgunFs4vcekqe/2TmGfElhMob2Iv67i9inO8cExCx6S0MxeK6paLu4VZIWU5DJNwi7zLLJDdVWRmD+Bs64IB5F7/vrr6YkUNVXXX37gsrIOMdhbtLSo33+z9nh3BeYMN8sCbGyUyaUD93Z8/AW3yMddtlwjeOTO+YWVFvAf3zNT3Q==", # fmt: skip
|
|
12
15
|
"ruyi-activate.bash.jinja": b"eNp9lN1u2kAQhe/3KSYGqiSVRUnuqIhEGqRESgLCBClKI2uxx/JKZh2t126o6yfqI/QuT9bxH2CHhBvEzpn1d2YO7sDCFxF4IkBYx5GGFUIcoQu/hPbBiMJYOQgrIfsq3giTO1okXKMBp54K17DikX/KOrAJY3C4lKEGFUsQGlyh0NHBhjEmPHgCo5tejq1r25o+zH9MzMyAEZ19M+D5O2gfJQP6oOOHYDzSZQVL9XSdE0aOEi96CD+79XHefPHlrGx8pUeenzNPMFaAulijwvEJpIWoAwoj1BAGLqBMhArlGqWGhCvBVwFGleqIcM3f0E2X4/lXO4Nn6tOxkhFoFSOQHSoAMbnoBFzRsLgGHgRFO1WrfnJszx8eb+zp7ZU9Gy+uh3QZ+YU9w/knL42MblNrbMv4+hIqXai2Z7HMfTQ7imLuvzQhZMID4eYDoKcV7bQhx8ei7tPewFRwdtF3MenLmOg/xbcGH8Bbgya7NXiPbg0+Jq9qNXhZL8rLyf3y0Jk9m0/vZosa94mAiXVQRUqG0sWIFpWvH1vEHbAw8KAWHLWwTA9a4anZMkYhL0VCKQww4Y3gtDPXpGBsCz9K050T+AO0hixj1aB2tllztzTiMhTVj62wT//MYVXbDwr7LAAHl5/28mHuhnw/vpuA2ctYe+4NB6WstpH2TMAgwsN9RveYXhco+Rphz4JxYpSN0iWA3vth1Psu3yNlMnfFqxtrfHk7qURmO6ONpBFCSl8kYtvsvv2d0+5g/8ryquztHzTke2mmRPwHax2J0Q==", # fmt: skip
|
|
13
16
|
"ruyi-cache.toml.jinja": b"eNptUs1uhCAYvPsUX2nMtknjoccmve0e9tK9eDOGsIJK1r8CmhjjuxcUF6uexG+GmWHgFX5u4eULwpxLSHnBQH9LUpGMUbj3INqeB3C+GRpcztfwxfOihCQ5o7j7jL3BB57CLytbfOcV+ONz+Q1oGBwyjkhzWUU13R/tvkbUxhMbEqs6AzjxYINOZmkt4PEBHWjJDR5wxUr59q5VtPFDO9oMnTH3ZnezX+P/k6xFFREZU3Iltkq0gFY/9lRdF0lOeGXOSLlYHKPTFjnFUwo3TguSySP+BFj6XFMXIQfLXoq6VijW0XbTIz0LzYr7O9DiWZJgXklFigLrqJP0ZuaEN8Ch7LPobbtJSXHJFKFEEVyS5rjmHQvZwlHsUSZXpzR/S7PT3WDVNvoRuxpWU9epy/cHatoHPQ==", # fmt: skip
|
|
14
17
|
"ruyi-venv.toml.jinja": b"eNqtkOFqwyAQx7/7FBIIbWEN7AH2JEHCYUwqbTxRN1ZC3n0atQlpSxnbN/V+3v3uX3NUnewZ0QY7eRH0gxbjSPNtmgoyllR21F6tQXT0WE4knyObb5E9UqFaz3uM1INw0IIDRr6EsRKV//JOQscBuEGqz32Tmb0+zM2N0NjINovUu/SwY/MEDk70aK5LPb8kQMEglmK4pcKikGrpIZWTehQL9nHvrFcXac/GSxcsELf1Kg38DL2wVWIYCRPWyz1pc/Cjw6Als+1UMXxewKEJvI1zPdKhoQ5ML1wDhp/eqKZSPf9VSScGu48BP9DOeBWSWfUNwdzvolfWQaR8tcK3M3Dvv1VeU7PnI9EAsb8rOcQLP4FUv4t1++1Vrjf+X4L9AYyLQa0=", # fmt: skip
|
|
15
18
|
"toolchain.cmake.jinja": b"eNqFkVFrwjAUhd/7Ky6IMGF274M9dG2cZWlT0joUhJDVaIuajKQTR+l/X6zOWccwb+F+5+Scmx5MjIBNuRaPTs/pQb7lawGu68Lc3g5nGPiR94pYRgj2x14Ys1GI0VNdw07IHdNKVdA0D5VSm7zgpXSPFnOn7g+hXIL5Mi3Tb64MwzjNPIxZQtEonB4Mf9CmOcmFXFiHs9LGchwjqrujQTpLMxSx2IsQ4FJ+7gd/hwklPkpTQsH6f2iVC2OUti9csj7zSZTYVi2V59fj6bQL7PctUfcv+w1tzF/NKIwDRgnJWOJlY+iUa5UX1YzgOi9gaXMVgi+ENsDlwn7Ku+a6FAZKCVUhoOJ6JSqr3JVaya2Q1X1XbfutNN+eBYUyHfzffCwiATrs6oV6EcToDdHBDRiHz9SjMyAxnt1iw9jHkwCd2G+4PsQk", # fmt: skip
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
"binfmt.conf":
|
|
20
|
-
"meson-cross.ini":
|
|
21
|
-
"prompt.venv-created.txt":
|
|
22
|
-
"
|
|
23
|
-
"ruyi-
|
|
24
|
-
"ruyi-
|
|
25
|
-
"
|
|
21
|
+
TEMPLATE_NAME_MAP: Final = {
|
|
22
|
+
"binfmt.conf": "binfmt.conf.jinja",
|
|
23
|
+
"meson-cross.ini": "meson-cross.ini.jinja",
|
|
24
|
+
"prompt.venv-created.en.txt": "prompt.venv-created.en.txt.jinja",
|
|
25
|
+
"prompt.venv-created.zh_CN.txt": "prompt.venv-created.zh_CN.txt.jinja",
|
|
26
|
+
"ruyi-activate.bash": "ruyi-activate.bash.jinja",
|
|
27
|
+
"ruyi-cache.toml": "ruyi-cache.toml.jinja",
|
|
28
|
+
"ruyi-venv.toml": "ruyi-venv.toml.jinja",
|
|
29
|
+
"toolchain.cmake": "toolchain.cmake.jinja",
|
|
26
30
|
}
|
ruyi/ruyipkg/admin_checksum.py
CHANGED
|
@@ -6,6 +6,7 @@ from tomlkit import document, table
|
|
|
6
6
|
from tomlkit.items import AoT, Table
|
|
7
7
|
from tomlkit.toml_document import TOMLDocument
|
|
8
8
|
|
|
9
|
+
from ..i18n import _
|
|
9
10
|
from ..log import RuyiLogger
|
|
10
11
|
from . import checksum
|
|
11
12
|
from .pkg_manifest import DistfileDeclType, RestrictKind
|
|
@@ -18,7 +19,9 @@ def do_admin_checksum(
|
|
|
18
19
|
restrict: list[str],
|
|
19
20
|
) -> int:
|
|
20
21
|
if not validate_restrict_kinds(restrict):
|
|
21
|
-
logger.F(
|
|
22
|
+
logger.F(
|
|
23
|
+
_("invalid restrict kinds given: {restrict}").format(restrict=restrict)
|
|
24
|
+
)
|
|
22
25
|
return 1
|
|
23
26
|
|
|
24
27
|
entries = [gen_distfile_entry(logger, f, restrict) for f in files]
|
ruyi/ruyipkg/admin_cli.py
CHANGED
|
@@ -3,6 +3,7 @@ import pathlib
|
|
|
3
3
|
from typing import TYPE_CHECKING, cast
|
|
4
4
|
|
|
5
5
|
from ..cli.cmd import AdminCommand
|
|
6
|
+
from ..i18n import _
|
|
6
7
|
|
|
7
8
|
if TYPE_CHECKING:
|
|
8
9
|
from ..cli.completion import ArgumentParser
|
|
@@ -12,7 +13,7 @@ if TYPE_CHECKING:
|
|
|
12
13
|
class AdminChecksumCommand(
|
|
13
14
|
AdminCommand,
|
|
14
15
|
cmd="checksum",
|
|
15
|
-
help="Generate a checksum section for a manifest file for the distfiles given",
|
|
16
|
+
help=_("Generate a checksum section for a manifest file for the distfiles given"),
|
|
16
17
|
):
|
|
17
18
|
@classmethod
|
|
18
19
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
|
@@ -22,19 +23,21 @@ class AdminChecksumCommand(
|
|
|
22
23
|
type=str,
|
|
23
24
|
choices=["toml"],
|
|
24
25
|
default="toml",
|
|
25
|
-
help="Format of checksum section to generate in",
|
|
26
|
+
help=_("Format of checksum section to generate in"),
|
|
26
27
|
)
|
|
27
28
|
p.add_argument(
|
|
28
29
|
"--restrict",
|
|
29
30
|
type=str,
|
|
30
31
|
default="",
|
|
31
|
-
help=
|
|
32
|
+
help=_(
|
|
33
|
+
"the 'restrict' field to use for all mentioned distfiles, separated with comma"
|
|
34
|
+
),
|
|
32
35
|
)
|
|
33
36
|
p.add_argument(
|
|
34
37
|
"file",
|
|
35
38
|
type=str,
|
|
36
39
|
nargs="+",
|
|
37
|
-
help="Path to the distfile(s) to checksum",
|
|
40
|
+
help=_("Path to the distfile(s) to checksum"),
|
|
38
41
|
)
|
|
39
42
|
|
|
40
43
|
@classmethod
|
|
@@ -53,7 +56,7 @@ class AdminChecksumCommand(
|
|
|
53
56
|
class AdminFormatManifestCommand(
|
|
54
57
|
AdminCommand,
|
|
55
58
|
cmd="format-manifest",
|
|
56
|
-
help="Format the given package manifests into canonical TOML representation",
|
|
59
|
+
help=_("Format the given package manifests into canonical TOML representation"),
|
|
57
60
|
):
|
|
58
61
|
@classmethod
|
|
59
62
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
|
@@ -61,7 +64,7 @@ class AdminFormatManifestCommand(
|
|
|
61
64
|
"file",
|
|
62
65
|
type=str,
|
|
63
66
|
nargs="+",
|
|
64
|
-
help="Path to the distfile(s) to generate manifest for",
|
|
67
|
+
help=_("Path to the distfile(s) to generate manifest for"),
|
|
65
68
|
)
|
|
66
69
|
|
|
67
70
|
@classmethod
|
ruyi/ruyipkg/augmented_pkg.py
CHANGED
|
@@ -6,6 +6,7 @@ if TYPE_CHECKING:
|
|
|
6
6
|
from typing_extensions import Self
|
|
7
7
|
|
|
8
8
|
from ..config import GlobalConfig
|
|
9
|
+
from ..i18n import _
|
|
9
10
|
from ..utils.porcelain import PorcelainEntity, PorcelainEntityType
|
|
10
11
|
from .distfile import Distfile
|
|
11
12
|
from .host import get_native_host
|
|
@@ -28,19 +29,19 @@ if sys.version_info >= (3, 11):
|
|
|
28
29
|
def as_rich_markup(self) -> str:
|
|
29
30
|
match self:
|
|
30
31
|
case self.Latest:
|
|
31
|
-
return "latest"
|
|
32
|
+
return _("latest")
|
|
32
33
|
case self.LatestPreRelease:
|
|
33
|
-
return "latest-prerelease"
|
|
34
|
+
return _("latest-prerelease")
|
|
34
35
|
case self.NoBinaryForCurrentHost:
|
|
35
|
-
return "[red]no binary for current host[/]"
|
|
36
|
+
return _("[red]no binary for current host[/]")
|
|
36
37
|
case self.PreRelease:
|
|
37
|
-
return "prerelease"
|
|
38
|
+
return _("prerelease")
|
|
38
39
|
case self.HasKnownIssue:
|
|
39
|
-
return "[yellow]has known issue[/]"
|
|
40
|
+
return _("[yellow]has known issue[/]")
|
|
40
41
|
case self.Downloaded:
|
|
41
|
-
return "[green]downloaded[/]"
|
|
42
|
+
return _("[green]downloaded[/]")
|
|
42
43
|
case self.Installed:
|
|
43
|
-
return "[green]installed[/]"
|
|
44
|
+
return _("[green]installed[/]")
|
|
44
45
|
return ""
|
|
45
46
|
|
|
46
47
|
else:
|
|
@@ -57,19 +58,19 @@ else:
|
|
|
57
58
|
def as_rich_markup(self) -> str:
|
|
58
59
|
match self:
|
|
59
60
|
case self.Latest:
|
|
60
|
-
return "latest"
|
|
61
|
+
return _("latest")
|
|
61
62
|
case self.LatestPreRelease:
|
|
62
|
-
return "latest-prerelease"
|
|
63
|
+
return _("latest-prerelease")
|
|
63
64
|
case self.NoBinaryForCurrentHost:
|
|
64
|
-
return "[red]no binary for current host[/]"
|
|
65
|
+
return _("[red]no binary for current host[/]")
|
|
65
66
|
case self.PreRelease:
|
|
66
|
-
return "prerelease"
|
|
67
|
+
return _("prerelease")
|
|
67
68
|
case self.HasKnownIssue:
|
|
68
|
-
return "[yellow]has known issue[/]"
|
|
69
|
+
return _("[yellow]has known issue[/]")
|
|
69
70
|
case self.Downloaded:
|
|
70
|
-
return "[green]downloaded[/]"
|
|
71
|
+
return _("[green]downloaded[/]")
|
|
71
72
|
case self.Installed:
|
|
72
|
-
return "[green]installed[/]"
|
|
73
|
+
return _("[green]installed[/]")
|
|
73
74
|
return ""
|
|
74
75
|
|
|
75
76
|
|
ruyi/ruyipkg/checksum.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
from typing import BinaryIO, Final, Iterable
|
|
3
3
|
|
|
4
|
+
from ..i18n import _
|
|
5
|
+
|
|
4
6
|
SUPPORTED_CHECKSUM_KINDS: Final = {"sha256", "sha512"}
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
def get_hash_instance(kind: str) -> "hashlib._Hash":
|
|
8
10
|
if kind not in SUPPORTED_CHECKSUM_KINDS:
|
|
9
|
-
raise ValueError(
|
|
11
|
+
raise ValueError(_("checksum algorithm {kind} not supported").format(kind=kind))
|
|
10
12
|
return hashlib.new(kind)
|
|
11
13
|
|
|
12
14
|
|
|
@@ -20,7 +22,11 @@ class Checksummer:
|
|
|
20
22
|
for kind, expected_csum in self.checksums.items():
|
|
21
23
|
if computed_csums[kind] != expected_csum:
|
|
22
24
|
raise ValueError(
|
|
23
|
-
|
|
25
|
+
_("wrong {kind} checksum: want {want}, got {got}").format(
|
|
26
|
+
kind=kind,
|
|
27
|
+
want=expected_csum,
|
|
28
|
+
got=computed_csums[kind],
|
|
29
|
+
)
|
|
24
30
|
)
|
|
25
31
|
|
|
26
32
|
def compute(
|
ruyi/ruyipkg/distfile.py
CHANGED
|
@@ -2,6 +2,7 @@ from functools import cached_property
|
|
|
2
2
|
import os
|
|
3
3
|
from typing import Any, Final
|
|
4
4
|
|
|
5
|
+
from ..i18n import _, d_
|
|
5
6
|
from ..log import RuyiLogger
|
|
6
7
|
from .checksum import Checksummer
|
|
7
8
|
from .fetcher import BaseFetcher
|
|
@@ -12,7 +13,8 @@ from .unpack_method import UnpackMethod
|
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
# https://github.com/ruyisdk/ruyi/issues/46
|
|
15
|
-
HELP_ERROR_FETCHING: Final =
|
|
16
|
+
HELP_ERROR_FETCHING: Final = d_(
|
|
17
|
+
"""
|
|
16
18
|
Downloads can fail for a multitude of reasons, most of which should not and
|
|
17
19
|
cannot be handled by [yellow]Ruyi[/]. For your convenience though, please check if any
|
|
18
20
|
of the following common failure modes apply to you, and take actions
|
|
@@ -28,6 +30,7 @@ accordingly if one of them turns out to be the case:
|
|
|
28
30
|
* Volatile upstream
|
|
29
31
|
- is the recorded [yellow]link dead[/]? (Please raise a Ruyi issue for a fix!)
|
|
30
32
|
"""
|
|
33
|
+
)
|
|
31
34
|
|
|
32
35
|
|
|
33
36
|
class Distfile:
|
|
@@ -86,9 +89,13 @@ class Distfile:
|
|
|
86
89
|
# to reduce surprises for packagers.
|
|
87
90
|
if k in fr["params"]:
|
|
88
91
|
logger.F(
|
|
89
|
-
|
|
92
|
+
_(
|
|
93
|
+
"malformed package fetch instructions: the param named '{param}' is reserved and cannot be overridden by packages"
|
|
94
|
+
).format(
|
|
95
|
+
param=k,
|
|
96
|
+
)
|
|
90
97
|
)
|
|
91
|
-
raise RuntimeError("malformed package fetch instructions")
|
|
98
|
+
raise RuntimeError(_("malformed package fetch instructions"))
|
|
92
99
|
|
|
93
100
|
params.update(fr["params"])
|
|
94
101
|
|
|
@@ -128,7 +135,13 @@ class Distfile:
|
|
|
128
135
|
return self.fetch_and_ensure_integrity(logger)
|
|
129
136
|
|
|
130
137
|
logger.W(
|
|
131
|
-
|
|
138
|
+
_(
|
|
139
|
+
"file {file} is corrupt: size too big ({actual_size} > {expected_size}); deleting"
|
|
140
|
+
).format(
|
|
141
|
+
file=self.dest,
|
|
142
|
+
actual_size=st.st_size,
|
|
143
|
+
expected_size=self.size,
|
|
144
|
+
)
|
|
132
145
|
)
|
|
133
146
|
os.remove(self.dest)
|
|
134
147
|
return self.fetch_and_ensure_integrity(logger)
|
|
@@ -140,7 +153,12 @@ class Distfile:
|
|
|
140
153
|
cs.check()
|
|
141
154
|
return True
|
|
142
155
|
except ValueError as e:
|
|
143
|
-
logger.W(
|
|
156
|
+
logger.W(
|
|
157
|
+
_("file {file} is corrupt: {reason}; deleting").format(
|
|
158
|
+
file=self.dest,
|
|
159
|
+
reason=e,
|
|
160
|
+
)
|
|
161
|
+
)
|
|
144
162
|
os.remove(self.dest)
|
|
145
163
|
return False
|
|
146
164
|
|
|
@@ -158,9 +176,13 @@ class Distfile:
|
|
|
158
176
|
# TODO: allow rendering instructions for all missing fetch-restricted
|
|
159
177
|
# files at once
|
|
160
178
|
logger.F(
|
|
161
|
-
|
|
179
|
+
_(
|
|
180
|
+
"the file [yellow]'{file}'[/] cannot be automatically fetched"
|
|
181
|
+
).format(
|
|
182
|
+
file=self.dest,
|
|
183
|
+
)
|
|
162
184
|
)
|
|
163
|
-
logger.I("instructions on fetching this file:")
|
|
185
|
+
logger.I(_("instructions on fetching this file:"))
|
|
164
186
|
logger.I(
|
|
165
187
|
self.render_fetch_instructions(logger, self._mr.global_config.lang_code)
|
|
166
188
|
)
|
|
@@ -170,7 +192,7 @@ class Distfile:
|
|
|
170
192
|
return self._fetch_and_ensure_integrity(logger, resume=resume)
|
|
171
193
|
except RuntimeError as e:
|
|
172
194
|
logger.F(f"{e}")
|
|
173
|
-
logger.stdout(HELP_ERROR_FETCHING)
|
|
195
|
+
logger.stdout(_(HELP_ERROR_FETCHING))
|
|
174
196
|
raise SystemExit(1)
|
|
175
197
|
|
|
176
198
|
def _fetch_and_ensure_integrity(
|
|
@@ -184,7 +206,9 @@ class Distfile:
|
|
|
184
206
|
|
|
185
207
|
if not self.ensure_integrity_or_rm(logger):
|
|
186
208
|
raise RuntimeError(
|
|
187
|
-
|
|
209
|
+
_("failed to fetch distfile: {file} failed integrity checks").format(
|
|
210
|
+
file=self.dest,
|
|
211
|
+
)
|
|
188
212
|
)
|
|
189
213
|
|
|
190
214
|
def unpack(
|
ruyi/ruyipkg/entity.py
CHANGED
|
@@ -3,6 +3,7 @@ from typing import Any, Callable, Iterable, Iterator, Mapping
|
|
|
3
3
|
import fastjsonschema
|
|
4
4
|
from fastjsonschema.exceptions import JsonSchemaException
|
|
5
5
|
|
|
6
|
+
from ..i18n import _
|
|
6
7
|
from ..log import RuyiLogger
|
|
7
8
|
from .entity_provider import BaseEntity, BaseEntityProvider, EntityValidationError
|
|
8
9
|
|
|
@@ -63,7 +64,11 @@ class EntityStore:
|
|
|
63
64
|
|
|
64
65
|
schema = self._schemas.get(entity_type)
|
|
65
66
|
if not schema:
|
|
66
|
-
self._logger.W(
|
|
67
|
+
self._logger.W(
|
|
68
|
+
_("no schema found for entity type: {entity_type}").format(
|
|
69
|
+
entity_type=entity_type,
|
|
70
|
+
)
|
|
71
|
+
)
|
|
67
72
|
# Return a simple validator that accepts anything
|
|
68
73
|
return lambda x: x
|
|
69
74
|
|
|
@@ -72,7 +77,12 @@ class EntityStore:
|
|
|
72
77
|
self._validators[entity_type] = validator
|
|
73
78
|
return validator
|
|
74
79
|
except Exception as e:
|
|
75
|
-
self._logger.W(
|
|
80
|
+
self._logger.W(
|
|
81
|
+
_("failed to compile schema for {entity_type}: {reason}").format(
|
|
82
|
+
entity_type=entity_type,
|
|
83
|
+
reason=e,
|
|
84
|
+
)
|
|
85
|
+
)
|
|
76
86
|
# Return a simple validator that accepts anything
|
|
77
87
|
return lambda x: x
|
|
78
88
|
|
ruyi/ruyipkg/entity_cli.py
CHANGED
|
@@ -2,6 +2,7 @@ import argparse
|
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from ..cli.cmd import RootCommand
|
|
5
|
+
from ..i18n import _
|
|
5
6
|
|
|
6
7
|
if TYPE_CHECKING:
|
|
7
8
|
from ..cli.completion import ArgumentParser
|
|
@@ -13,7 +14,7 @@ class EntityCommand(
|
|
|
13
14
|
cmd="entity",
|
|
14
15
|
has_subcommands=True,
|
|
15
16
|
is_experimental=True,
|
|
16
|
-
help="Interact with entities defined in the repositories",
|
|
17
|
+
help=_("Interact with entities defined in the repositories"),
|
|
17
18
|
):
|
|
18
19
|
@classmethod
|
|
19
20
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
|
@@ -23,13 +24,15 @@ class EntityCommand(
|
|
|
23
24
|
class EntityDescribeCommand(
|
|
24
25
|
EntityCommand,
|
|
25
26
|
cmd="describe",
|
|
26
|
-
help="Describe an entity",
|
|
27
|
+
help=_("Describe an entity"),
|
|
27
28
|
):
|
|
28
29
|
@classmethod
|
|
29
30
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
|
30
31
|
p.add_argument(
|
|
31
32
|
"ref",
|
|
32
|
-
help=
|
|
33
|
+
help=_(
|
|
34
|
+
"Reference to the entity to describe in the form of '<type>:<name>'"
|
|
35
|
+
),
|
|
33
36
|
)
|
|
34
37
|
|
|
35
38
|
@classmethod
|
|
@@ -40,30 +43,33 @@ class EntityDescribeCommand(
|
|
|
40
43
|
entity_store = cfg.repo.entity_store
|
|
41
44
|
entity = entity_store.get_entity_by_ref(ref)
|
|
42
45
|
if entity is None:
|
|
43
|
-
logger.F(
|
|
46
|
+
logger.F(_("entity [yellow]{ref}[/] not found").format(ref=ref))
|
|
44
47
|
return 1
|
|
45
48
|
|
|
46
49
|
logger.stdout(
|
|
47
|
-
|
|
50
|
+
_("Entity [bold]{entity}[/] ([green]{display_name}[/])\n").format(
|
|
51
|
+
entity=str(entity),
|
|
52
|
+
display_name=entity.display_name,
|
|
53
|
+
)
|
|
48
54
|
)
|
|
49
55
|
|
|
50
56
|
fwd_refs = entity.related_refs
|
|
51
57
|
if fwd_refs:
|
|
52
|
-
logger.stdout(" Direct forward relationships:")
|
|
58
|
+
logger.stdout(_(" Direct forward relationships:"))
|
|
53
59
|
for ref in sorted(fwd_refs):
|
|
54
60
|
logger.stdout(f" - [yellow]{ref}[/]")
|
|
55
61
|
else:
|
|
56
|
-
logger.stdout(" Direct forward relationships: [gray]none[/]")
|
|
62
|
+
logger.stdout(_(" Direct forward relationships: [gray]none[/]"))
|
|
57
63
|
|
|
58
64
|
rev_refs = entity.reverse_refs
|
|
59
65
|
if rev_refs:
|
|
60
|
-
logger.stdout(" Direct reverse relationships:")
|
|
66
|
+
logger.stdout(_(" Direct reverse relationships:"))
|
|
61
67
|
for ref in sorted(rev_refs):
|
|
62
68
|
logger.stdout(f" - [yellow]{ref}[/]")
|
|
63
69
|
else:
|
|
64
|
-
logger.stdout(" Direct reverse relationships: [gray]none[/]")
|
|
70
|
+
logger.stdout(_(" Direct reverse relationships: [gray]none[/]"))
|
|
65
71
|
|
|
66
|
-
logger.stdout(" All indirectly related entities:")
|
|
72
|
+
logger.stdout(_(" All indirectly related entities:"))
|
|
67
73
|
for e in entity_store.traverse_related_entities(
|
|
68
74
|
entity,
|
|
69
75
|
transitive=True,
|
|
@@ -81,7 +87,7 @@ class EntityDescribeCommand(
|
|
|
81
87
|
class EntityListCommand(
|
|
82
88
|
EntityCommand,
|
|
83
89
|
cmd="list",
|
|
84
|
-
help="List entities",
|
|
90
|
+
help=_("List entities"),
|
|
85
91
|
):
|
|
86
92
|
@classmethod
|
|
87
93
|
def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
|
|
@@ -91,7 +97,9 @@ class EntityListCommand(
|
|
|
91
97
|
action="append",
|
|
92
98
|
nargs=1,
|
|
93
99
|
dest="entity_type",
|
|
94
|
-
help=
|
|
100
|
+
help=_(
|
|
101
|
+
"List entities of this type. Can be passed multiple times to list multiple types."
|
|
102
|
+
),
|
|
95
103
|
)
|
|
96
104
|
|
|
97
105
|
@classmethod
|
ruyi/ruyipkg/entity_provider.py
CHANGED
|
@@ -10,6 +10,7 @@ if sys.version_info >= (3, 11):
|
|
|
10
10
|
else:
|
|
11
11
|
import tomli as tomllib
|
|
12
12
|
|
|
13
|
+
from ..i18n import _
|
|
13
14
|
from ..log import RuyiLogger
|
|
14
15
|
from ..utils.porcelain import PorcelainEntity, PorcelainEntityType
|
|
15
16
|
|
|
@@ -208,7 +209,10 @@ class FSEntityProvider(BaseEntityProvider):
|
|
|
208
209
|
schema_files = list(self._schemas_root.glob("*.jsonschema"))
|
|
209
210
|
except IOError as e:
|
|
210
211
|
self._logger.W(
|
|
211
|
-
|
|
212
|
+
_("failed to access entity schemas directory {dir}: {reason}").format(
|
|
213
|
+
dir=self._schemas_root,
|
|
214
|
+
reason=e,
|
|
215
|
+
)
|
|
212
216
|
)
|
|
213
217
|
return schemas
|
|
214
218
|
|
|
@@ -259,7 +263,12 @@ class FSEntityProvider(BaseEntityProvider):
|
|
|
259
263
|
with open(file_path, "rb") as f:
|
|
260
264
|
data = tomllib.load(f)
|
|
261
265
|
except (IOError, tomllib.TOMLDecodeError) as e:
|
|
262
|
-
self._logger.W(
|
|
266
|
+
self._logger.W(
|
|
267
|
+
_("failed to load entity from {path}: {reason}").format(
|
|
268
|
+
path=file_path,
|
|
269
|
+
reason=e,
|
|
270
|
+
)
|
|
271
|
+
)
|
|
263
272
|
continue
|
|
264
273
|
|
|
265
274
|
# Extract entity ID from filename (remove .toml extension)
|