def test_prompt_yes_no(mocker, return_values): patch = mocker.patch("conda_env_tracker.utils.input", side_effect=return_values) if return_values[-1] in ["n", "no"]: assert not prompt_yes_no(prompt_msg="") else: assert prompt_yes_no(prompt_msg="") assert len(patch.call_args_list) == len(return_values)
def _ask_user_to_sync(name: str, yes: bool = False): """If the user is updating the environment and using the auto functionality then ask if they want to sync.""" if ( not yes and os.environ.get("CET_AUTO") == "0" and _remote_dir_is_set(name=name) and prompt_yes_no(f"Conda-env-tracker sync changes to '{name}' environment") ): sync(name=name, yes=True)
def remove(self, yes=False) -> None: """Remove the environment and history.""" if yes or prompt_yes_no( f"Are you sure you want to remove the {self.name} environment", default=False, ): delete_conda_environment(name=self.name) try: remote_io = EnvIO(self.local_io.get_remote_dir()) except CondaEnvTrackerRemoteError: remote_io = None self.local_io.delete_all() if remote_io and ( yes or prompt_yes_no( prompt_msg=f"Do you want to remove remote files in dir: {remote_io.env_dir}?" ) ): remote_io.delete_all()
def jupyter_kernel_install_query(name: str, packages: Packages): """A function to install conda env as jupyter kernel if user agrees""" if any(pkg.name.startswith("jupyter") for pkg in packages): try: if _jupyter_kernel_exists(name=name): logger.debug( f"{name} is already installed as a jupyter kernel") else: if prompt_yes_no( prompt_msg=f"Would you like to register {name} as a " "jupyter kernel available from another environment"): _install_conda_jupyter_kernel(name=name) except JupyterKernelInstallError as err: logger.debug(f"Error while installing jupyter kernel: {str(err)}")
def _create_env_with_new_id(env: Environment, remote_history: History, yes: bool): if env.history and remote_history and env.history.id != remote_history.id: if yes or prompt_yes_no( prompt_msg= (f"The remote cet environment ({env.local_io.get_remote_dir()}) doesn't match the local environment ({env.name})\n" "Would you like to replace your local environment"), default=False, ): return True raise CondaEnvTrackerCreationError( f"The remote environment ({env.local_io.get_remote_dir()}) appears to be new environment and would replace the current local environment ({env.name})\n" "User elected not to update") return False
def _local_needs_update(local_history: History, remote_history: History, yes: bool = True) -> bool: """Either the local does not yet exist or local is a subset of remote.""" if not local_history and remote_history: return True if _no_new_actions_in_local(local_history=local_history, remote_history=remote_history): return True if _actions_in_different_order(local_history=local_history, remote_history=remote_history): if not yes and not prompt_yes_no( prompt_msg= "Remote environment has same packages but in different order, " "Should we overwrite local with remote environment"): raise CondaEnvTrackerPullError( "Actions in a different order; user elected not to update") return True return False
def pull(env: Environment, yes: bool = False) -> Environment: """Pull history from remote to local""" try: remote_dir = env.local_io.get_remote_dir() except CondaEnvTrackerRemoteError as remote_err: raise CondaEnvTrackerPullError(str(remote_err)) remote_io = EnvIO(env_directory=remote_dir) remote_history = remote_io.get_history() local_history = env.history if _create_env_with_new_id(env=env, remote_history=remote_history, yes=yes): return replace_local_with_remote( env=env, remote_dir=remote_dir, remote_io=remote_io, remote_history=remote_history, ) if _nothing_to_pull(local_history=local_history, remote_history=remote_history): logger.info("Nothing to pull.") return env if _local_needs_update(local_history=local_history, remote_history=remote_history, yes=yes): return replace_local_with_remote( env=env, remote_dir=remote_dir, remote_io=remote_io, remote_history=remote_history, ) if not yes and not prompt_yes_no(prompt_msg=( "Remote and local have different packages, do you want to overwrite " "with remote and append local")): logger.info("Exiting without updating local environment.") raise CondaEnvTrackerPullError( "Conflicting Packages in remote and local; user elected not to update" ) return merge_conflicting_changes(env=env)
def pull(env: Environment, yes: bool = False) -> Environment: """Pull history from remote to local""" remote_dir = env.local_io.get_remote_dir() remote_io = EnvIO(env_directory=remote_dir) remote_history = remote_io.get_history() local_history = env.history _check_for_errors(local_history=local_history, remote_history=remote_history) if _nothing_to_pull(local_history=local_history, remote_history=remote_history): logger.info("Nothing to pull.") return env if _local_needs_update(local_history=local_history, remote_history=remote_history): update( env=env, remote_dir=remote_dir, remote_io=remote_io, remote_history=remote_history, ) return env if _actions_in_different_order( local_history=local_history, remote_history=remote_history ): if not yes and not prompt_yes_no( prompt_msg="Remote environment has same packages but in different order, " "Should we overwrite local with remote environment" ): logger.info("Exiting without updating local environment.") return env update( env=env, remote_dir=remote_dir, remote_io=remote_io, remote_history=remote_history, ) return env if not yes and not prompt_yes_no( prompt_msg=( "Remote and local have different packages, do you want to overwrite " "with remote and append local" ) ): logger.info("Exiting without updating local environment.") return env update_conda_environment(env_dir=remote_dir) if _r_env_needs_updating( local_history=local_history, remote_history=remote_history ): update_r_environment(name=env.name, env_dir=remote_dir) EnvIO.overwrite_local(local_io=env.local_io, remote_io=remote_io) new_env = Environment(name=env.name, history=remote_history) new_env.validate() extra_logs = [] for log in local_history.logs: if log not in set(remote_history.logs): extra_logs.append(log) for log in extra_logs: new_env = _update_from_extra_log(env=new_env, history=local_history, log=log) new_env = _update_r_packages( env=new_env, local_history=local_history, remote_history=remote_history ) new_env.validate() new_env.export() env.history = new_env.history logger.info("Successfully updated the environment.") return new_env
def test_prompt_yes_no_default(mocker): patch = mocker.patch("conda_env_tracker.utils.input", return_value="y") prompt_yes_no(prompt_msg="", default=True) patch.assert_called_with(" ([y]/n)? ") prompt_yes_no(prompt_msg="", default=False) patch.assert_called_with(" (y/[n])? ")
def create( cls, name: str, packages: Packages, channels: ListLike = None, yes: bool = False, strict_channel_priority: bool = True, ): """Creating a conda environment from a list of packages.""" if name == "base": raise CondaEnvTrackerCondaError( "Environment can not be created using default name base" ) if name in get_all_existing_environment(): message = ( f"This environment {name} already exists. Would you like to replace it" ) if prompt_yes_no(prompt_msg=message, default=False): delete_conda_environment(name=name) local_io = EnvIO(env_directory=USER_ENVS_DIR / name) if local_io.env_dir.exists(): local_io.delete_all() else: raise CondaEnvTrackerCondaError(f"Environment {name} already exists") logger.debug(f"creating conda env {name}") conda_create( name=name, packages=packages, channels=channels, yes=yes, strict_channel_priority=strict_channel_priority, ) create_cmd = get_conda_create_command( name=name, packages=packages, channels=channels, strict_channel_priority=strict_channel_priority, ) specs = Actions.get_package_specs( packages=packages, dependencies=get_dependencies(name=name)["conda"] ) if not channels: channels = get_conda_channels() dependencies = get_dependencies(name=name) history = History.create( name=name, channels=Channels(channels), packages=PackageRevision.create(packages, dependencies=dependencies), logs=Logs(create_cmd), actions=Actions.create( name=name, specs=specs, channels=Channels(channels), strict_channel_priority=strict_channel_priority, ), diff=Diff.create(packages=packages, dependencies=dependencies), debug=Debug.create(name=name), ) env = cls(name=name, history=history, dependencies=dependencies) env.export() return env