ruyi 0.39.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 (101) hide show
  1. ruyi/__init__.py +21 -0
  2. ruyi/__main__.py +98 -0
  3. ruyi/cli/__init__.py +5 -0
  4. ruyi/cli/builtin_commands.py +14 -0
  5. ruyi/cli/cmd.py +224 -0
  6. ruyi/cli/completer.py +50 -0
  7. ruyi/cli/completion.py +26 -0
  8. ruyi/cli/config_cli.py +153 -0
  9. ruyi/cli/main.py +111 -0
  10. ruyi/cli/self_cli.py +295 -0
  11. ruyi/cli/user_input.py +127 -0
  12. ruyi/cli/version_cli.py +45 -0
  13. ruyi/config/__init__.py +401 -0
  14. ruyi/config/editor.py +92 -0
  15. ruyi/config/errors.py +76 -0
  16. ruyi/config/news.py +39 -0
  17. ruyi/config/schema.py +197 -0
  18. ruyi/device/__init__.py +0 -0
  19. ruyi/device/provision.py +591 -0
  20. ruyi/device/provision_cli.py +40 -0
  21. ruyi/log/__init__.py +272 -0
  22. ruyi/mux/.gitignore +1 -0
  23. ruyi/mux/__init__.py +0 -0
  24. ruyi/mux/runtime.py +213 -0
  25. ruyi/mux/venv/__init__.py +12 -0
  26. ruyi/mux/venv/emulator_cfg.py +41 -0
  27. ruyi/mux/venv/maker.py +782 -0
  28. ruyi/mux/venv/venv_cli.py +92 -0
  29. ruyi/mux/venv_cfg.py +214 -0
  30. ruyi/pluginhost/__init__.py +0 -0
  31. ruyi/pluginhost/api.py +206 -0
  32. ruyi/pluginhost/ctx.py +222 -0
  33. ruyi/pluginhost/paths.py +135 -0
  34. ruyi/pluginhost/plugin_cli.py +37 -0
  35. ruyi/pluginhost/unsandboxed.py +246 -0
  36. ruyi/py.typed +0 -0
  37. ruyi/resource_bundle/__init__.py +20 -0
  38. ruyi/resource_bundle/__main__.py +55 -0
  39. ruyi/resource_bundle/data.py +26 -0
  40. ruyi/ruyipkg/__init__.py +0 -0
  41. ruyi/ruyipkg/admin_checksum.py +88 -0
  42. ruyi/ruyipkg/admin_cli.py +83 -0
  43. ruyi/ruyipkg/atom.py +184 -0
  44. ruyi/ruyipkg/augmented_pkg.py +212 -0
  45. ruyi/ruyipkg/canonical_dump.py +320 -0
  46. ruyi/ruyipkg/checksum.py +39 -0
  47. ruyi/ruyipkg/cli_completion.py +42 -0
  48. ruyi/ruyipkg/distfile.py +208 -0
  49. ruyi/ruyipkg/entity.py +387 -0
  50. ruyi/ruyipkg/entity_cli.py +123 -0
  51. ruyi/ruyipkg/entity_provider.py +273 -0
  52. ruyi/ruyipkg/fetch.py +271 -0
  53. ruyi/ruyipkg/host.py +55 -0
  54. ruyi/ruyipkg/install.py +554 -0
  55. ruyi/ruyipkg/install_cli.py +150 -0
  56. ruyi/ruyipkg/list.py +126 -0
  57. ruyi/ruyipkg/list_cli.py +79 -0
  58. ruyi/ruyipkg/list_filter.py +173 -0
  59. ruyi/ruyipkg/msg.py +99 -0
  60. ruyi/ruyipkg/news.py +123 -0
  61. ruyi/ruyipkg/news_cli.py +78 -0
  62. ruyi/ruyipkg/news_store.py +183 -0
  63. ruyi/ruyipkg/pkg_manifest.py +657 -0
  64. ruyi/ruyipkg/profile.py +208 -0
  65. ruyi/ruyipkg/profile_cli.py +33 -0
  66. ruyi/ruyipkg/protocols.py +55 -0
  67. ruyi/ruyipkg/repo.py +763 -0
  68. ruyi/ruyipkg/state.py +345 -0
  69. ruyi/ruyipkg/unpack.py +369 -0
  70. ruyi/ruyipkg/unpack_method.py +91 -0
  71. ruyi/ruyipkg/update_cli.py +54 -0
  72. ruyi/telemetry/__init__.py +0 -0
  73. ruyi/telemetry/aggregate.py +72 -0
  74. ruyi/telemetry/event.py +41 -0
  75. ruyi/telemetry/node_info.py +192 -0
  76. ruyi/telemetry/provider.py +411 -0
  77. ruyi/telemetry/scope.py +43 -0
  78. ruyi/telemetry/store.py +238 -0
  79. ruyi/telemetry/telemetry_cli.py +127 -0
  80. ruyi/utils/__init__.py +0 -0
  81. ruyi/utils/ar.py +74 -0
  82. ruyi/utils/ci.py +63 -0
  83. ruyi/utils/frontmatter.py +38 -0
  84. ruyi/utils/git.py +169 -0
  85. ruyi/utils/global_mode.py +204 -0
  86. ruyi/utils/l10n.py +83 -0
  87. ruyi/utils/markdown.py +73 -0
  88. ruyi/utils/nuitka.py +33 -0
  89. ruyi/utils/porcelain.py +51 -0
  90. ruyi/utils/prereqs.py +77 -0
  91. ruyi/utils/ssl_patch.py +170 -0
  92. ruyi/utils/templating.py +34 -0
  93. ruyi/utils/toml.py +115 -0
  94. ruyi/utils/url.py +7 -0
  95. ruyi/utils/xdg_basedir.py +80 -0
  96. ruyi/version.py +67 -0
  97. ruyi-0.39.0.dist-info/LICENSE-Apache.txt +201 -0
  98. ruyi-0.39.0.dist-info/METADATA +403 -0
  99. ruyi-0.39.0.dist-info/RECORD +101 -0
  100. ruyi-0.39.0.dist-info/WHEEL +4 -0
  101. ruyi-0.39.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,92 @@
