-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add uninstall subcommand #112
base: main
Are you sure you want to change the base?
Conversation
…the same as prefix
except PermissionError: | ||
pass | ||
|
||
def _remove_file_and_parents(file: Path, stop_at: Path | None = None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be quite a destructive function, so I'd recommend adding separate tests and making it a global function.
For example, we need to ensure that file
is contained in stop_at
, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a check, which actually made me find a bug. This made me add a warning to the README file that support for softlinks is limited.
The problem with writing separate tests is that we would have to create an entry point for it, which would expose that destructive function even more. I added another tests to ensure that the function does not a non-empty directory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, cannot we import the entry_point.py
module in the tests directly and target that function?
# Cannot make this a required argument or `conda.exe constructor uninstall --prefix` | ||
# will not work (would have to be `conda constructor --prefix uninstall`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are checking against prefix=None
later right? So in practice it is required for the user. A comment here to clarify would be perfect.
@@ -110,7 +116,7 @@ def __call__(self, parser, namespace, values, option_string=None): | |||
f"Defaults to {DEFAULT_NUM_PROCESSORS}.", | |||
) | |||
|
|||
g = p.add_mutually_exclusive_group(required=True) | |||
g = p.add_mutually_exclusive_group() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd need to add checks to make this required manually if uninstall
is not in the ARGV. Maybe an else
clause in _constructor_main()
(line 602, I think?).
# See: https://github.com/conda/conda/blob/475e6acbdc98122fcbef4733eb8cb8689324c1c8/conda/gateways/disk/create.py#L482-L488 # noqa | ||
ENVS_DIR_MAGIC_FILE = ".conda_envs_dir_test" | ||
|
||
uninstall_prefix = Path(uninstall_dir).expanduser().resolve() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to os.path.expandvars
too or is that not expected?
_remove_file_directory(parent) | ||
parent = parent.parent | ||
|
||
def _remove_config_file_and_parents(file: Path): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's precedent for users not liking this default behaviour, see conda/conda#13113. Not sure if we should delete empty parents recursively 🤔 Maybe just the config-containing one? Happy to have my mind changed here, though, so let me know.
# Sort by path depth. This will place the root prefix first | ||
# Since it is more likely that profiles contain the root prefix, | ||
# this makes loops more efficient. | ||
prefixes.sort(key=lambda x: len(x.parts)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏 👏 👏
run_plan(plan) | ||
run_plan_elevated(plan) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, these functions do not raise errors? They just write to plan
?
continue | ||
expected_files = [pkgs_dir / "urls", pkgs_dir / "urls.txt"] | ||
if all(file in expected_files for file in pkgs_dir.iterdir()): | ||
_remove_file_and_parents(pkgs_dir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here about parents (and elsewhere), I don't think we need to recursively remove all parents.
> This can cause files to be left behind. | ||
> [!WARNING] | ||
> Support for softlinks is still limited. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate? Are errors expected, or undefined behavior?
), | ||
) | ||
uninstall_subcommand.add_argument( | ||
"--remove-caches", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"--remove-caches", | |
"--remove-additional-caches", |
The term is a bit convoluted with pkgs/
and pkgs/cache
being taken care of in conda-clean
, but this argument targets ~/.conda
and anaconda-client
stuff.
required=False, | ||
help=( | ||
"Remove all cache directories created by conda." | ||
" This includes the .conda directory inside HOME/USERPROFILE." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
" This includes the .conda directory inside HOME/USERPROFILE." | |
" This includes the ~/.conda directory and anaconda-client data." |
Description
Uninstalling distributions for
conda
currently only has incomplete solutions with files and broken auto-run/initializer scripts left behind (see conda/constructor#642, conda/constructor#588, and conda/constructor#572). A clean uninstallation is currently not available without manually running a series of commands that are still not 100% safe - e.g.,conda init --reverse --all
will remove initialization ofconda
, no matter what installation it is pointed to.This PR introduces a cross-platform uninstallation subcommand that uses conda-internal features to perform the following tasks:
envs_dirs
). This uninstalls all shortcuts, performs pre-unlink actions, and de-registers the environments from theenvironments.txt
file.conda init reverse
if a configuration file points to any of the environments that are removed..condarc
files, either for the user, system-wide, or all.conda clean --all
to remove package caches outside the installation directory..conda
file.This is implemented in
conda-standalone
so that a single binary can be used on macOS and Linux. It also allows decoupling of the installer "front end" (built byconstructor
) from the installer "back end" (such asconda-standalone
ormicromamba
), as outlined in the following issue: conda/constructor#549Known gap: Soft links are currently not well supported: files are unlinked, but no efforts are made to ensure that this doesn't leave unused files behind.
Checklist - did you ...
news
directory (using the template) for the next release's release notes?