Skip to content

Commit 6f3e1d5

Browse files
authored
Feature: 实现基于项目管理器的依赖管理 (#183)
* ♻️ add `Requirement` support for raw dependency management * ✨ add basic environment manager support * ♻️ refactor environment manager methods to use a common run function * ♻️ refactor probe_environment_manager to limit available managers to current and pip * ♻️ refactor environment handler imports and enhance EnvironmentExecutor with new methods * ✨ add as_requirement method to PackageInfo for enhanced dependency management * ♻️ refactor adapter, driver, and plugin commands to utilize EnvironmentExecutor for dependency management * 🌐 update localization * ✨ enhance dependency management with support for dependency groups * ♻️ update doc for EnvironmentExecutor and remove unused parameter types * ✨ enhance install method in environment executors to support development dependencies * ✨ enhance project creation process to support environment managers and improve dependency installation * 🌐 add new translations * ♻️ rename parameter 'name' to 'manager_name' in EnvironmentExecutor subclasses due to conflict with ABCMeta * ➕ add pip to project dependencies
1 parent 3b87cc1 commit 6f3e1d5

12 files changed

Lines changed: 951 additions & 429 deletions

File tree

nb_cli/cli/commands/adapter.py

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88
from nb_cli import _
99
from nb_cli.config import GLOBAL_CONFIG
10-
from nb_cli.exceptions import NoSelectablePackageError
1110
from nb_cli.cli.utils import find_exact_package, format_package_results
11+
from nb_cli.exceptions import ProcessExecutionError, NoSelectablePackageError
1212
from nb_cli.cli import (
1313
CLI_DEFAULT_STYLE,
1414
ClickAliasedGroup,
@@ -18,11 +18,9 @@
1818
run_async,
1919
)
2020
from nb_cli.handlers import (
21+
EnvironmentExecutor,
2122
list_adapters,
2223
create_adapter,
23-
call_pip_update,
24-
call_pip_install,
25-
call_pip_uninstall,
2624
list_installed_adapters,
2725
)
2826

@@ -205,11 +203,13 @@ async def install(
205203
fg="yellow",
206204
)
207205

208-
proc = await call_pip_install(
209-
adapter.as_dependency(extras=extras, versioned=not no_restrict_version),
210-
pip_args,
211-
)
212-
if await proc.wait() != 0:
206+
executor = await EnvironmentExecutor.get()
207+
try:
208+
await executor.install(
209+
adapter.as_requirement(extras=extras, versioned=not no_restrict_version),
210+
extra_args=pip_args or (),
211+
)
212+
except ProcessExecutionError:
213213
click.secho(
214214
_(
215215
"Errors occurred in installing adapter {adapter.name}\n"
@@ -218,8 +218,7 @@ async def install(
218218
).format(adapter=adapter),
219219
fg="red",
220220
)
221-
assert proc.returncode
222-
ctx.exit(proc.returncode)
221+
ctx.exit(1)
223222

224223
try:
225224
GLOBAL_CONFIG.add_adapter(adapter)
@@ -230,17 +229,6 @@ async def install(
230229
)
231230
)
232231

233-
try:
234-
GLOBAL_CONFIG.add_dependency(
235-
adapter.as_dependency(extras=extras, versioned=not no_restrict_version)
236-
)
237-
except RuntimeError as e:
238-
click.echo(
239-
_("Failed to add adapter {adapter.name} to dependencies: {e}").format(
240-
adapter=adapter, e=e
241-
)
242-
)
243-
244232

245233
@adapter.command(
246234
context_settings={"ignore_unknown_options": True}, help=_("Update nonebot adapter.")
@@ -281,8 +269,13 @@ async def update(
281269
fg="yellow",
282270
)
283271

284-
proc = await call_pip_update(adapter.project_link, pip_args)
285-
if await proc.wait() != 0:
272+
executor = await EnvironmentExecutor.get()
273+
try:
274+
await executor.install(
275+
adapter.as_requirement(versioned=False),
276+
extra_args=pip_args or (),
277+
)
278+
except ProcessExecutionError:
286279
click.secho(
287280
_("Errors occurred in updating adapter {adapter.name}. Aborted.").format(
288281
adapter=adapter
@@ -291,15 +284,6 @@ async def update(
291284
)
292285
return
293286

294-
try:
295-
GLOBAL_CONFIG.update_dependency(adapter)
296-
except RuntimeError as e:
297-
click.echo(
298-
_("Failed to update adapter {adapter.name} to dependencies: {e}").format(
299-
adapter=adapter, e=e
300-
)
301-
)
302-
303287

304288
@adapter.command(
305289
aliases=["remove"],
@@ -310,6 +294,11 @@ async def update(
310294
@click.argument("pip_args", nargs=-1, default=None)
311295
@run_async
312296
async def uninstall(name: str | None, pip_args: list[str] | None):
297+
extras: str | None = None
298+
if name and "[" in name:
299+
name, extras = name.split("[", 1)
300+
extras = extras.rstrip("]")
301+
313302
try:
314303
adapter = await find_exact_package(
315304
_("Adapter name to uninstall:"),
@@ -322,20 +311,8 @@ async def uninstall(name: str | None, pip_args: list[str] | None):
322311
click.echo(_("No installed adapter found to uninstall."))
323312
return
324313

325-
extras: str | None = None
326-
if name and "[" in name:
327-
name, extras = name.split("[", 1)
328-
extras = extras.rstrip("]")
329-
330314
try:
331-
if extras is not None:
332-
if not GLOBAL_CONFIG.remove_dependency(
333-
adapter.as_dependency(extras=extras)
334-
):
335-
return
336315
can_uninstall = GLOBAL_CONFIG.remove_adapter(adapter)
337-
if can_uninstall:
338-
GLOBAL_CONFIG.remove_dependency(adapter)
339316
except RuntimeError as e:
340317
click.echo(
341318
_("Failed to remove adapter {adapter.name} from config: {e}").format(
@@ -345,8 +322,11 @@ async def uninstall(name: str | None, pip_args: list[str] | None):
345322
return
346323

347324
if can_uninstall:
348-
proc = await call_pip_uninstall(adapter.project_link, pip_args)
349-
await proc.wait()
325+
executor = await EnvironmentExecutor.get()
326+
await executor.uninstall(
327+
adapter.as_requirement(extras=extras, versioned=False),
328+
extra_args=pip_args or (),
329+
)
350330

351331

352332
@adapter.command(aliases=["new"], help=_("Create a new nonebot adapter."))

nb_cli/cli/commands/driver.py

Lines changed: 32 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
from typing import cast
22

33
import click
4+
from packaging.requirements import Requirement
45
from noneprompt import Choice, ListPrompt, InputPrompt, CancelledError
56

67
from nb_cli import _
7-
from nb_cli.config import GLOBAL_CONFIG
8+
from nb_cli.exceptions import ProcessExecutionError
9+
from nb_cli.handlers import EnvironmentExecutor, list_drivers
810
from nb_cli.cli.utils import find_exact_package, format_package_results
9-
from nb_cli.handlers import (
10-
list_drivers,
11-
call_pip_update,
12-
call_pip_install,
13-
call_pip_uninstall,
14-
)
1511
from nb_cli.cli import (
1612
CLI_DEFAULT_STYLE,
1713
ClickAliasedGroup,
@@ -153,25 +149,20 @@ async def install(
153149
fg="yellow",
154150
)
155151

156-
if driver.project_link:
157-
proc = await call_pip_install(driver.project_link, pip_args)
158-
if await proc.wait() != 0:
159-
click.secho(
160-
_(
161-
"Errors occurred in installing driver {driver.name}. Aborted."
162-
).format(driver=driver),
163-
fg="red",
164-
)
165-
return
166-
152+
executor = await EnvironmentExecutor.get()
167153
try:
168-
GLOBAL_CONFIG.add_dependency(driver)
169-
except RuntimeError as e:
170-
click.echo(
171-
_("Failed to add driver {driver.name} to dependencies: {e}").format(
172-
driver=driver, e=e
173-
)
154+
await executor.install(
155+
driver.as_requirement() if driver.project_link else Requirement("nonebot2"),
156+
extra_args=pip_args or (),
174157
)
158+
except ProcessExecutionError:
159+
click.secho(
160+
_("Errors occurred in installing driver {driver.name}. Aborted.").format(
161+
driver=driver
162+
),
163+
fg="red",
164+
)
165+
return
175166

176167

177168
@driver.command(
@@ -210,25 +201,20 @@ async def update(
210201
fg="yellow",
211202
)
212203

213-
if driver.project_link:
214-
proc = await call_pip_update(driver.project_link, pip_args)
215-
if await proc.wait() != 0:
216-
click.secho(
217-
_("Errors occurred in updating driver {driver.name}. Aborted.").format(
218-
driver=driver
219-
),
220-
fg="red",
221-
)
222-
return
223-
204+
executor = await EnvironmentExecutor.get()
224205
try:
225-
GLOBAL_CONFIG.update_dependency(driver)
226-
except RuntimeError as e:
227-
click.echo(
228-
_("Failed to update driver {driver.name} to dependencies: {e}").format(
229-
driver=driver, e=e
230-
)
206+
await executor.install(
207+
driver.as_requirement() if driver.project_link else Requirement("nonebot2"),
208+
extra_args=pip_args or (),
209+
)
210+
except ProcessExecutionError:
211+
click.secho(
212+
_("Errors occurred in updating driver {driver.name}. Aborted.").format(
213+
driver=driver
214+
),
215+
fg="red",
231216
)
217+
return
232218

233219

234220
@driver.command(
@@ -251,19 +237,9 @@ async def uninstall(name: str | None, pip_args: list[str] | None):
251237
except CancelledError:
252238
return
253239

254-
try:
255-
GLOBAL_CONFIG.remove_dependency(driver)
256-
GLOBAL_CONFIG.add_dependency("nonebot2") # hold a nonebot2 package
257-
except RuntimeError as e:
258-
click.echo(
259-
_("Failed to remove driver {driver.name} from dependencies: {e}").format(
260-
driver=driver, e=e
261-
)
240+
if driver.project_link:
241+
executor = await EnvironmentExecutor.get()
242+
await executor.uninstall(
243+
driver.as_requirement(versioned=False), extra_args=pip_args or ()
262244
)
263-
264-
if package := driver.project_link:
265-
if package.startswith("nonebot2[") and package.endswith("]"):
266-
package = package[9:-1]
267-
268-
proc = await call_pip_uninstall(package, pip_args)
269-
await proc.wait()
245+
await executor.install(Requirement("nonebot2"), extra_args=pip_args or ())

0 commit comments

Comments
 (0)