From 1bb809a6e2af096c8d28e5e23e21e5f44145c4ff Mon Sep 17 00:00:00 2001 From: Alex Myers Date: Fri, 11 Jul 2025 17:20:57 -0500 Subject: [PATCH] reckless: add uv python env installation method uv is a python installation and package manager written in rust. We can use it to quickly install python package dependencies and configure our plugin's python virtual environment. To maintain consistency with our other reckless python installations, the venv is still activated in a wrapper which then imports the original python source. Changelog-added: reckless can now install python plugins using the uv package manager. --- tools/reckless | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tools/reckless b/tools/reckless index 880ea8d34..c33b4ce79 100755 --- a/tools/reckless +++ b/tools/reckless @@ -986,6 +986,37 @@ def cargo_installation(cloned_plugin: InstInfo): return cloned_plugin +def install_python_uv(cloned_plugin: InstInfo): + """This uses the rust-based python plugin manager uv to manage the python + installation and create a virtual environment.""" + + source = Path(cloned_plugin.source_loc) / 'source' / cloned_plugin.name + # This virtual env path matches the other python installations and allows + # creating the wrapper in the same manner. Otherwise uv would build it in + # the source/{name} subdirectory. + cloned_plugin.venv = Path('.venv') + + # We want the virtual env at the head of our directory structure and uv + # will need a pyproject.toml there in order to get started. + (Path(cloned_plugin.source_loc) / 'pyproject.toml').\ + symlink_to(source / 'pyproject.toml') + + call = ['uv', '-v', 'sync'] + uv = run(call, cwd=str(cloned_plugin.source_loc), stdout=PIPE, stderr=PIPE, + text=True, check=False) + if uv.returncode != 0: + for line in uv.stderr.splitlines(): + log.debug(line) + log.error('Failed to install virtual environment') + raise InstallationFailure('Failed to create virtual environment!') + + # Delete entrypoint symlink so that a venv wrapper can take it's place + (Path(cloned_plugin.source_loc) / cloned_plugin.entry).unlink() + + create_wrapper(cloned_plugin) + return cloned_plugin + + python3venv = Installer('python3venv', exe='python3', manager='pip', entry='{name}.py') python3venv.add_entrypoint('{name}') @@ -997,6 +1028,7 @@ poetryvenv = Installer('poetryvenv', exe='python3', manager='poetry', entry='{name}.py') poetryvenv.add_entrypoint('{name}') poetryvenv.add_entrypoint('__init__.py') +poetryvenv.add_dependency_file('poetry.lock') poetryvenv.add_dependency_file('pyproject.toml') poetryvenv.dependency_call = install_to_python_virtual_environment @@ -1007,6 +1039,9 @@ pyprojectViaPip.add_entrypoint('__init__.py') pyprojectViaPip.add_dependency_file('pyproject.toml') pyprojectViaPip.dependency_call = install_to_python_virtual_environment +pythonuv = Installer('pythonuv', exe='python3', manager='uv', entry="{name}.py") +pythonuv.add_dependency_file('uv.lock') +pythonuv.dependency_call = install_python_uv # Nodejs plugin installer nodejs = Installer('nodejs', exe='node', @@ -1020,7 +1055,8 @@ rust_cargo = Installer('rust', manager='cargo', entry='Cargo.toml') rust_cargo.add_dependency_file('Cargo.toml') rust_cargo.dependency_call = cargo_installation -INSTALLERS = [python3venv, poetryvenv, pyprojectViaPip, nodejs, rust_cargo] +INSTALLERS = [pythonuv, python3venv, poetryvenv, pyprojectViaPip, nodejs, + rust_cargo] def help_alias(targets: list):