Skip to content

Commit

Permalink
fix: make linters etc happy
Browse files Browse the repository at this point in the history
  • Loading branch information
robinvandernoord committed Apr 10, 2024
1 parent 9026c37 commit bc16251
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 23 deletions.
8 changes: 8 additions & 0 deletions src/uvx/_maybe.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@


class Empty(Err[None]):
"""
Alias for Err[None], used by Maybe.
Usage:
Result[str, None] = Maybe[str]
"""

def __init__(self) -> None:
"""Set up the Err result with None as it's value."""
super().__init__(None)


Expand Down
54 changes: 44 additions & 10 deletions src/uvx/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sys
from datetime import datetime
from pathlib import Path
from typing import Optional
from typing import Annotated

import rich
import typer
Expand Down Expand Up @@ -34,40 +34,73 @@


def output(result: Result[str, Exception]) -> None:
"""Output positive (ok) result to stdout and error result to stderr."""
match result:
case Ok(msg):
rich.print(msg) # :trash:
rich.print(msg)
case Err(err):
rich.print(err, file=sys.stderr)


OPTION_PYTHON_HELP_TEXT = "Python version or executable to use, e.g. `3.12`, `python3.12`, `/usr/bin/python3.12`"


@app.command()
def install(package_name: str, force: bool = False, python: str = "", no_cache: bool = False):
def install(
package_name: str,
force: Annotated[
bool,
typer.Option(
"-f", "--force", help="Overwrite currently installed executables with the same name (in ~/.local/bin)"
),
] = False,
python: Annotated[str, typer.Option(help=OPTION_PYTHON_HELP_TEXT)] = "",
no_cache: Annotated[bool, typer.Option("--no-cache", help="Run without `uv` cache")] = False,
):
"""Install a package (by pip name)."""
# todo: support 'install .'
output(install_package(package_name, python=python, force=force, no_cache=no_cache))


@app.command(name="upgrade")
@app.command(name="update")
def upgrade(package_name: str, force: bool = False, skip_injected: bool = False, no_cache: bool = False):
def upgrade(
package_name: str,
force: Annotated[bool, typer.Option("-f", "--force", help="Ignore previous version constraint")] = False,
skip_injected: Annotated[
bool, typer.Option("--skip-injected", help="Don't also upgrade injected packages")
] = False,
no_cache: Annotated[bool, typer.Option("--no-cache", help="Run without `uv` cache")] = False,
):
"""Upgrade a package."""
output(upgrade_package(package_name, force=force, skip_injected=skip_injected, no_cache=no_cache))


@app.command(name="remove")
@app.command(name="uninstall")
def uninstall(package_name: str, force: bool = False):
def uninstall(
package_name: str,
force: Annotated[
bool,
typer.Option(
"-f",
"--force",
help="Remove executable with the same name (in ~/.local/bin) even if related venv was not found",
),
] = False,
):
"""Uninstall a package (by pip name)."""
output(uninstall_package(package_name, force=force).map(lambda version: f"🗑️ {package_name}{version} removed!"))


@app.command()
def reinstall(
package: str,
python: Optional[str] = None,
force: bool = False,
without_injected: bool = False,
no_cache: bool = False,
python: Annotated[str, typer.Option(help=OPTION_PYTHON_HELP_TEXT)] = "",
force: Annotated[bool, typer.Option("-f", "--force", help="See `install --force`")] = False,
without_injected: Annotated[
bool, typer.Option("--without-injected", help="Don't include previously injected libraries in reinstall")
] = False,
no_cache: Annotated[bool, typer.Option("--no-cache", help="Run without `uv` cache")] = False,
):
"""Uninstall a package (by pip name) and re-install from the original spec (unless a new spec is supplied)."""
output(
Expand All @@ -83,6 +116,7 @@ def reinstall(

@app.command()
def inject(into: str, package_specs: list[str]):
"""Install additional packages to a virtual environment managed by uvx."""
output(
inject_packages(
into,
Expand Down
33 changes: 20 additions & 13 deletions src/uvx/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def install_package(
venv: Optional[Path] = None,
python: Optional[str] = None,
force: bool = False,
extras: Optional[list[str]] = None,
extras: Optional[typing.Iterable[str]] = None,
no_cache: bool = False,
) -> Result[str, Exception]:
"""
Expand All @@ -138,23 +138,23 @@ def install_package(
# todo: make --force use --no-cache

with virtualenv(venv), exit_on_pb_error():
try:
args = []
text = f"installing {meta.name}"
if extras:
args.extend(extras)
text += f" with {extras}"
args: list[str] = []
text = f"installing {meta.name}"
if extras:
args.extend(extras)
text += f" with {extras}"

if no_cache:
args += ["--no-cache"]
if no_cache:
args += ["--no-cache"]

try:
animate(uv("pip", "install", meta.install_spec, *args), text=text)

# must still be in the venv for these:
# must still be in the venv and try for these:
meta.installed_version = get_package_version(meta.name, venv)
meta.python = get_python_version(venv)
meta.python_raw = get_python_executable(venv)
meta.injected = extras
meta.injected = set(extras)

except plumbum.ProcessExecutionError as e:
remove_dir(venv)
Expand Down Expand Up @@ -235,6 +235,7 @@ def reinstall_package(


def inject_packages(into: str, package_specs: set[str]) -> Result[str, Exception]:
"""Install extra libraries into a package-specific venv."""
match collect_metadata(into):
case Err(e):
return Err(e)
Expand Down Expand Up @@ -277,6 +278,7 @@ def remove_dir(path: Path):
def upgrade_package(
package_name: str, force: bool = False, skip_injected: bool = False, no_cache: bool = False
) -> Result[str, Exception]:
"""Upgrade a package in its venv."""
# run `uv pip install --upgrade package` with requested install spec (version, extras, injected)
# if --force is used, the previous version is ignored.
match collect_metadata(package_name):
Expand All @@ -302,8 +304,13 @@ def upgrade_package(
# if --force, drop version spec
base_pkg = meta.name
extras = meta.extras
injected = [] if skip_injected else (meta.injected or [])
version = spec_metadata.requested_version or ("" if force else meta.requested_version)

injected: set[str] = (not skip_injected and meta.injected) or set()
# injected = set() if skip_injected else (meta.injected or set())

version: str = spec_metadata.requested_version or (not force and meta.requested_version) or ""
# version = spec_metadata.requested_version or ("" if force else meta.requested_version)

options = []
if force:
options.append("--no-cache")
Expand Down
2 changes: 2 additions & 0 deletions src/uvx/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def check_script_symlinks(self, name: str) -> "Self":


def fake_install(spec: str) -> dict:
"""Dry run pip to extract metadata of a local package."""
_uv("pip", "install", "pip") # ensure we have pip

with tempfile.NamedTemporaryFile() as f:
Expand All @@ -74,6 +75,7 @@ def fake_install(spec: str) -> dict:

@threadful.thread
def resolve_local(spec: str) -> tuple[Maybe[str], Maybe[str]]:
"""Resolve the package name of a local package by dry run installing it."""
try:
full_data = fake_install(spec)
install_data = full_data["install"][0]
Expand Down

0 comments on commit bc16251

Please sign in to comment.