def infer(cls, name: str, packages: Packages, channels: ListLike = None): """create conda_env_tracker environment by inferring to existing conda environment""" if name == "base": raise CondaEnvTrackerCondaError( "Environment can not be created using default name base" ) if name not in get_all_existing_environment(): raise CondaEnvTrackerCondaError( f"Environment {name} can not be inferred, does not exist" ) existing_packages = get_dependencies(name=name) if "r-base" in existing_packages["conda"]: existing_r_packages = get_r_dependencies(name=name) else: existing_r_packages = {} user_packages = {"conda": Packages(), "pip": Packages(), "r": Packages()} for package in packages: if package.name in existing_packages.get("conda", Packages()): user_packages["conda"].append(package) elif package.name in existing_packages.get("pip", Packages()): user_packages["pip"].append(package) elif package.name in existing_r_packages: raise RError( "Cannot infer R packages, must run follow-up R install command.\n" f"Found '{package.name}' in installed R packages {existing_r_packages}." ) else: raise CondaEnvTrackerCondaError( f"Environment {name} does not have {package.spec} installed" ) conda_create_cmd = get_conda_create_command( name, user_packages["conda"], channels ) specs = Actions.get_package_specs( packages=user_packages["conda"], dependencies=existing_packages["conda"] ) history = History( name=name, channels=Channels(channels), packages=HistoryPackages.create(user_packages["conda"]), logs=Logs.create(conda_create_cmd), actions=Actions.create(name=name, specs=specs, channels=Channels(channels)), debug=Debug.create(name=name), ) env = cls(name=name, history=history) if user_packages["pip"]: handler = PipHandler(env=env) handler.update_history_install(packages=user_packages["pip"]) env = handler.env env.export() return env
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(): raise CondaEnvTrackerCondaError(f"Environment {name} already exist") 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() history = History( name=name, channels=Channels(channels), packages=HistoryPackages.create(packages), logs=Logs.create(create_cmd), actions=Actions.create( name=name, specs=specs, channels=Channels(channels), strict_channel_priority=strict_channel_priority, ), debug=Debug.create(name=name), ) env = cls(name=name, history=history) env.export() return env
def get_dependencies(name: str) -> dict: """Get the information about pip and conda packages in the environment using `conda list`. Package information includes: name and version """ completed_process = subprocess.run( f"conda list --name {name}", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, encoding="UTF-8", ) if completed_process.returncode != 0: error_message = completed_process.stderr.strip() raise CondaEnvTrackerCondaError(error_message) lines = completed_process.stdout.strip().split("\n") dependencies = {"conda": {}} for line in lines: if not line.startswith("#"): dep = line.strip().split() name = dep[0] spec = name version = dep[1] if dep[-1] in ["pip", "pypi"]: dependencies["pip"] = dependencies.get("pip", {}) dependencies["pip"][name] = Package(name, spec, version) else: build = dep[2] dependencies["conda"][name] = Package(name, spec, version, build) return dependencies
def delete_conda_environment(name: str) -> None: """Delete conda environment""" if os.environ["CONDA_DEFAULT_ENV"] == name: raise CondaEnvTrackerCondaError( f'Must run "conda deactivate" before removing or rebuilding the {name} environment.' ) subprocess.run(f"conda env remove -y --name {name}", shell=True)
def _get_env_file(env_dir: Path) -> Path: """Get the environment file. This file only contains packages the user has specifically asked for and does not contain dependencies.""" env_file = env_dir / "conda-env.yaml" if env_file.exists(): return env_file raise CondaEnvTrackerCondaError( f"No environment file to update from in {env_dir}.\n" "Someone may need to push to this remote or recover the lost file in some other way." )
def init() -> str: """Check conda version.""" completed_process = subprocess.run( "conda --version", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, encoding="UTF-8", ) process_code = completed_process.returncode if process_code == 0: conda_version = completed_process.stdout.rstrip().split(" ")[1] else: raise CondaEnvTrackerCondaError( "Error checking conda version. Maybe you haven't installed anaconda/miniconda?" f"\nError: {completed_process.stderr}") if conda_version < MINIMUM_CONDA_VERSION: raise CondaEnvTrackerCondaError( f"Need conda>={MINIMUM_CONDA_VERSION}, but found conda={conda_version}." ' Please run "conda update -n base conda".') logger.info(f"Using conda version {conda_version}") return conda_version
def get_conda_bin_path() -> Path: """Find the path to the conda binary.""" conda_exe_path = os.environ.get("CONDA_EXE") if conda_exe_path: return Path(conda_exe_path).parent completed_process = subprocess.run( "which conda", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="UTF-8", ) process_code = completed_process.returncode if process_code != 0: raise CondaEnvTrackerCondaError( "CondaEnvTracker requires an anaconda/miniconda install") return Path(completed_process.stdout.strip()).parent
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