ruyi 0.42.0b20251014__tar.gz → 0.42.0b20251015__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/PKG-INFO +2 -2
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/pyproject.toml +2 -2
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/cli/oobe.py +7 -1
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/cli/self_cli.py +7 -2
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/config/__init__.py +1 -7
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/config/errors.py +10 -6
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/config/schema.py +67 -17
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/state.py +10 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/global_mode.py +19 -1
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/LICENSE-Apache.txt +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/README.md +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/__init__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/__main__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/cli/__init__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/cli/builtin_commands.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/cli/cmd.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/cli/completer.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/cli/completion.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/cli/config_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/cli/main.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/cli/user_input.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/cli/version_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/config/editor.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/config/news.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/device/__init__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/device/provision.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/device/provision_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/log/__init__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/mux/__init__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/mux/runtime.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/mux/venv/__init__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/mux/venv/emulator_cfg.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/mux/venv/maker.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/mux/venv/venv_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/mux/venv_cfg.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/pluginhost/__init__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/pluginhost/api.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/pluginhost/ctx.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/pluginhost/paths.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/pluginhost/plugin_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/pluginhost/unsandboxed.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/py.typed +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/resource_bundle/__init__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/resource_bundle/__main__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/resource_bundle/data.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/__init__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/admin_checksum.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/admin_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/atom.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/augmented_pkg.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/canonical_dump.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/checksum.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/cli_completion.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/distfile.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/entity.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/entity_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/entity_provider.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/fetcher.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/host.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/install.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/install_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/list.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/list_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/list_filter.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/msg.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/news.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/news_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/news_store.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/pkg_manifest.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/profile.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/profile_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/protocols.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/repo.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/unpack.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/unpack_method.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/ruyipkg/update_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/telemetry/__init__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/telemetry/aggregate.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/telemetry/event.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/telemetry/node_info.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/telemetry/provider.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/telemetry/scope.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/telemetry/store.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/telemetry/telemetry_cli.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/__init__.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/ar.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/ci.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/frontmatter.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/git.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/l10n.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/markdown.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/mounts.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/nuitka.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/porcelain.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/prereqs.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/ssl_patch.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/templating.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/toml.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/url.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/utils/xdg_basedir.py +0 -0
- {ruyi-0.42.0b20251014 → ruyi-0.42.0b20251015}/ruyi/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ruyi
|
|
3
|
-
Version: 0.42.
|
|
3
|
+
Version: 0.42.0b20251015
|
|
4
4
|
Summary: Package manager for RuyiSDK
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -221,7 +221,7 @@ Classifier: Topic :: Software Development :: Build Tools
|
|
|
221
221
|
Classifier: Topic :: Software Development :: Embedded Systems
|
|
222
222
|
Classifier: Topic :: System :: Software Distribution
|
|
223
223
|
Classifier: Typing :: Typed
|
|
224
|
-
Requires-Dist: argcomplete (>=2.0.0
|
|
224
|
+
Requires-Dist: argcomplete (>=2.0.0)
|
|
225
225
|
Requires-Dist: arpy
|
|
226
226
|
Requires-Dist: fastjsonschema (>=2.15.1)
|
|
227
227
|
Requires-Dist: jinja2 (>=3,<4)
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ruyi"
|
|
7
|
-
version = "0.42.0-beta.
|
|
7
|
+
version = "0.42.0-beta.20251015"
|
|
8
8
|
description = "Package manager for RuyiSDK"
|
|
9
9
|
keywords = ["ruyi", "ruyisdk"]
|
|
10
10
|
license = { file = "LICENSE-Apache.txt" }
|
|
@@ -29,6 +29,7 @@ classifiers = [
|
|
|
29
29
|
]
|
|
30
30
|
requires-python = ">=3.10"
|
|
31
31
|
dependencies = [
|
|
32
|
+
"argcomplete>=2.0.0",
|
|
32
33
|
"arpy",
|
|
33
34
|
"fastjsonschema>=2.15.1",
|
|
34
35
|
"jinja2 (>=3, <4)",
|
|
@@ -41,7 +42,6 @@ dependencies = [
|
|
|
41
42
|
"tomlkit>=0.9",
|
|
42
43
|
"tomli>=1.2; python_version<'3.11'",
|
|
43
44
|
"tzdata; sys_platform=='win32'",
|
|
44
|
-
"argcomplete (>=2.0.0,<4.0.0)",
|
|
45
45
|
]
|
|
46
46
|
|
|
47
47
|
[project.scripts]
|
|
@@ -41,10 +41,16 @@ class OOBE:
|
|
|
41
41
|
def should_prompt(self) -> bool:
|
|
42
42
|
from ..utils.global_mode import is_env_var_truthy
|
|
43
43
|
|
|
44
|
+
if not sys.stdin.isatty() or not sys.stdout.isatty():
|
|
45
|
+
# This is of higher priority than even the debug override, because
|
|
46
|
+
# we don't want to mess up non-interactive sessions even in case of
|
|
47
|
+
# debugging.
|
|
48
|
+
return False
|
|
49
|
+
|
|
44
50
|
if is_env_var_truthy(os.environ, "RUYI_DEBUG_FORCE_FIRST_RUN"):
|
|
45
51
|
return True
|
|
46
52
|
|
|
47
|
-
return self.is_first_run()
|
|
53
|
+
return self.is_first_run()
|
|
48
54
|
|
|
49
55
|
def maybe_prompt(self) -> None:
|
|
50
56
|
if not self.should_prompt():
|
|
@@ -131,12 +131,16 @@ class SelfCleanCommand(
|
|
|
131
131
|
_do_reset(
|
|
132
132
|
cfg,
|
|
133
133
|
quiet=quiet,
|
|
134
|
+
# state-related
|
|
135
|
+
all_state=all,
|
|
136
|
+
news_read_status=news_read_status,
|
|
137
|
+
telemetry=telemetry,
|
|
138
|
+
# cache-related
|
|
139
|
+
all_cache=all,
|
|
134
140
|
distfiles=distfiles,
|
|
135
141
|
installed_pkgs=installed_pkgs,
|
|
136
|
-
news_read_status=news_read_status,
|
|
137
142
|
progcache=progcache,
|
|
138
143
|
repo=repo,
|
|
139
|
-
telemetry=telemetry,
|
|
140
144
|
)
|
|
141
145
|
|
|
142
146
|
return 0
|
|
@@ -233,6 +237,7 @@ def _do_reset(
|
|
|
233
237
|
if installed_pkgs:
|
|
234
238
|
status("removing installed packages")
|
|
235
239
|
shutil.rmtree(cfg.data_root, True)
|
|
240
|
+
cfg.ruyipkg_global_state.purge_installation_info()
|
|
236
241
|
|
|
237
242
|
# do not record any telemetry data if we're purging it
|
|
238
243
|
if all_state or telemetry:
|
|
@@ -166,13 +166,7 @@ class GlobalConfig:
|
|
|
166
166
|
attr_name = self._get_attr_name_by_key(section, sel)
|
|
167
167
|
if attr_name is None:
|
|
168
168
|
raise errors.InvalidConfigKeyError(key)
|
|
169
|
-
|
|
170
|
-
expected_type = schema.get_expected_type_for_config_key(key)
|
|
171
|
-
if not isinstance(value, expected_type):
|
|
172
|
-
raise TypeError(
|
|
173
|
-
f"expected type {expected_type.__name__} for config key '{key}', got {type(value).__name__}"
|
|
174
|
-
)
|
|
175
|
-
|
|
169
|
+
schema.ensure_valid_config_kv(key, True, value)
|
|
176
170
|
setattr(self, attr_name, value)
|
|
177
171
|
|
|
178
172
|
@classmethod
|
|
@@ -31,7 +31,7 @@ class InvalidConfigValueTypeError(TypeError):
|
|
|
31
31
|
self,
|
|
32
32
|
key: str | Sequence[str],
|
|
33
33
|
val: object | None,
|
|
34
|
-
expected: type,
|
|
34
|
+
expected: type | Sequence[type],
|
|
35
35
|
) -> None:
|
|
36
36
|
super().__init__()
|
|
37
37
|
self._key = key
|
|
@@ -48,20 +48,24 @@ class InvalidConfigValueTypeError(TypeError):
|
|
|
48
48
|
class InvalidConfigValueError(ValueError):
|
|
49
49
|
def __init__(
|
|
50
50
|
self,
|
|
51
|
-
key: str | Sequence[str] |
|
|
51
|
+
key: str | Sequence[str] | None,
|
|
52
52
|
val: object | None,
|
|
53
|
+
typ: type | Sequence[type],
|
|
53
54
|
) -> None:
|
|
54
55
|
super().__init__()
|
|
55
56
|
self._key = key
|
|
56
57
|
self._val = val
|
|
58
|
+
self._typ = typ
|
|
57
59
|
|
|
58
60
|
def __str__(self) -> str:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
return (
|
|
62
|
+
f"invalid config value for key {self._key} (type {self._typ}): {self._val}"
|
|
63
|
+
)
|
|
62
64
|
|
|
63
65
|
def __repr__(self) -> str:
|
|
64
|
-
return
|
|
66
|
+
return (
|
|
67
|
+
f"InvalidConfigValueError({self._key:!r}, {self._val:!r}, {self._typ:!r})"
|
|
68
|
+
)
|
|
65
69
|
|
|
66
70
|
|
|
67
71
|
class MalformedConfigFileError(Exception):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import sys
|
|
3
|
-
from typing import Final, Sequence
|
|
3
|
+
from typing import Final, Sequence, TypeGuard
|
|
4
4
|
|
|
5
5
|
from .errors import (
|
|
6
6
|
InvalidConfigKeyError,
|
|
@@ -43,7 +43,7 @@ def validate_section(section: str) -> None:
|
|
|
43
43
|
raise InvalidConfigSectionError(section)
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
def get_expected_type_for_config_key(key: str | Sequence[str]) -> type:
|
|
46
|
+
def get_expected_type_for_config_key(key: str | Sequence[str]) -> type | Sequence[type]:
|
|
47
47
|
parsed_key = parse_config_key(key)
|
|
48
48
|
if len(parsed_key) != 2:
|
|
49
49
|
# for now there's no nested config option
|
|
@@ -87,17 +87,29 @@ def _get_expected_type_for_section_repo(sel: str) -> type:
|
|
|
87
87
|
raise InvalidConfigKeyError(sel)
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
def _get_expected_type_for_section_telemetry(sel: str) -> type:
|
|
90
|
+
def _get_expected_type_for_section_telemetry(sel: str) -> type | tuple[type, ...]:
|
|
91
91
|
if sel == KEY_TELEMETRY_MODE:
|
|
92
92
|
return str
|
|
93
93
|
elif sel == KEY_TELEMETRY_PM_TELEMETRY_URL:
|
|
94
94
|
return str
|
|
95
95
|
elif sel == KEY_TELEMETRY_UPLOAD_CONSENT:
|
|
96
|
-
return datetime.datetime
|
|
96
|
+
return (type(None), datetime.datetime)
|
|
97
97
|
else:
|
|
98
98
|
raise InvalidConfigKeyError(sel)
|
|
99
99
|
|
|
100
100
|
|
|
101
|
+
def _is_all_str(obj: object) -> TypeGuard[Sequence[str]]:
|
|
102
|
+
if not isinstance(obj, Sequence):
|
|
103
|
+
return False
|
|
104
|
+
return all(isinstance(i, str) for i in obj)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _is_all_type(obj: object) -> TypeGuard[Sequence[type]]:
|
|
108
|
+
if not isinstance(obj, Sequence):
|
|
109
|
+
return False
|
|
110
|
+
return all(isinstance(i, type) for i in obj)
|
|
111
|
+
|
|
112
|
+
|
|
101
113
|
def ensure_valid_config_kv(
|
|
102
114
|
key: str | Sequence[str],
|
|
103
115
|
check_val: bool = False,
|
|
@@ -108,9 +120,9 @@ def ensure_valid_config_kv(
|
|
|
108
120
|
# for now there's no nested config option
|
|
109
121
|
raise InvalidConfigKeyError(key)
|
|
110
122
|
|
|
111
|
-
|
|
123
|
+
expected_types = get_expected_type_for_config_key(parsed_key)
|
|
112
124
|
# validity of config key is already checked by get_expected_type_for_config_key
|
|
113
|
-
|
|
125
|
+
ensure_value_type(key, check_val, val, expected_types)
|
|
114
126
|
|
|
115
127
|
if not check_val:
|
|
116
128
|
return
|
|
@@ -120,14 +132,26 @@ def ensure_valid_config_kv(
|
|
|
120
132
|
return _extra_validate_section_telemetry_kv(key, sel, val)
|
|
121
133
|
|
|
122
134
|
|
|
123
|
-
def
|
|
135
|
+
def ensure_value_type(
|
|
124
136
|
key: str | Sequence[str],
|
|
125
137
|
check_val: bool,
|
|
126
138
|
val: object | None,
|
|
127
|
-
expected: type,
|
|
139
|
+
expected: type | Sequence[type],
|
|
128
140
|
) -> None:
|
|
129
|
-
if
|
|
130
|
-
|
|
141
|
+
if not check_val:
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
expected_types: tuple[type, ...]
|
|
145
|
+
if isinstance(expected, type):
|
|
146
|
+
expected_types = (expected,)
|
|
147
|
+
else:
|
|
148
|
+
expected_types = tuple(expected)
|
|
149
|
+
|
|
150
|
+
for ty in expected_types:
|
|
151
|
+
if isinstance(val, ty):
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
raise InvalidConfigValueTypeError(key, val, expected)
|
|
131
155
|
|
|
132
156
|
|
|
133
157
|
def _extra_validate_section_telemetry_kv(
|
|
@@ -138,7 +162,7 @@ def _extra_validate_section_telemetry_kv(
|
|
|
138
162
|
if sel == KEY_TELEMETRY_MODE:
|
|
139
163
|
# value type is already ensured earlier
|
|
140
164
|
if val not in ("local", "off", "on"):
|
|
141
|
-
raise InvalidConfigValueError(key, val)
|
|
165
|
+
raise InvalidConfigValueError(key, val, str)
|
|
142
166
|
|
|
143
167
|
|
|
144
168
|
def encode_value(v: object) -> str:
|
|
@@ -169,24 +193,50 @@ def encode_value(v: object) -> str:
|
|
|
169
193
|
|
|
170
194
|
|
|
171
195
|
def decode_value(
|
|
172
|
-
key: str | Sequence[str]
|
|
196
|
+
key: str | Sequence[str],
|
|
173
197
|
val: str,
|
|
174
198
|
) -> object:
|
|
175
199
|
"""Decodes the given string representation of a config value into a Python
|
|
176
200
|
value, directed by type information implied by the config key."""
|
|
177
201
|
|
|
178
202
|
if isinstance(key, type):
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
203
|
+
return _decode_single_type_value(None, val, key)
|
|
204
|
+
elif _is_all_type(key):
|
|
205
|
+
return _decode_typed_value(None, val, key)
|
|
206
|
+
|
|
207
|
+
assert isinstance(key, str) or _is_all_str(key)
|
|
208
|
+
expected_types = get_expected_type_for_config_key(key)
|
|
209
|
+
|
|
210
|
+
if isinstance(expected_types, type):
|
|
211
|
+
return _decode_single_type_value(key, val, expected_types)
|
|
212
|
+
return _decode_typed_value(key, val, expected_types)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _decode_typed_value(
|
|
216
|
+
key: str | Sequence[str] | None,
|
|
217
|
+
val: str,
|
|
218
|
+
expected_types: Sequence[type],
|
|
219
|
+
) -> object:
|
|
220
|
+
for ty in expected_types:
|
|
221
|
+
try:
|
|
222
|
+
return _decode_single_type_value(key, val, ty)
|
|
223
|
+
except (RuntimeError, TypeError, ValueError):
|
|
224
|
+
continue
|
|
225
|
+
raise InvalidConfigValueError(key, val, expected_types)
|
|
226
|
+
|
|
182
227
|
|
|
228
|
+
def _decode_single_type_value(
|
|
229
|
+
key: str | Sequence[str] | None,
|
|
230
|
+
val: str,
|
|
231
|
+
expected_type: type,
|
|
232
|
+
) -> object:
|
|
183
233
|
if expected_type is bool:
|
|
184
234
|
if val in ("true", "yes", "1"):
|
|
185
235
|
return True
|
|
186
236
|
elif val in ("false", "no", "0"):
|
|
187
237
|
return False
|
|
188
238
|
else:
|
|
189
|
-
raise InvalidConfigValueError(key, val)
|
|
239
|
+
raise InvalidConfigValueError(key, val, expected_type)
|
|
190
240
|
elif expected_type is int:
|
|
191
241
|
return int(val, 10)
|
|
192
242
|
elif expected_type is str:
|
|
@@ -199,4 +249,4 @@ def decode_value(
|
|
|
199
249
|
v = datetime.datetime.fromisoformat(val)
|
|
200
250
|
return v.astimezone() if v.tzinfo is None else v
|
|
201
251
|
else:
|
|
202
|
-
raise NotImplementedError(f"
|
|
252
|
+
raise NotImplementedError(f"unhandled type for config value: {expected_type}")
|
|
@@ -76,6 +76,16 @@ class RuyipkgGlobalStateStore:
|
|
|
76
76
|
"""Ensure the state directory exists."""
|
|
77
77
|
self.root.mkdir(parents=True, exist_ok=True)
|
|
78
78
|
|
|
79
|
+
def purge_installation_info(self) -> None:
|
|
80
|
+
"""Purge installation records."""
|
|
81
|
+
self._installs_file.unlink(missing_ok=True)
|
|
82
|
+
self._installs_cache = None
|
|
83
|
+
# if the state dir is empty, remove it
|
|
84
|
+
try:
|
|
85
|
+
self.root.rmdir()
|
|
86
|
+
except OSError:
|
|
87
|
+
pass
|
|
88
|
+
|
|
79
89
|
def _load_installs(self) -> dict[str, PackageInstallationInfo]:
|
|
80
90
|
"""Load installation records from disk."""
|
|
81
91
|
if self._installs_cache is not None:
|
|
@@ -128,6 +128,24 @@ def _guess_porcelain_from_argv(argv: list[str]) -> bool:
|
|
|
128
128
|
return len(argv) > 1 and argv[1] == "--porcelain"
|
|
129
129
|
|
|
130
130
|
|
|
131
|
+
def _probe_cli_autocomplete(env: Mapping[str, str], argv: list[str]) -> bool:
|
|
132
|
+
"""
|
|
133
|
+
Probe if the current invocation is related to shell completion based on
|
|
134
|
+
the arguments passed, without requiring the ``argparse`` machinery to be
|
|
135
|
+
completely initialized, and the environment.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
# If `--output-completion-script` is present anywhere in the arguments,
|
|
139
|
+
# then this is related to shell completion even if _ARGCOMPLETE is not yet
|
|
140
|
+
# set (which is only set for invocations after the shell finished sourcing
|
|
141
|
+
# the completion script).
|
|
142
|
+
for arg in argv:
|
|
143
|
+
if arg.startswith("--output-completion-script"):
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
return "_ARGCOMPLETE" in os.environ
|
|
147
|
+
|
|
148
|
+
|
|
131
149
|
class EnvGlobalModeProvider(GlobalModeProvider):
|
|
132
150
|
def __init__(
|
|
133
151
|
self,
|
|
@@ -150,7 +168,7 @@ class EnvGlobalModeProvider(GlobalModeProvider):
|
|
|
150
168
|
|
|
151
169
|
# We have to lift this piece of implementation detail out of argcomplete,
|
|
152
170
|
# as the argcomplete import is very costly in terms of startup time.
|
|
153
|
-
self._is_cli_autocomplete =
|
|
171
|
+
self._is_cli_autocomplete = _probe_cli_autocomplete(env, argv)
|
|
154
172
|
|
|
155
173
|
self._venv_root = env.get(ENV_VENV_ROOT_KEY)
|
|
156
174
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|