def test_current_platform_unsupported_by_env_spec(monkeypatch): lock_set = CondaLockSet(package_specs_by_platform={'all': []}, platforms=conda_api.default_platforms) spec = EnvSpec(name='myenv', conda_packages=['ipython'], pip_packages=['flake8'], channels=[], platforms=['commodore-64', 'apple-2'], lock_set=lock_set) def do_test(dirname): envdir = os.path.join(dirname, spec.name) manager = DefaultCondaManager(frontend=NullFrontend()) deviations = manager.find_environment_deviations(envdir, spec) error = "Env spec 'myenv' does not support current platform %s (it supports: apple-2, commodore-64)" % \ conda_api.current_platform() assert error == deviations.summary with pytest.raises(CondaManagerError) as excinfo: manager.fix_environment_deviations(envdir, spec, deviations=deviations) assert str( excinfo.value).startswith("Unable to update environment at ") with_directory_contents(dict(), do_test)
def default_env_specs_func(): return [ EnvSpec(name='abc', conda_packages=['anaconda'], pip_packages=[], channels=['mychannel'], description="ABC", inherit_from_names=(), inherit_from=()), EnvSpec(name='xyz', conda_packages=['foo'], pip_packages=[], channels=['bar'], description="XYZ", inherit_from_names=(), inherit_from=()) ]
def check(dirname): prefix = os.path.join(dirname, "myenv") os.makedirs(os.path.join(prefix, 'conda-meta')) def mock_installed(prefix): return {'bokeh': ('bokeh', '0.12.4', '1')} monkeypatch.setattr('anaconda_project.internal.conda_api.installed', mock_installed) spec_with_matching_bokeh = EnvSpec(name='myenv', conda_packages=['bokeh=0.12.4=1'], pip_packages=[], channels=[]) spec_with_more_vague_bokeh = EnvSpec(name='myenv', conda_packages=['bokeh=0.12'], pip_packages=[], channels=[]) spec_with_unspecified_bokeh = EnvSpec(name='myenv', conda_packages=['bokeh'], pip_packages=[], channels=[]) spec_with_wrong_version_bokeh = EnvSpec(name='myenv', conda_packages=['bokeh=0.12.3'], pip_packages=[], channels=[]) spec_with_wrong_build_bokeh = EnvSpec(name='myenv', conda_packages=['bokeh=0.12.4=0'], pip_packages=[], channels=[]) manager = DefaultCondaManager(frontend=NullFrontend()) deviations = manager.find_environment_deviations(prefix, spec_with_matching_bokeh) assert deviations.missing_packages == () assert deviations.wrong_version_packages == () deviations = manager.find_environment_deviations(prefix, spec_with_more_vague_bokeh) assert deviations.missing_packages == () assert deviations.wrong_version_packages == () deviations = manager.find_environment_deviations(prefix, spec_with_unspecified_bokeh) assert deviations.missing_packages == () assert deviations.wrong_version_packages == () deviations = manager.find_environment_deviations(prefix, spec_with_wrong_version_bokeh) assert deviations.missing_packages == () assert deviations.wrong_version_packages == ('bokeh', ) deviations = manager.find_environment_deviations(prefix, spec_with_wrong_build_bokeh) assert deviations.missing_packages == () assert deviations.wrong_version_packages == ('bokeh', )
def test_platforms_affect_hash(): with_platforms_spec = EnvSpec(name="foo", conda_packages=['a', 'b'], pip_packages=['c', 'd'], channels=['x', 'y'], platforms=('linux-64', )) without_platforms_spec = EnvSpec( name=with_platforms_spec.name, conda_packages=with_platforms_spec.conda_packages, pip_packages=with_platforms_spec.pip_packages, channels=with_platforms_spec.channels, platforms=()) assert with_platforms_spec.logical_hash != with_platforms_spec.locked_hash assert with_platforms_spec.logical_hash != with_platforms_spec.import_hash assert without_platforms_spec.logical_hash == without_platforms_spec.locked_hash assert without_platforms_spec.logical_hash == without_platforms_spec.import_hash
def test_lock_set_affects_hash(): lock_set = CondaLockSet({'all': ['a=1.0=1']}, platforms=['linux-32', 'linux-64', 'osx-64', 'win-32', 'win-64']) with_lock_spec = EnvSpec( name="foo", conda_packages=['a', 'b'], pip_packages=['c', 'd'], channels=['x', 'y'], lock_set=lock_set) without_lock_spec = EnvSpec( name=with_lock_spec.name, conda_packages=with_lock_spec.conda_packages, pip_packages=with_lock_spec.pip_packages, channels=with_lock_spec.channels, lock_set=None) assert with_lock_spec.conda_packages != with_lock_spec.conda_packages_for_create assert without_lock_spec.conda_packages == without_lock_spec.conda_packages_for_create assert without_lock_spec.logical_hash == without_lock_spec.locked_hash assert with_lock_spec.logical_hash != with_lock_spec.locked_hash assert with_lock_spec.logical_hash == without_lock_spec.logical_hash assert without_lock_spec.locked_hash == without_lock_spec.import_hash assert with_lock_spec.locked_hash != with_lock_spec.import_hash
def check_missing_package(dirname): requirement = CondaEnvRequirement( registry=RequirementsRegistry(), env_specs=dict(default=EnvSpec( 'default', ['boguspackage', 'boguspackage2'], []))) project_dir_disable_dedicated_env(dirname) local_state = LocalStateFile.load_for_directory(dirname) environ = minimal_environ(PROJECT_DIR=dirname) status = requirement.check_status( environ, local_state, 'default', UserConfigOverrides(inherited_env=environ.get(conda_env_var))) assert "Conda environment is missing packages: boguspackage, boguspackage2" == status.status_description
def test_lock_set_affects_name_sets(): lock_set = CondaLockSet({ 'all': ['a=1.0=1', 'q=2.0=2'] }, platforms=['linux-32', 'linux-64', 'osx-64', 'win-32', 'win-64']) spec = EnvSpec( name="foo", conda_packages=['a', 'b'], pip_packages=['c', 'd'], channels=['x', 'y'], lock_set=lock_set) assert ('a', 'b') == spec.conda_packages assert ('a=1.0=1', 'q=2.0=2') == spec.conda_packages_for_create assert set(['a', 'b']) == spec.conda_package_names_set assert set(['a', 'q']) == spec.conda_package_names_for_create_set
def test_overwrite_packages_with_lock_set(): lock_set = CondaLockSet( {'all': ['a=1.0=1']}, platforms=['linux-32', 'linux-64', 'osx-64', 'win-32', 'win-64']) spec = EnvSpec(name="foo", conda_packages=['a', 'b'], pip_packages=['c', 'd'], channels=['x', 'y'], lock_set=lock_set) # package "b" is now ignored assert ('a=1.0=1', ) == spec.conda_packages_for_create
def check(filename): spec = _load_environment_yml(filename) assert spec is not None changed = EnvSpec( name=spec.name, conda_packages=spec.conda_packages[1:], pip_packages=spec.pip_packages, channels=spec.channels) (desynced, name) = _find_out_of_sync_importable_spec([changed], os.path.dirname(filename)) assert desynced is not None assert desynced.logical_hash == spec.logical_hash assert name == os.path.basename(filename)
def check_fails_while_listing_installed(dirname): def sabotaged_installed_command(prefix): from anaconda_project.internal import conda_api raise conda_api.CondaError("sabotage!") monkeypatch.setattr('anaconda_project.internal.conda_api.installed', sabotaged_installed_command) project_dir_disable_dedicated_env(dirname) local_state = LocalStateFile.load_for_directory(dirname) requirement = CondaEnvRequirement( registry=RequirementsRegistry(), env_specs=dict( default=EnvSpec('default', ['not_a_real_package'], []))) environ = minimal_environ(PROJECT_DIR=dirname) status = requirement.check_status( environ, local_state, 'default', UserConfigOverrides(inherited_env=environ.get(conda_env_var))) assert status.status_description.startswith( "Conda failed while listing installed packages in ") assert status.status_description.endswith(": sabotage!")
if platform.system() == 'Windows': PYTHON_BINARY = "python.exe" IPYTHON_BINARY = "Scripts\\ipython.exe" FLAKE8_BINARY = "Scripts\\flake8.exe" # Use a different package from the test env due to weird CI path/env errors PYINSTRUMENT_BINARY = "Scripts\\pyinstrument.exe" else: PYTHON_BINARY = "bin/python" IPYTHON_BINARY = "bin/ipython" FLAKE8_BINARY = "bin/flake8" # Use a different package from the test env due to weird CI path/env errors PYINSTRUMENT_BINARY = "bin/pyinstrument" test_spec = EnvSpec(name='myenv', conda_packages=['ipython', 'python=3.8'], pip_packages=['pyinstrument'], channels=[]) def test_current_platform_unsupported_by_env_spec(monkeypatch): lock_set = CondaLockSet(package_specs_by_platform={'all': []}, platforms=conda_api.default_platforms) spec = EnvSpec(name='myenv', conda_packages=['ipython'], pip_packages=['flake8'], channels=[], platforms=['commodore-64', 'apple-2'], lock_set=lock_set) def do_test(dirname): envdir = os.path.join(dirname, spec.name)
def test_diff_from(): spec1 = EnvSpec(name="foo", conda_packages=['a', 'b'], pip_packages=['c', 'd'], channels=['x', 'y']) spec2 = EnvSpec(name="bar", conda_packages=['a', 'b', 'q'], pip_packages=['c'], channels=['x', 'y', 'z']) diff = spec2.diff_from(spec1) assert ' channels:\n x\n y\n + z\n a\n b\n+ q\n pip:\n c\n - d' == diff
def _empty_default_requirement(): return CondaEnvRequirement( registry=RequirementsRegistry(), env_specs=dict(default=EnvSpec('default', [], [])))
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)
import anaconda_project.internal.pip_api as pip_api from anaconda_project.internal.test.tmpfile_utils import with_directory_contents from anaconda_project.internal.test.test_conda_api import monkeypatch_conda_not_to_use_links if platform.system() == 'Windows': PYTHON_BINARY = "python.exe" IPYTHON_BINARY = "Scripts\ipython.exe" FLAKE8_BINARY = "Scripts\\flake8.exe" else: PYTHON_BINARY = "bin/python" IPYTHON_BINARY = "bin/ipython" FLAKE8_BINARY = "bin/flake8" test_spec = EnvSpec(name='myenv', conda_packages=['ipython'], pip_packages=['flake8'], channels=[]) 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=[])
def _empty_default_env_spec(): return (EnvSpec(name="default", channels=[], conda_packages=()), )
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)