def test_conda_create_and_install_and_remove_pip_stuff(monkeypatch): monkeypatch_conda_not_to_use_links(monkeypatch) def do_test(dirname): envdir = os.path.join(dirname, "myenv") # don't specify a python version so we use the one we already have # in the root env, otherwise this might take forever. conda_api.create(prefix=envdir, pkgs=['python']) assert os.path.isdir(envdir) assert os.path.isdir(os.path.join(envdir, "conda-meta")) # test that we can install a package via pip assert not os.path.exists(os.path.join(envdir, FLAKE8_BINARY)) pip_api.install(prefix=envdir, pkgs=['flake8']) assert os.path.exists(os.path.join(envdir, FLAKE8_BINARY)) # list what was installed installed = pip_api.installed(prefix=envdir) assert 'flake8' in installed assert installed['flake8'][0] == 'flake8' assert installed['flake8'][1] is not None # test that we can remove it again pip_api.remove(prefix=envdir, pkgs=['flake8']) assert not os.path.exists(os.path.join(envdir, FLAKE8_BINARY)) # no longer in the installed list installed = pip_api.installed(prefix=envdir) assert 'flake8' not in installed with_directory_contents(dict(), do_test)
def test_prepare_project_scoped_env_with_packages(monkeypatch): monkeypatch_conda_not_to_use_links(monkeypatch) def prepare_project_scoped_env_with_packages(dirname): project = Project(dirname) environ = minimal_environ(PROJECT_DIR=dirname) result = prepare_without_interaction(project, environ=environ, env_spec_name='bootstrap-env') assert result envs_dir = os.path.join(dirname, "envs") env_name = 'bootstrap-env' prefix = os.path.join(envs_dir, env_name) installed = conda_api.installed(prefix) assert 'bokeh' not in installed deps = ['ipython', 'numpy', 'pip'] for pkg in deps: assert pkg in installed deps += ['bokeh'] # Preparing it again with new packages added should add those project.project_file.set_value('packages', deps) project.project_file.save() environ = minimal_environ(PROJECT_DIR=dirname) result = prepare_without_interaction(project, environ=environ) assert result prefix = result.environ[conda_env_var] installed = conda_api.installed(prefix) for pkg in deps: assert pkg in installed installed_pip = pip_api.installed(prefix) assert 'flake8' in installed_pip # Preparing it again with a bogus package should fail deps = project.project_file.get_value('packages') project.project_file.set_value(['packages'], deps + ['boguspackage']) project.project_file.save() environ = minimal_environ(PROJECT_DIR=dirname) result = prepare_without_interaction(project, environ=environ) assert not result with_directory_contents_completing_project_file({ DEFAULT_PROJECT_FILENAME: """ env_specs: bootstrap-env: packages: - python=3.7 - ipython - numpy=1.15 - pip: - flake8 """ }, prepare_project_scoped_env_with_packages)
def test_prepare_and_unprepare_project_scoped_env(monkeypatch): monkeypatch_conda_not_to_use_links(monkeypatch) def prepare_project_scoped_env(dirname): project = Project(dirname) fake_old_path = "foo" + os.pathsep + "bar" environ = dict(PROJECT_DIR=dirname, PATH=fake_old_path) result = prepare_without_interaction(project, environ=environ) assert result expected_env = os.path.join(dirname, "envs", "default") if platform.system() == 'Windows': expected_new_path = expected_env + os.pathsep + os.path.join( expected_env, script_dir) + os.pathsep + os.path.join( expected_env, "Library", "bin") + os.pathsep + "foo" + os.pathsep + "bar" else: expected_new_path = os.path.join( expected_env, script_dir) + os.pathsep + "foo" + os.pathsep + "bar" expected = dict(PROJECT_DIR=project.directory_path, PATH=expected_new_path) conda_api.environ_set_prefix(expected, expected_env) expected == result.environ assert os.path.exists(os.path.join(expected_env, "conda-meta")) conda_meta_mtime = os.path.getmtime( os.path.join(expected_env, "conda-meta")) # bare minimum default env shouldn't include these # (contrast with the test later where we list them in # requirements) installed = conda_api.installed(expected_env) assert 'ipython' not in installed assert 'numpy' not in installed # Prepare it again should no-op (use the already-existing environment) environ = dict(PROJECT_DIR=dirname, PATH=fake_old_path) result = prepare_without_interaction(project, environ=environ) assert result expected = dict(PROJECT_DIR=project.directory_path, PATH=expected_new_path) conda_api.environ_set_prefix(expected, expected_env) assert expected == result.environ assert conda_meta_mtime == os.path.getmtime( os.path.join(expected_env, "conda-meta")) # Now unprepare status = unprepare(project, result) assert status, status.errors assert status.status_description == ( 'Deleted environment files in %s.' % (expected_env)) assert status.errors == [] assert not os.path.exists(expected_env) with_directory_contents_completing_project_file( dict(), prepare_project_scoped_env)
def test_prepare_and_unprepare_project_scoped_env(monkeypatch): monkeypatch_conda_not_to_use_links(monkeypatch) def prepare_project_scoped_env(dirname): project = Project(dirname) fake_old_path = "foo" + os.pathsep + "bar" environ = dict(PROJECT_DIR=dirname, PATH=fake_old_path) result = prepare_without_interaction(project, environ=environ) assert result expected_env = os.path.join(dirname, "envs", "bootstrap-env") if platform.system() == 'Windows': expected_new_path = expected_env + os.pathsep + os.path.join( expected_env, script_dir) + os.pathsep + os.path.join(expected_env, "Library", "bin") + os.pathsep + "foo" + os.pathsep + "bar" else: expected_new_path = os.path.join(expected_env, script_dir) + os.pathsep + "foo" + os.pathsep + "bar" expected = dict(PROJECT_DIR=project.directory_path, PATH=expected_new_path, BOOTSTRAP_ENV_PREFIX=expected_env) conda_api.environ_set_prefix(expected, expected_env) expected == result.environ assert os.path.exists(os.path.join(expected_env, "conda-meta")) conda_meta_mtime = os.path.getmtime(os.path.join(expected_env, "conda-meta")) # bare minimum bootstrap-env env shouldn't include these # (contrast with the test later where we list them in # requirements) installed = conda_api.installed(expected_env) assert 'ipython' not in installed assert 'numpy' not in installed # Prepare it again should no-op (use the already-existing environment) environ = dict(PROJECT_DIR=dirname, PATH=fake_old_path) result = prepare_without_interaction(project, environ=environ) assert result expected = dict(PROJECT_DIR=project.directory_path, PATH=expected_new_path) conda_api.environ_set_prefix(expected, expected_env) assert conda_meta_mtime == os.path.getmtime(os.path.join(expected_env, "conda-meta")) # Now unprepare status = unprepare(project, result) assert status # todo: this differs from standard CondaEnvProvider assert status.status_description == 'Success.' assert status.errors == [] assert not os.path.exists(expected_env) with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: """ env_specs: bootstrap-env: packages: - python """ }, prepare_project_scoped_env)
def test_prepare_project_scoped_env_with_packages(monkeypatch): monkeypatch_conda_not_to_use_links(monkeypatch) def prepare_project_scoped_env_with_packages(dirname): project = Project(dirname) environ = minimal_environ(PROJECT_DIR=dirname) result = prepare_without_interaction(project, environ=environ) assert result prefix = result.environ[conda_env_var] installed = conda_api.installed(prefix) assert 'ipython' in installed assert 'numpy' in installed assert 'bokeh' not in installed # Preparing it again with new packages added should add those deps = project.project_file.get_value('packages') project.project_file.set_value('packages', deps + ['bokeh']) project.project_file.save() environ = minimal_environ(PROJECT_DIR=dirname) result = prepare_without_interaction(project, environ=environ) assert result prefix = result.environ[conda_env_var] installed = conda_api.installed(prefix) assert 'ipython' in installed assert 'numpy' in installed assert 'bokeh' in installed installed_pip = pip_api.installed(prefix) assert 'flake8' in installed_pip # Preparing it again with a bogus package should fail deps = project.project_file.get_value('packages') project.project_file.set_value(['packages'], deps + ['boguspackage']) project.project_file.save() environ = minimal_environ(PROJECT_DIR=dirname) result = prepare_without_interaction(project, environ=environ) assert not result with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: """ packages: - python=3.7 - ipython - numpy=1.15 - pip - pip: - flake8 """ }, prepare_project_scoped_env_with_packages)
def test_timestamp_file_ignores_failed_write(monkeypatch): monkeypatch_conda_not_to_use_links(monkeypatch) spec = test_spec def do_test(dirname): from codecs import open as real_open envdir = os.path.join(dirname, spec.name) manager = DefaultCondaManager(frontend=NullFrontend()) counts = dict(calls=0) def mock_open(*args, **kwargs): counts['calls'] += 1 if counts['calls'] == 1: raise IOError("did not open") else: return real_open(*args, **kwargs) monkeypatch.setattr('codecs.open', mock_open) # this should NOT throw but also should not write the # timestamp file (we ignore errors) filename = manager._timestamp_file(envdir, spec) assert filename.startswith(envdir) assert not os.path.exists(filename) manager._write_timestamp_file(envdir, spec) assert not os.path.exists(filename) # the second time we really wsrite it (this is to prove we # are looking at the right filename) manager._write_timestamp_file(envdir, spec) assert os.path.exists(filename) # check on the file contents with real_open(filename, 'r', encoding='utf-8') as f: content = json.loads(f.read()) assert dict(anaconda_project_version=version) == content with_directory_contents(dict(), do_test)
def test_pip_errors(monkeypatch): monkeypatch_conda_not_to_use_links(monkeypatch) def do_test(dirname): envdir = os.path.join(dirname, "myenv") conda_api.create(prefix=envdir, pkgs=['python']) # no packages to install with pytest.raises(TypeError) as excinfo: pip_api.install(prefix=envdir, pkgs=[]) assert 'must specify a list' in repr(excinfo.value) # no packages to remove with pytest.raises(TypeError) as excinfo: pip_api.remove(prefix=envdir, pkgs=[]) assert 'must specify a list' in repr(excinfo.value) # pip command not installed from os.path import exists as real_exists def mock_exists(path): if path.endswith("pip") or path.endswith("pip.exe"): return False else: return real_exists(path) monkeypatch.setattr('os.path.exists', mock_exists) with pytest.raises(pip_api.PipNotInstalledError) as excinfo: pip_api.install(prefix=envdir, pkgs=['foo']) assert 'command is not installed in the environment' in repr( excinfo.value) installed = pip_api.installed(prefix=envdir) assert dict( ) == installed # with pip not installed, no packages are listed. # pip command exits nonzero error_script = """from __future__ import print_function import sys print("TEST_ERROR", file=sys.stderr) sys.exit(1) """ def get_failed_command(prefix, extra_args): return tmp_script_commandline(error_script) monkeypatch.setattr( 'anaconda_project.internal.pip_api._get_pip_command', get_failed_command) with pytest.raises(pip_api.PipError) as excinfo: pip_api.install(prefix=envdir, pkgs=['flake8']) assert 'TEST_ERROR' in repr(excinfo.value) # pip command exits zero printing stuff on stderr error_message_but_success_script = """from __future__ import print_function import sys print("TEST_ERROR", file=sys.stderr) sys.exit(0) """ def get_failed_command(prefix, extra_args): return tmp_script_commandline(error_message_but_success_script) monkeypatch.setattr( 'anaconda_project.internal.pip_api._get_pip_command', get_failed_command) pip_api.install(prefix=envdir, pkgs=['flake8']) # cannot exec pip def mock_popen(args, stdout=None, stderr=None): raise OSError("failed to exec") monkeypatch.setattr('subprocess.Popen', mock_popen) with pytest.raises(pip_api.PipError) as excinfo: pip_api.install(prefix=envdir, pkgs=['flake8']) assert 'failed to exec' in repr(excinfo.value) with_directory_contents(dict(), do_test)
def test_timestamp_file_works(monkeypatch): monkeypatch_conda_not_to_use_links(monkeypatch) spec = test_spec def do_test(dirname): envdir = os.path.join(dirname, spec.name) manager = DefaultCondaManager(frontend=NullFrontend()) def print_timestamps(when): newest_in_prefix = 0 for d in manager._timestamp_comparison_directories(envdir): try: t = os.path.getmtime(d) except Exception: t = 0 if t > newest_in_prefix: newest_in_prefix = t timestamp_fname = manager._timestamp_file(envdir, spec) try: timestamp_file = os.path.getmtime(timestamp_fname) except Exception: timestamp_file = 0 print("%s: timestamp file %d prefix %d diff %g" % (when, timestamp_file, newest_in_prefix, newest_in_prefix - timestamp_file)) print_timestamps("before env creation") assert not os.path.isdir(envdir) assert not os.path.exists(os.path.join(envdir, IPYTHON_BINARY)) assert not os.path.exists(os.path.join(envdir, PYINSTRUMENT_BINARY)) assert not manager._timestamp_file_up_to_date(envdir, spec) deviations = manager.find_environment_deviations(envdir, spec) assert set(deviations.missing_packages) == {'python', 'ipython'} assert deviations.missing_pip_packages == ('pyinstrument', ) assert not deviations.ok manager.fix_environment_deviations(envdir, spec, deviations) print_timestamps("after fixing deviations") assert os.path.isdir(envdir) assert os.path.isdir(os.path.join(envdir, "conda-meta")) assert os.path.exists(os.path.join(envdir, IPYTHON_BINARY)) assert os.path.exists(os.path.join(envdir, PYINSTRUMENT_BINARY)) assert manager._timestamp_file_up_to_date(envdir, spec) called = [] from anaconda_project.internal.pip_api import installed as real_pip_installed from anaconda_project.internal.conda_api import installed as real_conda_installed def traced_pip_installed(*args, **kwargs): called.append(("pip_api.installed", args, kwargs)) return real_pip_installed(*args, **kwargs) monkeypatch.setattr('anaconda_project.internal.pip_api.installed', traced_pip_installed) def trace_conda_installed(*args, **kwargs): called.append(("conda_api.installed", args, kwargs)) return real_conda_installed(*args, **kwargs) monkeypatch.setattr('anaconda_project.internal.conda_api.installed', trace_conda_installed) deviations = manager.find_environment_deviations(envdir, spec) assert [] == called assert deviations.missing_packages == () assert deviations.missing_pip_packages == () assert deviations.ok assert manager._timestamp_file_up_to_date(envdir, spec) # now modify conda-meta and check that we DO call the package managers time.sleep(1.1) # be sure we are in a new second conda_meta_dir = os.path.join(envdir, "conda-meta") print("conda-meta original timestamp: %d" % os.path.getmtime(conda_meta_dir)) inside_conda_meta = os.path.join(conda_meta_dir, "thing.txt") with codecs.open(inside_conda_meta, 'w', encoding='utf-8') as f: f.write(u"This file should change the mtime on conda-meta\n") print("file inside conda-meta %d and conda-meta itself %d" % (os.path.getmtime(inside_conda_meta), os.path.getmtime(conda_meta_dir))) os.remove(inside_conda_meta) print_timestamps("after touching conda-meta") assert not manager._timestamp_file_up_to_date(envdir, spec) deviations = manager.find_environment_deviations(envdir, spec) assert len(called) == 2 assert deviations.missing_packages == () assert deviations.missing_pip_packages == () # deviations should not be ok (due to timestamp) assert not deviations.ok assert not manager._timestamp_file_up_to_date(envdir, spec) # we want to be sure we update the timestamp file even though # there wasn't any actual work to do manager.fix_environment_deviations(envdir, spec, deviations) print_timestamps("after fixing deviations 2") assert manager._timestamp_file_up_to_date(envdir, spec) with_directory_contents(dict(), do_test)
def test_conda_create_and_install_and_remove(monkeypatch): monkeypatch_conda_not_to_use_links(monkeypatch) spec = test_spec assert spec.conda_packages == ('ipython', 'python=3.8') assert spec.pip_packages == ('pyinstrument', ) spec_with_phony_pip_package = EnvSpec( name='myenv', conda_packages=['ipython'], pip_packages=['pyinstrument', 'nope_not_a_thing'], channels=[]) assert spec_with_phony_pip_package.conda_packages == ('ipython', ) assert spec_with_phony_pip_package.pip_packages == ('pyinstrument', 'nope_not_a_thing') assert spec_with_phony_pip_package.pip_package_names_set == set( ('pyinstrument', 'nope_not_a_thing')) # package url is supposed to be on a nonexistent port, if it # causes a problem we need to mock spec_with_bad_url_pip_package = EnvSpec( name='myenv', conda_packages=['ipython'], pip_packages=[ 'pyinstrument', 'https://127.0.0.1:24729/nope#egg=phony' ], channels=[]) assert spec_with_bad_url_pip_package.conda_packages == ('ipython', ) assert spec_with_bad_url_pip_package.pip_packages == ( 'pyinstrument', 'https://127.0.0.1:24729/nope#egg=phony') assert spec_with_bad_url_pip_package.pip_package_names_set == set( ('pyinstrument', 'phony')) spec_with_old_ipython = EnvSpec(name='myenv', conda_packages=['ipython=7.10.1'], pip_packages=['pyinstrument'], channels=[]) assert spec_with_old_ipython.conda_packages == ('ipython=7.10.1', ) spec_with_bokeh = EnvSpec(name='myenv', conda_packages=['bokeh'], pip_packages=['pyinstrument'], channels=[]) assert spec_with_bokeh.conda_packages == ('bokeh', ) spec_with_bokeh_and_old_ipython = EnvSpec( name='myenv', conda_packages=['bokeh', 'ipython=7.10.1'], pip_packages=['pyinstrument'], channels=[]) assert spec_with_bokeh_and_old_ipython.conda_packages == ( 'bokeh', 'ipython=7.10.1', ) def do_test(dirname): from codecs import open as real_open envdir = os.path.join(dirname, spec.name) manager = DefaultCondaManager(frontend=NullFrontend()) is_readonly = dict(readonly=False) def mock_open(*args, **kwargs): if is_readonly['readonly']: raise IOError("did not open") return real_open(*args, **kwargs) monkeypatch.setattr('codecs.open', mock_open) assert not os.path.isdir(envdir) assert not os.path.exists(os.path.join(envdir, IPYTHON_BINARY)) assert not os.path.exists(os.path.join(envdir, FLAKE8_BINARY)) assert not manager._timestamp_file_up_to_date(envdir, spec) deviations = manager.find_environment_deviations(envdir, spec) assert set(deviations.missing_packages) == {'python', 'ipython'} assert deviations.missing_pip_packages == ('pyinstrument', ) # with create=False, we won't create the env with pytest.raises(CondaManagerError) as excinfo: manager.fix_environment_deviations(envdir, spec, deviations, create=False) assert 'does not exist' in str(excinfo.value) assert not os.path.isdir(envdir) # now create the env manager.fix_environment_deviations(envdir, spec, deviations) assert os.path.isdir(envdir) assert os.path.isdir(os.path.join(envdir, "conda-meta")) assert os.path.exists(os.path.join(envdir, IPYTHON_BINARY)) assert os.path.exists(os.path.join(envdir, PYINSTRUMENT_BINARY)) assert manager._timestamp_file_up_to_date(envdir, spec) assert not manager._timestamp_file_up_to_date( envdir, spec_with_phony_pip_package) # test bad pip package throws error deviations = manager.find_environment_deviations( envdir, spec_with_phony_pip_package) assert deviations.missing_packages == () assert deviations.wrong_version_packages == () assert deviations.missing_pip_packages == ('nope_not_a_thing', ) with pytest.raises(CondaManagerError) as excinfo: manager.fix_environment_deviations(envdir, spec_with_phony_pip_package, deviations) assert 'Failed to install missing pip packages' in str(excinfo.value) assert not manager._timestamp_file_up_to_date( envdir, spec_with_phony_pip_package) # test bad url package throws error deviations = manager.find_environment_deviations( envdir, spec_with_bad_url_pip_package) assert deviations.missing_packages == () assert deviations.wrong_version_packages == () assert deviations.missing_pip_packages == ('phony', ) with pytest.raises(CondaManagerError) as excinfo: manager.fix_environment_deviations(envdir, spec_with_bad_url_pip_package, deviations) assert 'Failed to install missing pip packages' in str(excinfo.value) assert not manager._timestamp_file_up_to_date( envdir, spec_with_bad_url_pip_package) # test we notice wrong ipython version AND missing bokeh AND readonly environment is_readonly['readonly'] = True deviations = manager.find_environment_deviations( envdir, spec_with_bokeh_and_old_ipython) assert deviations.missing_packages == ('bokeh', ) assert deviations.wrong_version_packages == ('ipython', ) assert deviations.unfixable is_readonly['readonly'] = False # test we notice only missing bokeh deviations = manager.find_environment_deviations( envdir, spec_with_bokeh) assert deviations.missing_packages == ('bokeh', ) assert deviations.wrong_version_packages == () assert not deviations.unfixable # test we notice wrong ipython version and can downgrade deviations = manager.find_environment_deviations( envdir, spec_with_old_ipython) assert deviations.missing_packages == () assert deviations.wrong_version_packages == ('ipython', ) assert not deviations.unfixable manager.fix_environment_deviations(envdir, spec_with_old_ipython, deviations) assert manager._timestamp_file_up_to_date(envdir, spec_with_old_ipython) deviations = manager.find_environment_deviations( envdir, spec_with_old_ipython) assert deviations.missing_packages == () assert deviations.wrong_version_packages == () # update timestamp; this doesn't re-upgrade because `spec` doesn't # specify an ipython version assert not manager._timestamp_file_up_to_date(envdir, spec) deviations = manager.find_environment_deviations(envdir, spec) assert deviations.missing_packages == () assert deviations.wrong_version_packages == () # fix_environment_deviations should be a no-op on readonly envs # with no deviations, in particular the time stamp file should # not be changed and therefore not be up to date is_readonly['readonly'] = True manager.fix_environment_deviations(envdir, spec, deviations) assert not manager._timestamp_file_up_to_date(envdir, spec) # when the environment is readwrite, the timestamp file should # be updated is_readonly['readonly'] = False manager.fix_environment_deviations(envdir, spec, deviations) assert manager._timestamp_file_up_to_date(envdir, spec) deviations = manager.find_environment_deviations(envdir, spec) assert deviations.missing_packages == () assert deviations.wrong_version_packages == () # test that we can remove a package assert manager._timestamp_file_up_to_date(envdir, spec) time.sleep( 1) # removal is fast enough to break our timestamp resolution manager.remove_packages(prefix=envdir, packages=['ipython']) assert not os.path.exists(os.path.join(envdir, IPYTHON_BINARY)) assert not manager._timestamp_file_up_to_date(envdir, spec) # test for error removing with pytest.raises(CondaManagerError) as excinfo: manager.remove_packages(prefix=envdir, packages=['ipython']) # different versions of conda word this differently message = str(excinfo.value) valid_strings = ('no packages found to remove', 'Package not found', "named 'ipython' found to remove", 'PackagesNotFoundError:', "is missing from the environment") assert any(s in message for s in valid_strings) assert not manager._timestamp_file_up_to_date(envdir, spec) # test failure to exec pip def mock_call_pip(*args, **kwargs): raise pip_api.PipError("pip fail") monkeypatch.setattr('anaconda_project.internal.pip_api._call_pip', mock_call_pip) with pytest.raises(CondaManagerError) as excinfo: deviations = manager.find_environment_deviations(envdir, spec) assert 'pip failed while listing' in str(excinfo.value) with_directory_contents(dict(), do_test)
def test_conda_create_and_install_and_remove(monkeypatch): monkeypatch_conda_not_to_use_links(monkeypatch) spec = test_spec assert spec.conda_packages == ('ipython', ) assert spec.pip_packages == ('flake8', ) spec_with_phony_pip_package = EnvSpec( name='myenv', conda_packages=['ipython'], pip_packages=['flake8', 'nope_not_a_thing'], channels=[]) assert spec_with_phony_pip_package.conda_packages == ('ipython', ) assert spec_with_phony_pip_package.pip_packages == ('flake8', 'nope_not_a_thing') assert spec_with_phony_pip_package.pip_package_names_set == set( ('flake8', 'nope_not_a_thing')) # package url is supposed to be on a nonexistent port, if it # causes a problem we need to mock spec_with_bad_url_pip_package = EnvSpec( name='myenv', conda_packages=['ipython'], pip_packages=['flake8', 'https://127.0.0.1:24729/nope#egg=phony'], channels=[]) assert spec_with_bad_url_pip_package.conda_packages == ('ipython', ) assert spec_with_bad_url_pip_package.pip_packages == ( 'flake8', 'https://127.0.0.1:24729/nope#egg=phony') assert spec_with_bad_url_pip_package.pip_package_names_set == set( ('flake8', 'phony')) def do_test(dirname): envdir = os.path.join(dirname, spec.name) manager = DefaultCondaManager() assert not os.path.isdir(envdir) assert not os.path.exists(os.path.join(envdir, IPYTHON_BINARY)) assert not os.path.exists(os.path.join(envdir, FLAKE8_BINARY)) assert not manager._timestamp_file_up_to_date(envdir, spec) deviations = manager.find_environment_deviations(envdir, spec) assert deviations.missing_packages == ('ipython', ) assert deviations.missing_pip_packages == ('flake8', ) manager.fix_environment_deviations(envdir, spec, deviations) assert os.path.isdir(envdir) assert os.path.isdir(os.path.join(envdir, "conda-meta")) assert os.path.exists(os.path.join(envdir, IPYTHON_BINARY)) assert os.path.exists(os.path.join(envdir, FLAKE8_BINARY)) assert manager._timestamp_file_up_to_date(envdir, spec) assert not manager._timestamp_file_up_to_date( envdir, spec_with_phony_pip_package) # test bad pip package throws error deviations = manager.find_environment_deviations( envdir, spec_with_phony_pip_package) assert deviations.missing_packages == () assert deviations.missing_pip_packages == ('nope_not_a_thing', ) with pytest.raises(CondaManagerError) as excinfo: manager.fix_environment_deviations(envdir, spec_with_phony_pip_package, deviations) assert 'Failed to install missing pip packages' in str(excinfo.value) assert not manager._timestamp_file_up_to_date( envdir, spec_with_phony_pip_package) # test bad url package throws error deviations = manager.find_environment_deviations( envdir, spec_with_bad_url_pip_package) assert deviations.missing_packages == () assert deviations.missing_pip_packages == ('phony', ) with pytest.raises(CondaManagerError) as excinfo: manager.fix_environment_deviations(envdir, spec_with_bad_url_pip_package, deviations) assert 'Failed to install missing pip packages' in str(excinfo.value) assert not manager._timestamp_file_up_to_date( envdir, spec_with_bad_url_pip_package) # test that we can remove a package assert manager._timestamp_file_up_to_date(envdir, spec) manager.remove_packages(prefix=envdir, packages=['ipython']) assert not os.path.exists(os.path.join(envdir, IPYTHON_BINARY)) assert not manager._timestamp_file_up_to_date(envdir, spec) # test for error removing with pytest.raises(CondaManagerError) as excinfo: manager.remove_packages(prefix=envdir, packages=['ipython']) # different versions of conda word this differently assert 'no packages found to remove' in str( excinfo.value) or 'Package not found' in str(excinfo.value) assert not manager._timestamp_file_up_to_date(envdir, spec) # test failure to exec pip def mock_call_pip(*args, **kwargs): raise pip_api.PipError("pip fail") monkeypatch.setattr('anaconda_project.internal.pip_api._call_pip', mock_call_pip) with pytest.raises(CondaManagerError) as excinfo: deviations = manager.find_environment_deviations(envdir, spec) assert 'pip failed while listing' in str(excinfo.value) with_directory_contents(dict(), do_test)