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 test_pull_no_new_action_in_local(setup_tests): overwrite_mock = setup_tests["overwrite_mock"] remote_history = setup_tests["remote_history"] local_packages = (Package.from_spec("pandas"), ) local_logs = ["conda create --name pull_testing_environment pandas"] local_actions = [ "conda create --name pull_testing_environment pandas=0.23=py36" ] local_history = History( name=ENV_NAME, packages=HistoryPackages.create(local_packages), channels=Channels(), logs=Logs([log for log in local_logs]), actions=Actions(local_actions), debug=Debug(), ) env = Environment(name=ENV_NAME, history=local_history) pull(env=env) assert env.history.packages == remote_history.packages assert env.history.logs == remote_history.logs assert env.history.actions == remote_history.actions overwrite_mock.called_once()
def setup(mocker, request): """Set up for diff function""" local_packages = request.param["local_packages"] local_logs = request.param["local_logs"] local_actions = request.param["local_actions"] env_name = request.param["env_name"] remote_packages = request.param["remote_packages"] remote_logs = request.param["remote_logs"] remote_actions = request.param["remote_actions"] mocker.patch("conda_env_tracker.env.get_dependencies") history = History( name=env_name, packages=HistoryPackages.create(local_packages), channels=Channels(["conda-forge"]), logs=Logs([log for log in local_logs]), actions=local_actions, debug=Debug(), ) env = Environment(name=env_name, history=history) mocker.patch("conda_env_tracker.main.Environment.read", return_value=env) mocker.patch( "conda_env_tracker.gateways.io.EnvIO.get_remote_dir", return_value="~/path/to/remote", ) mocker.patch("pathlib.Path.is_dir", return_value=True) history = History( name=env_name, channels=Channels(["conda-forge"]), packages=HistoryPackages.create(remote_packages), logs=Logs([log for log in remote_logs]), actions=remote_actions, debug=Debug(), ) mocker.patch("conda_env_tracker.gateways.io.EnvIO.get_history", return_value=history) mocker.patch("pathlib.Path.write_text") yield env, request.param["push_should_fail"] if (USER_ENVS_DIR / env_name).exists(): shutil.rmtree(USER_ENVS_DIR / env_name)
def test_extract_packages_from_actions(spec): """Test parsing the packges from action item""" action = Actions.create( name="test-extract", specs=[spec], channels=Channels(["conda-forge", "conda-remote"]), ) packages = action.extract_packages(index=0) assert packages[0] == Package.from_spec(spec)
def test_get_create_action(): """Test create action string""" channels = Channels(["conda-forge", "main"]) expected = [( "conda create --name test-create python=3.6.6=py_36 pandas=0.23.1=py_36 " "--override-channels --strict-channel-priority " "--channel conda-forge " "--channel main")] actual = Actions.create("test-create", ["python=3.6.6=py_36", "pandas=0.23.1=py_36"], channels=channels) assert expected == actual
def expected_update(mocker): """Set up for update function""" packages = Packages.from_specs("pandas") channel = "conda-forge" create_cmd = "conda install pandas" name = "test-update" mocker.patch("conda_env_tracker.gateways.io.Path.mkdir") mocker.patch("conda_env_tracker.gateways.io.Path.write_text") mocker.patch("conda_env_tracker.gateways.io.Path.iterdir") mocker.patch("conda_env_tracker.history.get_pip_version", return_value=None) history = History( name=name, channels=Channels([channel]), packages=HistoryPackages.create(packages), logs=Logs.create(create_cmd), actions=Actions.create(name="test-update", specs=["pandas"], channels=Channels([channel])), debug=Debug(), ) mocker.patch( "conda_env_tracker.env.get_dependencies", return_value={ "conda": { "pandas": Package("pandas", "pandas", "0.23", "py36"), "pytest": Package("pytest", "pytest", "0.1", "py36"), }, "pip": {}, }, ) mocker.patch("conda_env_tracker.env.EnvIO.export_packages") mocker.patch( "conda_env_tracker.main.Environment.read", return_value=Environment(name=name, history=history), ) return history
def test_write_history_file(env_io): """Test to create history yaml file and test get history""" env_io.write_history_file( History( name="", channels=Channels([]), packages=HistoryPackages({}), logs=Logs([]), actions=Actions([]), debug=[], )) assert [] == env_io.get_history().channels assert {} == env_io.get_history().packages assert [] == env_io.get_history().logs assert [] == env_io.get_history().debug
def test_export_empty(): """Test package export""" expected = { "name": "", "channels": [], "packages": {}, "logs": [], "actions": [], "debug": [], } actual = History( name="", channels=Channels(channels=[]), packages=HistoryPackages(), logs=Logs(), actions=Actions([]), debug=[], ).export() assert expected == actual
def test_pull_no_new_action_in_remote(setup_tests, local_packages, local_logs, local_actions): overwrite_mock = setup_tests["overwrite_mock"] local_history = History( name=ENV_NAME, packages=HistoryPackages.create(local_packages), channels=Channels(), logs=Logs([log for log in local_logs]), actions=Actions(local_actions), debug=Debug(), ) env = Environment(name=ENV_NAME, history=local_history) pull(env=env) assert env.history.packages == local_history.packages assert env.history.logs == local_history.logs assert env.history.actions == local_history.actions overwrite_mock.assert_not_called()
def test_export_success(): """Test package export""" expected = { "name": "environment-name", "channels": ["conda-forge", "main"], "packages": { "conda": { "pytest": "*" } }, "logs": ["conda create --name test pytest"], "actions": [ "conda create --name test pytest=0.1=py36_0 " "--channel conda-forge " "--channel main" ], "debug": [{ "platform": "osx", "conda_version": "4.5.10" }], } actual = History( name="environment-name", channels=Channels(["conda-forge", "main"]), packages=HistoryPackages(conda=dict( pytest=Package.from_spec("pytest"))), logs=Logs(["conda create --name test pytest"]), actions=Actions([ "conda create --name test pytest=0.1=py36_0 " "--channel conda-forge " "--channel main" ]), debug=[{ "platform": "osx", "conda_version": "4.5.10" }], ).export() assert expected == actual
def test_ignore_empty_pip_packages(): """When a user installs a pip package and then removes it, the pip packages is an empty dictionry which does not need to show up in the history file. """ expected = { "name": "environment-name", "channels": ["main"], "packages": { "conda": { "pytest": "*" } }, "logs": ["conda create --name test pytest"], "actions": ["conda create --name test pytest=0.1=py36_0 " "--channel main"], "debug": [{ "platform": "osx", "conda_version": "4.5.10" }], } actual = History( name="environment-name", channels=Channels(["main"]), packages=HistoryPackages( conda=dict(pytest=Package.from_spec("pytest")), pip={}), logs=Logs(["conda create --name test pytest"]), actions=Actions( ["conda create --name test pytest=0.1=py36_0 " "--channel main"]), debug=[{ "platform": "osx", "conda_version": "4.5.10" }], ).export() assert expected == actual
from conda_env_tracker.errors import CondaEnvTrackerCreationError from conda_env_tracker.gateways.io import USER_ENVS_DIR from conda_env_tracker.gateways.r import R_COMMAND from conda_env_tracker.history import ( History, HistoryPackages, Logs, Channels, Actions, Debug, ) from conda_env_tracker.packages import Package, Packages from conda_env_tracker.pull import pull ENV_NAME = "pull_testing_environment" CHANNELS = Channels(["conda-forge", "main"]) @pytest.fixture(scope="function") def setup_tests(mocker): """Set up for pull function""" remote_packages = (Package.from_spec("pandas"), Package.from_spec("pytest")) remote_logs = [ "conda create --name pull_testing_environment pandas", "conda install --name pull_testing_environment pytest", ] remote_actions = [ "conda create --name pull_testing_environment pandas=0.23=py36", "conda install --name pull_testing_environment pytest=0.1=py36_3", ]