From ead0b134a7cc07c538fa795f475c66af7f078448 Mon Sep 17 00:00:00 2001 From: Pierre Augier Date: Mon, 30 Dec 2024 10:56:52 +0100 Subject: [PATCH] Use userpath --- .github/workflows/unix.yml | 4 ++ .github/workflows/windows.yml | 12 ++++ pdm.lock | 17 ++++- pyproject.toml | 2 +- src/conda_app.py | 113 +++++++++++++++++----------------- 5 files changed, 88 insertions(+), 60 deletions(-) diff --git a/.github/workflows/unix.yml b/.github/workflows/unix.yml index ee76694..c0a6e0d 100644 --- a/.github/workflows/unix.yml +++ b/.github/workflows/unix.yml @@ -44,6 +44,10 @@ jobs: conda-app uninstall pipx -y conda-app list + - name: test ensurepath + run: | + conda-app ensurepath + - name: test installed mercurial run: | if [ -f $HOME/.bash_profile ]; then diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 1bf33f0..2e7eada 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -32,10 +32,22 @@ jobs: conda-app install mercurial conda-app list + - name: test ensurepath + run: | + conda-app ensurepath + - name: test install pipx run: | conda-app install pipx python=3.12 conda-app list + + - name: set PATH + run: | + Add-Content $env:GITHUB_PATH "C:\Miniconda\condabin\app" + + - name: test installed pipx + run: | + which pipx pipx --version conda-app uninstall pipx -y diff --git a/pdm.lock b/pdm.lock index a22a5b8..0cdd7a1 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:d3ddc23c7584e7b42668c29348850061a5d0c6a0267adbec11c423d704772aa4" +content_hash = "sha256:d974fee420aff9a87f6179c871aed8f88b65e6d74a58c6d55ade25a5a373cb08" [[metadata.targets]] requires_python = ">=3.11" @@ -272,6 +272,21 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] +[[package]] +name = "userpath" +version = "1.9.2" +requires_python = ">=3.7" +summary = "Cross-platform tool for adding locations to the user PATH" +groups = ["default"] +marker = "python_version >= \"3.11\"" +dependencies = [ + "click", +] +files = [ + {file = "userpath-1.9.2-py3-none-any.whl", hash = "sha256:2cbf01a23d655a1ff8fc166dfb78da1b641d1ceabf0fe5f970767d380b14e89d"}, + {file = "userpath-1.9.2.tar.gz", hash = "sha256:6c52288dab069257cc831846d15d48133522455d4677ee69a9781f11dbefd815"}, +] + [[package]] name = "wcwidth" version = "0.2.13" diff --git a/pyproject.toml b/pyproject.toml index 005bc38..bd77809 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", ] -dependencies = ["click"] +dependencies = ["click", "userpath>=1.9.2"] [project.urls] Homepage = "https://foss.heptapod.net/fluiddyn/conda-app/" diff --git a/src/conda_app.py b/src/conda_app.py index 139e006..fb6758e 100644 --- a/src/conda_app.py +++ b/src/conda_app.py @@ -6,11 +6,15 @@ from pathlib import Path from functools import partial +from shutil import which import click +import userpath _open = partial(open, encoding="utf-8") +SHELLS = ["bash", "fish", "sh", "xonsh", "zsh"] + def check_command(conda_command): try: @@ -110,30 +114,6 @@ def query_yes_no(question, default="yes"): ) -def modif_config_file(path_config, line_config, force=False): - path_config = Path(path_config) - if force and not path_config.exists(): - path_config.touch() - if not line_config.endswith("\n"): - line_config = line_config + "\n" - if path_config.exists(): - with _open(path_config) as file: - lines = file.readlines() - if lines and lines[-1] and not lines[-1].endswith("\n"): - lines[-1] = lines[-1] + "\n" - if line_config not in lines: - print( - f"Add line \n{line_config.strip()}\n" - f"at the end of file {path_config}" - ) - - with _open(path_config.with_name(path_config.name + ".orig"), "w") as file: - file.write("".join(lines)) - - with _open(path_config, "a") as file: - file.write("\n# line added by conda-app\n" + line_config) - - def get_conda_data(): result = run_conda("info", "--json") return json.loads(result) @@ -218,44 +198,13 @@ def install(app_name, other_packages=None): print(f"Package {package_name} found!") - print("Running conda info... ", end="", flush=True) conda_data = get_conda_data() - print("done") path_root = conda_data["root_prefix"] - if conda_data["root_writable"]: - if os.name == "nt": - # quickfix: I wasn't able to permanently set the PATH on Windows - path_bin = Path(path_root) / "condabin" - else: - path_bin = Path(path_root) / "condabin/app" - else: - if not os.name == "nt": - path_bin = Path.home() / ".local/bin/conda-app-bin" - else: - print( - "\nError: conda-app cannot be used on Windows when " - "conda root is not writable. " - "You can retry with miniconda installed " - "only for you (not globally)." - ) - sys.exit(1) - + path_bin = _get_path_bin(conda_data) path_bin.mkdir(exist_ok=True, parents=True) - export_path_posix = f"export PATH={path_bin}:$PATH\n" - # bash - modif_config_file(bash_config, export_path_posix) - - # zsh - force_zshrc = platform.system() == "Darwin" - modif_config_file(Path.home() / ".zshrc", export_path_posix, force=force_zshrc) - - # fish - modif_config_file( - Path.home() / ".config/fish/config.fish", - f"set -gx PATH {path_bin} $PATH\n", - ) + _ensurepath(path_bin) env_names = get_env_names(conda_data) env_name = "_env_" + app_name @@ -314,7 +263,7 @@ def install(app_name, other_packages=None): path_symlink.unlink() path_symlink.symlink_to(path_command) - if os.name == "nt": + if userpath.need_shell_restart(str(path_bin)): txt = "T" else: txt = "Open a new terminal and t" @@ -369,3 +318,51 @@ def list_apps(): """List the applications installed by conda-app.""" data = load_data() print("Installed applications:\n", data["installed_apps"]) + + +@main.command(name="ensurepath", context_settings=CONTEXT_SETTINGS) +def ensurepath(): + """Add conda-app path to PATH.""" + conda_data = get_conda_data() + path_bin = str(_get_path_bin(conda_data)) + _ensurepath(path_bin) + + if userpath.need_shell_restart(path_bin): + click.echo( + f"{path_bin} has been been added to PATH, but you need to " + "open a new terminal or re-login for this PATH change to take " + "effect. Alternatively, you can source your shell's config file " + "with e.g. 'source ~/.bashrc'." + ) + + +def _ensurepath(path_bin): + + path_bin = str(path_bin) + in_current_path = userpath.in_current_path(path_bin) + if in_current_path: + click.echo(f"{path_bin} is already in PATH.") + return + + if os.name == "nt": + shells = None + else: + shells = [shell for shell in SHELLS if which(shell) is not None] + + path_added = userpath.prepend(path_bin, "conda-app", shells=shells) + + if not path_added: + click.secho( + f"{path_bin} is not added to the PATH environment variable " + "successfully. You may need to add it to PATH manually.", + fg="red", + ) + else: + click.echo(f"Added {path_bin} to the PATH environment variable.") + + +def _get_path_bin(conda_data): + if conda_data["root_writable"]: + return Path(conda_data["root_prefix"]) / "condabin/app" + else: + return Path.home() / ".local/bin/conda-app-bin"