def test_update_history_conda_remove(setup_env): env = setup_env["env"] expected = setup_env["expected"] channels = expected["channels"] packages = Packages.from_specs("pandas") CondaHandler(env=env).update_history_remove(packages=packages) history = env.history log = f"conda remove --name {env.name} pandas" action = f"conda remove --name {env.name} pandas --override-channels " + " ".join( "--channel " + channel for channel in channels ) expected_logs = expected["logs"].copy() expected_actions = expected["actions"].copy() expected_logs.append(log) expected_actions.append(action) assert history.logs == expected_logs assert history.actions == expected_actions
def update_history_install( self, packages: Packages, index_url: Union[str, ListLike] = PIP_DEFAULT_INDEX_URL, ): """Update history for pip install.""" self.env.update_dependencies() self.env.history.packages.update_packages(packages, source="pip") self.env.validate_installed_packages(packages) log = get_pip_install_command(packages=packages, index=index_url) specs = self.env.history.actions.get_package_specs( packages=packages, dependencies=self.env.dependencies["pip"], version_separator="==", ) action = get_pip_install_command(packages=Packages.from_specs(specs), index=index_url) self.env.history.append(log=log, action=action)
def test_remove_package(setup_env, mocker): env = setup_env["env"] env_io = setup_env["env_io"] expected = setup_env["expected"] mocker.patch("conda_env_tracker.conda.conda_remove") packages = Packages.from_specs("pandas") CondaHandler(env=env).remove(packages=packages) history = env_io.get_history() assert history.channels == expected["channels"] assert history.packages == {} assert history.logs == expected["logs"] + [f"conda remove --name {env.name} pandas"] channel_string = "--override-channels --strict-channel-priority " + " ".join( [f"--channel " + channel for channel in expected["channels"]] ) remove_channel_string = channel_string.replace("--strict-channel-priority ", "") assert history.actions == expected["actions"] + [ f"conda remove --name {env.name} pandas {remove_channel_string}" ]
def test_strict_channel_priority_install(setup_env, mocker): env = setup_env["env"] env_io = setup_env["env_io"] expected = setup_env["expected"] get_package_mock = setup_env["get_package_mock"] get_package_mock.configure_mock( return_value={ "conda": { "pyspark": Package("pyspark", "pyspark", "0.21", "py_36"), "pandas": Package("pandas", "pandas", "0.23", "py_36"), } }) mocker.patch("conda_env_tracker.conda.conda_install") CondaHandler(env=env).install(packages=Packages.from_specs("pyspark"), strict_channel_priority=False) history = env_io.get_history() expected_logs = expected["logs"].copy() expected_packages = copy.deepcopy(expected["packages"]) expected_packages["conda"]["pyspark"] = Package.from_spec("pyspark") expected_logs.append(f"conda install --name {env.name} pyspark") install_channel_string = "--override-channels " + " ".join( f"--channel {channel}" for channel in expected["channels"]) action = ( f"conda install --name {env.name} pyspark=0.21=py_36 {install_channel_string}" ) expected_actions = expected["actions"].copy() expected_actions.append(action) assert history.packages == expected_packages assert history.actions == expected_actions assert history.logs == expected_logs
def test_remove_dependency(setup_env, mocker): """Test removing a dependency 'numpy' which will cause the package 'pandas' to be removed.""" env = setup_env["env"] env_io = setup_env["env_io"] expected = setup_env["expected"] logger_mock = mocker.patch("conda_env_tracker.env.logger.warning") get_package_mock = setup_env["get_package_mock"] get_package_mock.configure_mock( return_value={"conda": {"numpy": Package("numpy", "numpy", "1.1", "py_36")}} ) env.dependencies["conda"]["numpy"] = "1.1=py_36" mocker.patch("conda_env_tracker.conda.conda_remove") packages = Packages.from_specs("numpy") CondaHandler(env=env).remove(packages=packages) history = env_io.get_history() channel_string = "--override-channels --strict-channel-priority " + " ".join( [f"--channel " + channel for channel in expected["channels"]] ) remove_channel_string = channel_string.replace("--strict-channel-priority ", "") expected_log = expected["logs"].copy() expected_log.append(f"conda remove --name {env.name} numpy") expected_action = expected["actions"].copy() expected_action.append( f"conda remove --name {env.name} numpy {remove_channel_string}" ) expected_packages = {} assert history.packages == expected_packages assert history.logs == expected_log assert history.actions == expected_action logger_mock.assert_called_once_with( 'Package "pandas" was removed during the last command.' )
def test_install_with_default_version_and_yes_success(setup_env, mocker): env = setup_env["env"] env_io = setup_env["env_io"] expected = setup_env["expected"] initial_conda_packages = setup_env["initial_conda_packages"] get_package_mock = setup_env["get_package_mock"] get_package_mock.configure_mock( return_value={ "conda": { **initial_conda_packages, "pyspark": Package("pyspark", "pyspark", "0.21", "py_36"), } } ) mocker.patch("conda_env_tracker.conda.conda_install") packages = Packages.from_specs("pyspark") CondaHandler(env=env).install(packages=packages) history = env_io.get_history() expected_log = expected["logs"].copy() expected_action = expected["actions"].copy() expected_packages = copy.deepcopy(expected["packages"]) expected_packages["conda"]["pyspark"] = Package.from_spec("pyspark") expected_log.append(f"conda install --name {env.name} pyspark") assert history.packages == expected_packages channel_string = "--override-channels --strict-channel-priority " + " ".join( [f"--channel " + channel for channel in expected["channels"]] ) expected_action.append( f"conda install --name {env.name} pyspark=0.21=py_36 {channel_string}" ) assert history.actions == expected_action assert history.logs == expected_log
def test_infer_environment_with_spec_success(mocker): env_name = "infer-test" dependencies = { "conda": { "pandas": Package("pandas", "pandas", "0.23", "py_36") } } mocker.patch( "conda_env_tracker.history.debug.get_pip_version", mocker.Mock(return_value="18.1"), ) mocker.patch("conda_env_tracker.env.get_all_existing_environment", return_value=[env_name]) mocker.patch("conda_env_tracker.env.get_dependencies", mocker.Mock(return_value=dependencies)) mocker.patch("conda_env_tracker.env.get_r_dependencies") env = Environment.infer( name=env_name, packages=Packages.from_specs("pandas=0.23"), channels=["conda-forge", "main"], ) assert env.history.packages == { "conda": { "pandas": Package("pandas", "pandas=0.23", "0.23", "py_36") } } assert env.history.channels == ["conda-forge", "main"] assert env.history.logs == [ f"conda create --name {env_name} pandas=0.23 --override-channels --strict-channel-priority " "--channel conda-forge " "--channel main" ] assert env.history.actions == [ f"conda create --name {env_name} pandas=0.23=py_36 " "--override-channels --strict-channel-priority " "--channel conda-forge " "--channel main" ]
def test_update_all(setup_env, package, mocker): env = setup_env["env"] env_io = setup_env["env_io"] expected = setup_env["expected"] get_package_mock = setup_env["get_package_mock"] get_package_mock.configure_mock( return_value={"conda": {"pandas": Package("pandas", "pandas", "0.24", "py_36")}} ) mocker.patch("conda_env_tracker.conda.conda_update_all") packages = Packages(package) CondaHandler(env=env).update_all(packages=packages) history = env_io.get_history() channel_string = "--override-channels --strict-channel-priority " + " ".join( [f"--channel " + channel for channel in expected["channels"]] ) expected_log = expected["logs"].copy() expected_log.append(f"conda update --all --name {env.name} {package.spec}") action_packages_cmd = " pandas=0.24=py_36" expected_action = expected["actions"].copy() expected_action.append( f"conda update --all --name {env.name}{action_packages_cmd} {channel_string}" ) expected_packages = { "conda": {"pandas": Package("pandas", package.spec, "0.24", "py_36")} } assert history.packages == expected_packages assert history.logs == expected_log assert history.actions == expected_action
def test_install_unexpectedly_removes_package(setup_env, mocker): """Simulating that installing python=3.8 uninstalls pandas and there is no version of pandas available for python 3.8 """ env = setup_env["env"] env_io = setup_env["env_io"] expected = setup_env["expected"] logger_mock = mocker.patch("conda_env_tracker.env.logger.warning") get_package_mock = setup_env["get_package_mock"] get_package_mock.configure_mock( return_value={"conda": {"python": Package("python", "python", "3.8", "py_38")}} ) mocker.patch("conda_env_tracker.conda.conda_install") CondaHandler(env=env).install(packages=Packages.from_specs("python=3.8")) history = env_io.get_history() channel_string = "--override-channels --strict-channel-priority " + " ".join( [f"--channel " + channel for channel in expected["channels"]] ) expected_log = expected["logs"].copy() expected_log.append(f"conda install --name {env.name} python=3.8") expected_action = expected["actions"].copy() expected_action.append( f"conda install --name {env.name} python=3.8=py_38 {channel_string}" ) expected_packages = {"conda": {"python": Package.from_spec("python=3.8")}} assert history.packages == expected_packages assert history.logs == expected_log assert history.actions == expected_action logger_mock.assert_called_once_with( 'Package "pandas" was removed during the last command.' )
def test_infer_environment_package_does_not_exist(mocker): env_name = "infer-test" dependencies = { "conda": { "pandas": Package("pandas", "pandas", "0.23", "py_36") } } mocker.patch("conda_env_tracker.history.get_pip_version", mocker.Mock(return_value="18.1")) mocker.patch("conda_env_tracker.env.get_all_existing_environment", return_value=[env_name]) mocker.patch("conda_env_tracker.env.get_dependencies", mocker.Mock(return_value=dependencies)) mocker.patch("conda_env_tracker.env.get_r_dependencies") with pytest.raises(Exception) as err: Environment.infer( name=env_name, packages=Packages.from_specs("pytest"), channels=["conda-forge", "main"], ) assert str( err.value) == "Environment infer-test does not have pytest installed"
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.debug.get_pip_version", return_value=None) dependencies = { "conda": { "pandas": Package("pandas", "pandas", "0.23", "py36"), "pytest": Package("pytest", "pytest", "0.1", "py36"), }, "pip": {}, } mocker.patch("conda_env_tracker.env.get_dependencies", return_value=dependencies) mocker.patch("conda_env_tracker.env.EnvIO.export_packages") history = History.create( name=name, channels=Channels([channel]), packages=PackageRevision.create(packages, dependencies=dependencies), logs=Logs(create_cmd), actions=Actions.create(name="test-update", specs=["pandas"], channels=Channels([channel])), diff=Diff.create(packages, dependencies=dependencies), debug=Debug(), ) mocker.patch( "conda_env_tracker.main.Environment.read", return_value=Environment(name=name, history=history), ) return history
def extract_r_packages(self, index: int) -> Optional[Packages]: """Extract R packages with versions (if the version was specified in the log).""" if self[index].startswith(R_COMMAND): r_packages = Packages() install_commands = self[index].split(";")[1:] for command in install_commands: start = command.index('"') + 1 end = command.index('"', start) name = command[start:end] if "version" in command: start = command.index('"', end + 1) + 1 end = command.index('"', start) version = command[start:end] r_packages.append_spec(f"{name}={version}") else: r_packages.append_spec(name) return r_packages return None
def setup_pip_env(setup_env, mocker): """Set up for pip remove""" pip_package_url = "git+ssh://[email protected]/pandas-dev/pandas" pip_dependencies = { "pandas": Package("pandas", pip_package_url), "pytest": Package("pytest", "pytest", "4.0.0"), } env = setup_env["env"] mocker.patch("conda_env_tracker.pip.pip_install") mocker.patch("conda_env_tracker.pip.pip_custom_install") get_package_mock = setup_env["get_package_mock"] initial_conda_packages = setup_env["initial_conda_packages"] get_package_mock.configure_mock(**{ "return_value": { "conda": initial_conda_packages, "pip": pip_dependencies } }) PipHandler(env=env).install(packages=Packages.from_specs("pytest")) custom_package = Package("pandas", pip_package_url) PipHandler(env=env).custom_install(package=custom_package) return setup_env
install_channel_string = ( "--override-channels --strict-channel-priority " + " ".join([f"--channel " + channel for channel in computed_channels])) assert history.packages == expected_packages expected_action.append( f"conda install --name {env.name} pyspark=0.21=py_36 {install_channel_string}" ) assert history.actions == expected_action assert history.logs == expected_log @pytest.mark.parametrize( "packages", (Packages.from_specs("pyspark"), Packages.from_specs("pyspark=0.21=py_36")), ) def test_install_with_default_version_success(setup_env, packages, mocker): env = setup_env["env"] env_io = setup_env["env_io"] expected = setup_env["expected"] get_package_mock = setup_env["get_package_mock"] get_package_mock.configure_mock( return_value={ "conda": { "pyspark": Package("pyspark", "pyspark", "0.21", "py_36"), "pandas": Package("pandas", "pandas", "0.23", "py_36"), } })
"""Test PipHandler""" # pylint: disable=redefined-outer-name import copy import pytest from conda_env_tracker.packages import Package, Packages from conda_env_tracker.pip import PipHandler, PIP_DEFAULT_INDEX_URL @pytest.mark.parametrize( "packages, pip_dependencies", [ ( Packages.from_specs("pytest"), { "pytest": Package("pytest", "pytest", "4.0.0") }, ), ( Packages.from_specs("pytest==4.0.0"), { "pytest": Package("pytest", "pytest==4.0.0", "4.0.0") }, ), ( Packages.from_specs(["pytest", "colorama"]), { "pytest": Package("pytest", "pytest", "4.0.0"), "colorama": Package("colorama", "colorama", "1.0.0"), },
install_channel_string = ( "--override-channels --strict-channel-priority " + " ".join([f"--channel " + channel for channel in computed_channels]) ) assert history.packages == expected_packages expected_action.append( f"conda install --name {env.name} pyspark=0.21=py_36 {install_channel_string}" ) assert history.actions == expected_action assert history.logs == expected_log @pytest.mark.parametrize( "packages", (Packages.from_specs("pyspark"), Packages.from_specs("pyspark=0.21=py_36")), ) def test_install_with_default_version_success(setup_env, packages, mocker): env = setup_env["env"] env_io = setup_env["env_io"] expected = setup_env["expected"] get_package_mock = setup_env["get_package_mock"] get_package_mock.configure_mock( return_value={ "conda": { "pyspark": Package("pyspark", "pyspark", "0.21", "py_36"), "pandas": Package("pandas", "pandas", "0.23", "py_36"), } } )
def test_pip_remove_package_missing_from_env(setup_pip_env): env = setup_pip_env["env"] packages = Packages.from_specs("this_is_a_nonexistant_package") with pytest.raises(PipRemoveError): PipHandler(env=env).remove(packages=packages)
def test_export_install_r_no_r_packages(): actual = r.export_install_r(Packages()) expected = "" assert actual == expected
from conda_env_tracker.channels import Channels from conda_env_tracker.gateways.io import USER_ENVS_DIR from conda_env_tracker.history import Debug, Diff, History, Logs, PackageRevision from conda_env_tracker.env import Environment from conda_env_tracker.errors import CondaEnvTrackerPushError, PUSH_ERROR_STR from conda_env_tracker.packages import Package, Packages from conda_env_tracker.push import push @pytest.fixture( params=[ { "env_name": "test-push-success", "push_should_fail": False, "local_packages": Packages.from_specs(["pandas", "pytest"]), "local_logs": [ "conda create --name test-push-success pandas", "conda install --name test-push-success pytest", ], "local_actions": [ "conda create --name test-push-success pandas=0.23.0=py36", "conda install --name test-push-success pytest=4.0.0=py36", ], "remote_packages": Packages.from_specs(["pandas", "pylint"]), "remote_logs": ["conda create --name test-push-success pandas"], "remote_actions": [ "conda create --name test-push-success pandas=0.23.0=py36" ], }, {
from conda_env_tracker.gateways.io import USER_ENVS_DIR from conda_env_tracker.history import History, HistoryPackages, Logs, Channels, Debug from conda_env_tracker.env import Environment from conda_env_tracker.errors import CondaEnvTrackerPushError, PUSH_ERROR_STR from conda_env_tracker.packages import Package, Packages from conda_env_tracker.push import push @pytest.fixture(params=[ { "env_name": "test-push-success", "push_should_fail": False, "local_packages": Packages.from_specs(["pandas", "pytest"]), "local_logs": [ "conda create --name test-push-success pandas", "conda install --name test-push-success pytest", ], "local_actions": [ "conda create --name test-push-success pandas=0.23=py36", "conda install --name test-push-success pytest=0.11=py36", ], "remote_packages": Packages.from_specs(["pandas", "pylint"]), "remote_logs": ["conda create --name test-push-success pandas"], "remote_actions": ["conda create --name test-push-success pandas=0.23=py36"], }, {
@pytest.mark.parametrize("spec", ["numpy", "pytest=0.26", "num-py=0.1=hbdcb_4"]) def test_extract_packages_from_logs(spec): """Test parsing the packges from action item""" log = Logs(f"conda install --name test {spec}") packages = log.extract_packages(index=0, packages=Packages.from_specs(spec)) assert packages[0] == Package.from_spec(spec) @pytest.mark.parametrize( "log, expected", [ ("conda remove --name test python", Packages.from_specs("python")), ( "conda remove --name test2 python pandas", Packages.from_specs(["python", "pandas"]), ), ("pip uninstall pandas", Packages.from_specs("pandas")), ("pip uninstall pandas numpy", Packages.from_specs(["pandas", "numpy" ])), ( f"{R_COMMAND} -e 'remove.packages(c(\"dplyr\"))'", Packages.from_specs("dplyr"), ), ( f'{R_COMMAND} -e \'remove.packages(c("dplyr","testthat"))\'', Packages.from_specs(["dplyr", "testthat"]), ),
def extract_packages(self, index: int) -> Packages: """Return the packages for the action item""" package_expression = re.compile("([a-z0-9-_.]+=[a-z0-9_=.]+)") return Packages.from_specs( [spec for spec in package_expression.findall(self[index])])