1
+ import argparse
2
+ import pathlib
3
+ from typing import TYPE_CHECKING
4
+
5
+ from ...cli.cmd import RootCommand
6
+
7
+ if TYPE_CHECKING:
8
+ from ...cli.completion import ArgumentParser
9
+ from ...config import GlobalConfig
10
+
11
+
12
+ class VenvCommand(
13
+ RootCommand,
14
+ cmd="venv",
15
+ help="Generate a virtual environment adapted to the chosen toolchain and profile",
16
+ ):
17
+ @classmethod
18
+ def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
19
+ p.add_argument("profile", type=str, help="Profile to use for the environment")
20
+ p.add_argument("dest", type=str, help="Path to the new virtual environment")
21
+ p.add_argument(
22
+ "--name",
23
+ "-n",
24
+ type=str,
25
+ default=None,
26
+ help="Override the venv's name",
27
+ )
28
+ p.add_argument(
29
+ "--toolchain",
30
+ "-t",
31
+ type=str,
32
+ action="append",
33
+ help="Specifier(s) (atoms) of the toolchain package(s) to use",
34
+ )
35
+ p.add_argument(
36
+ "--emulator",
37
+ "-e",
38
+ type=str,
39
+ help="Specifier (atom) of the emulator package to use",
40
+ )
41
+ p.add_argument(
42
+ "--with-sysroot",
43
+ action="store_true",
44
+ dest="with_sysroot",
45
+ default=True,
46
+ help="Provision a fresh sysroot inside the new virtual environment (default)",
47
+ )
48
+ p.add_argument(
49
+ "--without-sysroot",
50
+ action="store_false",
51
+ dest="with_sysroot",
52
+ help="Do not include a sysroot inside the new virtual environment",
53
+ )
54
+ p.add_argument(
55
+ "--sysroot-from",
56
+ type=str,
57
+ help="Specifier (atom) of the sysroot package to use, in favor of the toolchain-included one if applicable",
58
+ )
59
+ p.add_argument(
60
+ "--extra-commands-from",
61
+ type=str,
62
+ action="append",
63
+ help="Specifier(s) (atoms) of extra package(s) to add commands to the new virtual environment",
64
+ )
65
+
66
+ @classmethod
67
+ def main(cls, cfg: "GlobalConfig", args: argparse.Namespace) -> int:
68
+ from ...ruyipkg.host import get_native_host
69
+ from .maker import do_make_venv
70
+
71
+ profile_name: str = args.profile
72
+ dest = pathlib.Path(args.dest)
73
+ with_sysroot: bool = args.with_sysroot
74
+ override_name: str | None = args.name
75
+ tc_atoms_str: list[str] | None = args.toolchain
76
+ emu_atom_str: str | None = args.emulator
77
+ sysroot_atom_str: str | None = args.sysroot_from
78
+ extra_cmd_atoms_str: list[str] | None = args.extra_commands_from
79
+ host = str(get_native_host())
80
+
81
+ return do_make_venv(
82
+ cfg,
83
+ host,
84
+ profile_name,
85
+ dest,
86
+ with_sysroot,
87
+ override_name,
88
+ tc_atoms_str,
89
+ emu_atom_str,
90
+ sysroot_atom_str,
91
+ extra_cmd_atoms_str,
92
+ )
ruyi/mux/venv_cfg.py ADDED
@@ -0,0 +1,214 @@
1
+ import copy
2
+ import os.path
3
+ import pathlib
4
+ import sys
5
+ from typing import Any, TypedDict, TYPE_CHECKING, cast
6
+
7
+ if sys.version_info >= (3, 11):
8
+ import tomllib
9
+ else:
10
+ import tomli as tomllib
11
+
12
+ if TYPE_CHECKING:
13
+ from typing_extensions import NotRequired, Self
14
+
15
+
16
+ from ..log import RuyiLogger
17
+ from ..utils.global_mode import ProvidesGlobalMode
18
+
19
+
20
+ class VenvConfigType(TypedDict):
21
+ profile: str
22
+ sysroot: "NotRequired[str]"
23
+
24
+
25
+ class VenvConfigRootType(TypedDict):
26
+ config: VenvConfigType
27
+
28
+
29
+ class VenvCacheV0Type(TypedDict):
30
+ target_tuple: str
31
+ toolchain_bindir: str
32
+ gcc_install_dir: "NotRequired[str]"
33
+ profile_common_flags: str
34
+ qemu_bin: "NotRequired[str]"
35
+ profile_emu_env: "NotRequired[dict[str, str]]"
36
+
37
+
38
+ class VenvCacheV1TargetType(TypedDict):
39
+ toolchain_bindir: str
40
+ toolchain_sysroot: "NotRequired[str]"
41
+ gcc_install_dir: "NotRequired[str]"
42
+
43
+
44
+ class VenvCacheV2TargetType(VenvCacheV1TargetType):
45
+ toolchain_flags: str
46
+
47
+
48
+ class VenvCacheV1CmdMetadataEntryType(TypedDict):
49
+ dest: str
50
+ target_tuple: str
51
+
52
+
53
+ class VenvCacheV1Type(TypedDict):
54
+ profile_common_flags: str
55
+ profile_emu_env: "NotRequired[dict[str, str]]"
56
+ qemu_bin: "NotRequired[str]"
57
+ targets: dict[str, VenvCacheV1TargetType]
58
+ cmd_metadata_map: "NotRequired[dict[str, VenvCacheV1CmdMetadataEntryType]]"
59
+
60
+
61
+ class VenvCacheV2Type(TypedDict):
62
+ profile_emu_env: "NotRequired[dict[str, str]]"
63
+ qemu_bin: "NotRequired[str]"
64
+ targets: dict[str, VenvCacheV2TargetType]
65
+ cmd_metadata_map: "NotRequired[dict[str, VenvCacheV1CmdMetadataEntryType]]"
66
+
67
+
68
+ class VenvCacheRootType(TypedDict):
69
+ cached: "NotRequired[VenvCacheV0Type]"
70
+ cached_v1: "NotRequired[VenvCacheV1Type]"
71
+ cached_v2: "NotRequired[VenvCacheV2Type]"
72
+
73
+
74
+ def parse_venv_cache(
75
+ cache: VenvCacheRootType,
76
+ global_sysroot: str | None,
77
+ ) -> VenvCacheV2Type:
78
+ if "cached_v2" in cache:
79
+ return cache["cached_v2"]
80
+ if "cached_v1" in cache:
81
+ return upgrade_venv_cache_v1(cache["cached_v1"])
82
+ if "cached" in cache:
83
+ return upgrade_venv_cache_v0(cache["cached"], global_sysroot)
84
+ raise RuntimeError("unsupported venv cache version")
85
+
86
+
87
+ def upgrade_venv_cache_v1(x: VenvCacheV1Type) -> VenvCacheV2Type:
88
+ profile_common_flags = x["profile_common_flags"]
89
+ tmp = cast(dict[str, Any], copy.deepcopy(x))
90
+ del tmp["profile_common_flags"]
91
+ v2 = cast(VenvCacheV2Type, tmp)
92
+ for tgt in v2["targets"].values():
93
+ tgt["toolchain_flags"] = profile_common_flags
94
+ return v2
95
+
96
+
97
+ def upgrade_venv_cache_v0(
98
+ x: VenvCacheV0Type,
99
+ global_sysroot: str | None,
100
+ ) -> VenvCacheV2Type:
101
+ # v0 only supports one single target so upgrading is trivial
102
+ v1_target: VenvCacheV1TargetType = {
103
+ "toolchain_bindir": x["toolchain_bindir"],
104
+ }
105
+ if "gcc_install_dir" in x:
106
+ v1_target["gcc_install_dir"] = x["gcc_install_dir"]
107
+ if global_sysroot is not None:
108
+ v1_target["toolchain_sysroot"] = global_sysroot
109
+
110
+ y: VenvCacheV1Type = {
111
+ "profile_common_flags": x["profile_common_flags"],
112
+ "targets": {x["target_tuple"]: v1_target},
113
+ }
114
+ if "profile_emu_env" in x:
115
+ y["profile_emu_env"] = x["profile_emu_env"]
116
+ if "qemu_bin" in x:
117
+ y["qemu_bin"] = x["qemu_bin"]
118
+
119
+ return upgrade_venv_cache_v1(y)
120
+
121
+
122
+ class RuyiVenvConfig:
123
+ def __init__(
124
+ self,
125
+ venv_root: pathlib.Path,
126
+ cfg: VenvConfigRootType,
127
+ cache: VenvCacheRootType,
128
+ ) -> None:
129
+ self.venv_root = venv_root
130
+ self.profile = cfg["config"]["profile"]
131
+ self.sysroot = cfg["config"].get("sysroot")
132
+
133
+ parsed_cache = parse_venv_cache(cache, self.sysroot)
134
+ self.targets = parsed_cache["targets"]
135
+ self.qemu_bin = parsed_cache.get("qemu_bin")
136
+ self.profile_emu_env = parsed_cache.get("profile_emu_env")
137
+ self.cmd_metadata_map = parsed_cache.get("cmd_metadata_map")
138
+
139
+ # this must be in sync with provision.py
140
+ self._ruyi_priv_dir = self.venv_root / "ruyi-private"
141
+ self._cached_cmd_targets_dir = self._ruyi_priv_dir / "cached-cmd-targets"
142
+
143
+ @classmethod
144
+ def explicit_ruyi_venv_root(cls, gm: ProvidesGlobalMode) -> str | None:
145
+ return gm.venv_root
146
+
147
+ @classmethod
148
+ def probe_venv_root(cls, gm: ProvidesGlobalMode) -> pathlib.Path | None:
149
+ if explicit_root := cls.explicit_ruyi_venv_root(gm):
150
+ return pathlib.Path(explicit_root)
151
+
152
+ # check ../.. from argv[0]
153
+ # this only works if it contains a path separator, otherwise it's really
154
+ # hard without an explicit root (/proc/*/exe points to the resolved file,
155
+ # but we want the path to the first symlink without any symlink dereference)
156
+ argv0_path = gm.argv0
157
+ if os.path.sep not in argv0_path:
158
+ return None
159
+
160
+ implied_root = pathlib.Path(os.path.dirname(os.path.dirname(argv0_path)))
161
+ if (implied_root / "ruyi-venv.toml").exists():
162
+ return implied_root
163
+
164
+ return None
165
+
166
+ @classmethod
167
+ def load_from_venv(
168
+ cls,
169
+ gm: ProvidesGlobalMode,
170
+ logger: RuyiLogger,
171
+ ) -> "Self | None":
172
+ venv_root = cls.probe_venv_root(gm)
173
+ if venv_root is None:
174
+ return None
175
+
176
+ if cls.explicit_ruyi_venv_root(gm) is not None:
177
+ logger.D(f"using explicit venv root {venv_root}")
178
+ else:
179
+ logger.D(f"detected implicit venv root {venv_root}")
180
+
181
+ venv_config_path = venv_root / "ruyi-venv.toml"
182
+ with open(venv_config_path, "rb") as fp:
183
+ cfg: Any = tomllib.load(fp) # in order to cast to our stricter type
184
+
185
+ cache: Any # in order to cast to our stricter type
186
+ venv_cache_v2_path = venv_root / "ruyi-cache.v2.toml"
187
+ try:
188
+ with open(venv_cache_v2_path, "rb") as fp:
189
+ cache = tomllib.load(fp)
190
+ except FileNotFoundError:
191
+ venv_cache_v1_path = venv_root / "ruyi-cache.v1.toml"
192
+ try:
193
+ with open(venv_cache_v1_path, "rb") as fp:
194
+ cache = tomllib.load(fp)
195
+ except FileNotFoundError:
196
+ venv_cache_v0_path = venv_root / "ruyi-cache.toml"
197
+ with open(venv_cache_v0_path, "rb") as fp:
198
+ cache = tomllib.load(fp)
199
+
200
+ # NOTE: for now it's not prohibited to have cache data of a different
201
+ # version in a certain version's cache path, but this situation is
202
+ # harmless
203
+ return cls(venv_root, cfg, cache)
204
+
205
+ def resolve_cmd_metadata_with_cache(
206
+ self,
207
+ basename: str,
208
+ ) -> VenvCacheV1CmdMetadataEntryType | None:
209
+ if self.cmd_metadata_map is None:
210
+ # we are operating in a venv created with an older ruyi, thus no
211
+ # cmd_metadata_map in cache
212
+ return None
213
+
214
+ return self.cmd_metadata_map.get(basename)
File without changes
ruyi/pluginhost/api.py ADDED
@@ -0,0 +1,206 @@
1
+ from contextlib import AbstractContextManager
2
+ import pathlib
3
+ import subprocess
4
+ import sys
5
+ import time
6
+ from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast
7
+
8
+ if sys.version_info >= (3, 11):
9
+ import tomllib
10
+ else:
11
+ import tomli as tomllib
12
+
13
+ from rich.console import Console, RenderableType
14
+
15
+ from ..cli import user_input
16
+ from ..log import RuyiLogger
17
+ from ..version import RUYI_SEMVER
18
+ from .paths import resolve_ruyi_load_path
19
+
20
+ if TYPE_CHECKING:
21
+ from .ctx import PluginHostContext, SupportsEvalFunction, SupportsGetOption
22
+
23
+ T = TypeVar("T")
24
+ U = TypeVar("U")
25
+
26
+
27
+ class RuyiHostAPI:
28
+ def __init__(
29
+ self,
30
+ phctx: "PluginHostContext[SupportsGetOption, SupportsEvalFunction]",
31
+ this_file: pathlib.Path,
32
+ this_plugin_dir: pathlib.Path,
33
+ allow_host_fs_access: bool,
34
+ ) -> None:
35
+ self._phctx = phctx
36
+ self._this_file = this_file
37
+ self._this_plugin_dir = this_plugin_dir
38
+ self._ev = phctx.make_evaluator()
39
+ self._allow_host_fs_access = allow_host_fs_access
40
+
41
+ self._logger = RuyiPluginLogger(self._phctx.host_logger)
42
+ # TODO: unify into the plugin logger
43
+ self._host_logger = self._phctx.host_logger
44
+
45
+ @property
46
+ def ruyi_version(self) -> str:
47
+ return str(RUYI_SEMVER)
48
+
49
+ @property
50
+ def ruyi_plugin_api_rev(self) -> int:
51
+ return 1
52
+
53
+ def load_toml(self, path: str) -> object:
54
+ resolved_path = resolve_ruyi_load_path(
55
+ path,
56
+ self._phctx.plugin_root,
57
+ True,
58
+ self._this_file,
59
+ self._allow_host_fs_access,
60
+ )
61
+ with open(resolved_path, "rb") as f:
62
+ return tomllib.load(f)
63
+
64
+ @property
65
+ def log(self) -> "RuyiPluginLogger":
66
+ return self._logger
67
+
68
+ def cli_ask_for_choice(self, prompt: str, choice_texts: list[str]) -> int:
69
+ return user_input.ask_for_choice(self._host_logger, prompt, choice_texts)
70
+
71
+ def cli_ask_for_file(self, prompt: str) -> str:
72
+ return user_input.ask_for_file(self._host_logger, prompt)
73
+
74
+ def cli_ask_for_kv_choice(self, prompt: str, choices_kv: dict[str, str]) -> str:
75
+ return user_input.ask_for_kv_choice(self._host_logger, prompt, choices_kv)
76
+
77
+ def cli_ask_for_yesno_confirmation(
78
+ self,
79
+ prompt: str,
80
+ default: bool = False,
81
+ ) -> bool:
82
+ return user_input.ask_for_yesno_confirmation(self._host_logger, prompt, default)
83
+
84
+ def call_subprocess_argv(
85
+ self,
86
+ argv: list[str],
87
+ ) -> int:
88
+ # TODO: restrictions on this
89
+ return subprocess.call(argv)
90
+
91
+ def sleep(self, seconds: float, /) -> None:
92
+ return time.sleep(seconds)
93
+
94
+ def with_(
95
+ self,
96
+ cm: AbstractContextManager[T],
97
+ fn: object | Callable[[T], U],
98
+ ) -> U:
99
+ with cm as obj:
100
+ return cast(U, self._ev.eval_function(fn, obj))
101
+
102
+
103
+ def _ensure_str(message: RenderableType) -> None:
104
+ if not isinstance(message, str):
105
+ raise TypeError("message must be str in plugins")
106
+
107
+
108
+ class RuyiPluginLogger(RuyiLogger):
109
+ def __init__(self, host_logger: RuyiLogger) -> None:
110
+ self._h = host_logger
111
+
112
+ @property
113
+ def log_console(self) -> Console:
114
+ return self._h.log_console
115
+
116
+ def stdout(
117
+ self,
118
+ message: RenderableType,
119
+ *objects: Any,
120
+ sep: str = " ",
121
+ end: str = "\n",
122
+ ) -> None:
123
+ _ensure_str(message)
124
+ self._h.stdout(message, *objects, sep=sep, end=end)
125
+
126
+ def D(
127
+ self,
128
+ message: RenderableType,
129
+ *objects: Any,
130
+ sep: str = " ",
131
+ end: str = "\n",
132
+ _stack_offset_delta: int = 0,
133
+ ) -> None:
134
+ _ensure_str(message)
135
+ if _stack_offset_delta != 0:
136
+ raise ValueError("_stack_offset_delta is not supported in plugins")
137
+ self._h.D(message, *objects, sep=sep, end=end, _stack_offset_delta=1)
138
+
139
+ def W(
140
+ self,
141
+ message: RenderableType,
142
+ *objects: Any,
143
+ sep: str = " ",
144
+ end: str = "\n",
145
+ ) -> None:
146
+ _ensure_str(message)
147
+ self._h.W(message, *objects, sep=sep, end=end)
148
+
149
+ def I( # noqa: E743 # the name intentionally mimics Android logging for brevity
150
+ self,
151
+ message: RenderableType,
152
+ *objects: Any,
153
+ sep: str = " ",
154
+ end: str = "\n",
155
+ ) -> None:
156
+ _ensure_str(message)
157
+ self._h.I(message, *objects, sep=sep, end=end)
158
+
159
+ def F(
160
+ self,
161
+ message: RenderableType,
162
+ *objects: Any,
163
+ sep: str = " ",
164
+ end: str = "\n",
165
+ ) -> None:
166
+ _ensure_str(message)
167
+ self._h.F(message, *objects, sep=sep, end=end)
168
+
169
+
170
+ def _ruyi_plugin_rev(
171
+ phctx: "PluginHostContext[SupportsGetOption, SupportsEvalFunction]",
172
+ this_file: pathlib.Path,
173
+ this_plugin_dir: pathlib.Path,
174
+ allow_host_fs_access: bool,
175
+ rev: object,
176
+ ) -> RuyiHostAPI:
177
+ if not isinstance(rev, int):
178
+ raise TypeError("rev must be int in ruyi_plugin_rev calls")
179
+ if rev != 1:
180
+ raise ValueError(
181
+ f"Ruyi plugin API revision {rev} is not supported by this Ruyi"
182
+ )
183
+ return RuyiHostAPI(
184
+ phctx,
185
+ this_file,
186
+ this_plugin_dir,
187
+ allow_host_fs_access,
188
+ )
189
+
190
+
191
+ def make_ruyi_plugin_api_for_module(
192
+ phctx: "PluginHostContext[SupportsGetOption, SupportsEvalFunction]",
193
+ this_file: pathlib.Path,
194
+ this_plugin_dir: pathlib.Path,
195
+ is_cmd: bool,
196
+ ) -> Callable[[object], RuyiHostAPI]:
197
+ # Only allow access to host FS when we're being loaded as a command plugin
198
+ allow_host_fs_access = is_cmd
199
+
200
+ return lambda rev: _ruyi_plugin_rev(
201
+ phctx,
202
+ this_file,
203
+ this_plugin_dir,
204
+ allow_host_fs_access,
205
+ rev,
206
+ )