diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a1cde2a2..5e8a4797 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ Here's how to work on the code: specific versions, or the warnings and errors may have changed which will make tests fail. The simplest way to get the right environment is `conda env create environment.yml` but you can - also look in that file and/or .travis.yml/appveyor.xml to see + also look in that file and/or `.travis.yml/appveyor.xml` to see which packages are neded and then create the dev environment by hand as you see fit. * NOTE: Do make sure to respect the version pins found in the diff --git a/anaconda_project/conda_manager.py b/anaconda_project/conda_manager.py index ff8f0606..b34a6b2f 100644 --- a/anaconda_project/conda_manager.py +++ b/anaconda_project/conda_manager.py @@ -12,7 +12,7 @@ from copy import deepcopy import difflib -from anaconda_project.yaml_file import (_CommentedMap, _CommentedSeq, _block_style_all_nodes) +from anaconda_project.yaml_file import (_CommentedMap, _CommentedSeq, _block_style_all_nodes, YamlFile) from anaconda_project.internal.metaclass import with_metaclass from anaconda_project.internal import conda_api from anaconda_project.internal import pip_api @@ -394,7 +394,7 @@ def diff_from(self, old): packages_diff.extend(map(lambda x: x, diff)) if packages_diff: - packages_diff = [' packages:'] + packages_diff + packages_diff = [f' {YamlFile.pkg_key}:'] + packages_diff if old is None: old_platforms = [] @@ -442,7 +442,7 @@ def supports_current_platform(self): """Whether we have locked deps for the current platform.""" return self.enabled and conda_api.current_platform() in self.platforms - def to_json(self): + def to_json(self, pkg_key=YamlFile.pkg_key): """JSON/YAML version of the lock set.""" yaml_dict = _CommentedMap() @@ -462,7 +462,7 @@ def to_json(self): for package in self._package_specs_by_platform[platform]: packages.append(package) packages_dict[platform] = packages - yaml_dict['packages'] = packages_dict + yaml_dict[pkg_key] = packages_dict _block_style_all_nodes(yaml_dict) return yaml_dict diff --git a/anaconda_project/env_spec.py b/anaconda_project/env_spec.py index aaff7b8b..525ef4b0 100644 --- a/anaconda_project/env_spec.py +++ b/anaconda_project/env_spec.py @@ -19,7 +19,7 @@ from anaconda_project.internal.py2_compat import is_string from anaconda_project import conda_manager -from anaconda_project.yaml_file import _load_string, _save_file, _YAMLError, ryaml +from anaconda_project.yaml_file import _load_string, _save_file, _YAMLError, ryaml, YamlFile class EnvSpec(object): @@ -390,7 +390,7 @@ def filter_context(items): return True - def to_json(self): + def to_json(self, pkg_key=YamlFile.pkg_key): """Get JSON for an anaconda-project.yml env spec section.""" # Note that we use _conda_packages (only the packages we # introduce ourselves) rather than conda_packages @@ -406,14 +406,19 @@ def to_json(self): # have ordering... OrderedDict doesn't work because the # yaml saver saves them as some "!!omap" nonsense. Other # than ordering, the formatting isn't even preserved here. - template_json = ryaml.load("something:\n description: null\n" + " packages: []\n" + " channels: []\n", + template_json = ryaml.load(""" +something: + description: null + : [] + channels: [] +""".replace('', pkg_key), Loader=ryaml.RoundTripLoader) if self._description is not None: template_json['something']['description'] = self._description else: del template_json['something']['description'] - template_json['something']['packages'] = packages + template_json['something'][pkg_key] = packages template_json['something']['channels'] = channels # usually "platforms" will be global so don't clutter diff --git a/anaconda_project/internal/cli/test/test_command_commands.py b/anaconda_project/internal/cli/test/test_command_commands.py index 79abd361..70d9acfc 100644 --- a/anaconda_project/internal/cli/test/test_command_commands.py +++ b/anaconda_project/internal/cli/test/test_command_commands.py @@ -193,7 +193,7 @@ def mock_console_input(prompt): with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: ''}, check_ask_type) -def test_add_command_specifying_notebook(monkeypatch, capsys): +def test_add_command_specifying_notebook(monkeypatch, capsys, pkg_key): def check_specifying_notebook(dirname): args = Args('notebook', 'test', 'file.ipynb', directory=dirname) res = main(args) @@ -206,11 +206,11 @@ def check_specifying_notebook(dirname): assert command['env_spec'] == 'default' assert len(command.keys()) == 2 - with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: 'packages:\n - notebook\n'}, + with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: f'{pkg_key}:\n - notebook\n'}, check_specifying_notebook) -def test_add_command_guessing_notebook(monkeypatch, capsys): +def test_add_command_guessing_notebook(monkeypatch, capsys, pkg_key): def check_guessing_notebook(dirname): args = Args(None, 'test', 'file.ipynb', directory=dirname) res = main(args) @@ -225,7 +225,7 @@ def check_guessing_notebook(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: 'packages:\n - notebook\n', + DEFAULT_PROJECT_FILENAME: f'{pkg_key}:\n - notebook\n', 'file.ipynb': "{}" }, check_guessing_notebook) @@ -332,7 +332,7 @@ def test_remove_command_with_project_file_problems(capsys, monkeypatch): append_dir=True) -def test_remove_command(monkeypatch, capsys): +def test_remove_command(monkeypatch, capsys, pkg_key): def check(dirname): code = _parse_args_and_run_subcommand(['anaconda-project', 'remove-command', 'test', '--directory', dirname]) assert code == 0 @@ -347,7 +347,7 @@ def check(dirname): assert err == '' with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: 'packages: ["notebook"]\ncommands:\n test:\n notebook: file.ipynb\n'}, check) + {DEFAULT_PROJECT_FILENAME: f'{pkg_key}: ["notebook"]\ncommands:\n test:\n notebook: file.ipynb\n'}, check) def test_remove_command_missing(monkeypatch, capsys): @@ -378,7 +378,7 @@ def check_empty_project(dirname): with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: ""}, check_empty_project) -def test_list_commands(capsys): +def test_list_commands(capsys, pkg_key): def check_empty_project(dirname): code = _parse_args_and_run_subcommand(['anaconda-project', 'list-commands', '--directory', dirname]) assert code == 0 @@ -404,13 +404,13 @@ def check_empty_project(dirname): " bokeh_app: test.py\n" " run_notebook:\n" " notebook: test.ipynb\n" - "packages:\n" + f"{pkg_key}:\n" " - bokeh\n" " - notebook\n") }, check_empty_project) -def test_list_default_command(capsys): +def test_list_default_command(capsys, pkg_key): def check_empty_project(dirname): code = _parse_args_and_run_subcommand(['anaconda-project', 'list-default-command', '--directory', dirname]) assert code == 0 @@ -426,7 +426,7 @@ def check_empty_project(dirname): " bokeh_app: test.py\n" " 0second:\n" " notebook: test.ipynb\n" - "packages:\n" + f"{pkg_key}:\n" " - bokeh\n" " - notebook\n") }, check_empty_project) diff --git a/anaconda_project/internal/cli/test/test_environment_commands.py b/anaconda_project/internal/cli/test/test_environment_commands.py index 113db068..93a5e7ab 100644 --- a/anaconda_project/internal/cli/test/test_environment_commands.py +++ b/anaconda_project/internal/cli/test/test_environment_commands.py @@ -164,7 +164,7 @@ def check(dirname): with_directory_contents_completing_project_file(dict(), check) -def test_remove_env_spec_fails(capsys, monkeypatch): +def test_remove_env_spec_fails(capsys, monkeypatch, pkg_key): def check(dirname): from shutil import rmtree as real_rmtree _monkeypatch_pwd(monkeypatch, dirname) @@ -192,13 +192,24 @@ def mock_remove(path, ignore_errors=False, onerror=None): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: 'env_specs:\n foo:\n channels: []\n packages:\n - bar\n' + - ' baz:\n channels: []\n packages:\n - bar\n', - 'envs/foo/bin/test': 'code here' + DEFAULT_PROJECT_FILENAME: + """ +env_specs: + foo: + channels: [] + : + - bar + baz: + channels: [] + : + - bar +""".replace('', pkg_key), + 'envs/foo/bin/test': + 'code here' }, check) -def test_remove_env_spec(capsys, monkeypatch): +def test_remove_env_spec(capsys, monkeypatch, pkg_key): def check(dirname): _monkeypatch_pwd(monkeypatch, dirname) @@ -212,13 +223,24 @@ def check(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: 'env_specs:\n foo:\n channels: []\n packages:\n - bar\n' + - ' bar:\n channels: []\n packages:\n - baz\n', - 'envs/foo/bin/test': 'code here' + DEFAULT_PROJECT_FILENAME: + """ +env_specs: + foo: + channels: [] + : + - bar + bar: + channels: [] + : + - baz +""".replace('', pkg_key), + 'envs/foo/bin/test': + 'code here' }, check) -def test_remove_only_env_spec(capsys, monkeypatch): +def test_remove_only_env_spec(capsys, monkeypatch, pkg_key): def check(dirname): _monkeypatch_pwd(monkeypatch, dirname) @@ -231,12 +253,20 @@ def check(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: 'env_specs:\n foo:\n channels: []\n packages:\n - bar\n', - 'envs/foo/bin/test': 'code here' + DEFAULT_PROJECT_FILENAME: + """ +env_specs: + foo: + channels: [] + : + - bar +""".replace('', pkg_key), + 'envs/foo/bin/test': + 'code here' }, check) -def test_remove_env_spec_in_use(capsys, monkeypatch): +def test_remove_env_spec_in_use(capsys, monkeypatch, pkg_key): def check(dirname): _monkeypatch_pwd(monkeypatch, dirname) @@ -250,7 +280,8 @@ def check(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ + DEFAULT_PROJECT_FILENAME: + """ commands: foo: unix: envs/foo/bin/test @@ -258,13 +289,14 @@ def check(dirname): env_specs: other: - packages: + : - hello bar: - packages: + : - boo -""", - 'envs/foo/bin/test': 'code here' +""".replace('', pkg_key), + 'envs/foo/bin/test': + 'code here' }, check) @@ -278,7 +310,7 @@ def test_remove_env_spec_with_project_file_problems(capsys, monkeypatch): ['anaconda-project', 'remove-env-spec', '--name', 'foo']) -def test_export_env_spec(capsys, monkeypatch): +def test_export_env_spec(capsys, monkeypatch, pkg_key): def check(dirname): _monkeypatch_pwd(monkeypatch, dirname) @@ -292,13 +324,24 @@ def check(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: 'env_specs:\n foo:\n channels: []\n packages:\n - bar\n' + - ' bar:\n channels: []\n packages:\n - baz\n', - 'envs/foo/bin/test': 'code here' + DEFAULT_PROJECT_FILENAME: + """ +env_specs: + foo: + channels: [] + : + - bar + bar: + channels: [] + : + - baz +""".replace('', pkg_key), + 'envs/foo/bin/test': + 'code here' }, check) -def test_export_env_spec_default_name(capsys, monkeypatch): +def test_export_env_spec_default_name(capsys, monkeypatch, pkg_key): def check(dirname): _monkeypatch_pwd(monkeypatch, dirname) @@ -312,13 +355,24 @@ def check(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: 'env_specs:\n foo:\n channels: []\n packages:\n - bar\n' + - ' bar:\n channels: []\n packages:\n - baz\n', - 'envs/foo/bin/test': 'code here' + DEFAULT_PROJECT_FILENAME: + """ +env_specs: + foo: + channels: [] + : + - bar + bar: + channels: [] + : + - baz +""".replace('', pkg_key), + 'envs/foo/bin/test': + 'code here' }, check) -def test_export_env_spec_no_filename(capsys, monkeypatch): +def test_export_env_spec_no_filename(capsys, monkeypatch, pkg_key): def check(dirname): _monkeypatch_pwd(monkeypatch, dirname) @@ -331,9 +385,20 @@ def check(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: 'env_specs:\n foo:\n channels: []\n packages:\n - bar\n' + - ' bar:\n channels: []\n packages:\n - baz\n', - 'envs/foo/bin/test': 'code here' + DEFAULT_PROJECT_FILENAME: + """ +env_specs: + foo: + channels: [] + : + - bar + bar: + channels: [] + : + - baz +""".replace('', pkg_key), + 'envs/foo/bin/test': + 'code here' }, check) @@ -384,7 +449,7 @@ def check(dirname): with_directory_contents_completing_project_file(dict(), check) -def test_add_packages_to_specific_environment(capsys, monkeypatch): +def test_add_packages_to_specific_environment(capsys, monkeypatch, pkg_key): def check(dirname): _monkeypatch_pwd(monkeypatch, dirname) params = _monkeypatch_add_packages(monkeypatch, SimpleStatus(success=True, description='Installed ok.')) @@ -404,9 +469,9 @@ def check(dirname): {DEFAULT_PROJECT_FILENAME: """ env_specs: foo: - packages: + : - bar -"""}, check) +""".replace('', pkg_key)}, check) def test_add_pip_packages_to_specific_environment(capsys, monkeypatch): @@ -429,7 +494,7 @@ def check(dirname): {DEFAULT_PROJECT_FILENAME: """ env_specs: foo: - packages: + : - bar """}, check) @@ -498,7 +563,7 @@ def check(dirname): with_directory_contents_completing_project_file(dict(), check) -def test_add_platforms_to_specific_environment(capsys, monkeypatch): +def test_add_platforms_to_specific_environment(capsys, monkeypatch, pkg_key): def check(dirname): _monkeypatch_pwd(monkeypatch, dirname) params = _monkeypatch_add_platforms(monkeypatch, SimpleStatus(success=True, description='Installed ok.')) @@ -517,9 +582,9 @@ def check(dirname): {DEFAULT_PROJECT_FILENAME: """ env_specs: foo: - packages: + : - bar -"""}, check) +""".replace('', pkg_key)}, check) def test_remove_platforms_from_all_environments(capsys, monkeypatch): @@ -558,7 +623,7 @@ def check(dirname): with_directory_contents_completing_project_file(dict(), check) -def test_list_environments(capsys, monkeypatch): +def test_list_environments(capsys, monkeypatch, pkg_key): def check_list_not_empty(dirname): code = _parse_args_and_run_subcommand(['anaconda-project', 'list-env-specs', '--directory', dirname]) @@ -577,13 +642,16 @@ def check_list_not_empty(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: ('env_specs:\n' - ' foo:\n' - ' packages:\n' - ' - bar\n' - ' bar:\n' - ' packages:\n' - ' - bar\n') + DEFAULT_PROJECT_FILENAME: + """ +env_specs: + foo: + : + - bar + bar: + : + - bar +""".replace('', pkg_key) }, check_list_not_empty) @@ -629,7 +697,7 @@ def check_missing_env(dirname): with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: ""}, check_missing_env) -def _test_list_packages(capsys, env, expected_conda_deps, expected_pip_deps): +def _test_list_packages(capsys, env, expected_conda_deps, expected_pip_deps, pkg_key): def check_list_not_empty(dirname): params = ['anaconda-project', 'list-packages', '--directory', dirname] if env is not None: @@ -648,30 +716,43 @@ def check_list_not_empty(dirname): expected_pip_deps) assert out == expected_out - project_contents = ('env_specs:\n' - ' foo:\n' - ' packages:\n' - ' - requests\n' - ' - flask\n' - ' bar:\n' - ' packages:\n' - ' - httplib\n' - ' - django\n\n' - 'packages:\n' - ' - mandatory_package\n' - ' - pip:\n' - ' - mandatory_pip_package\n') + project_contents = """ +env_specs: + foo: + : + - requests + - flask + bar: + : + - httplib + - django\n +: + - mandatory_package + - pip: + - mandatory_pip_package""".replace('', pkg_key) with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: project_contents}, check_list_not_empty) -def test_list_packages_from_env(capsys): - _test_list_packages(capsys, 'bar', '\ndjango\nhttplib\nmandatory_package\n\n', '\nmandatory_pip_package\n\n') - _test_list_packages(capsys, 'foo', '\nflask\nmandatory_package\nrequests\n\n', '\nmandatory_pip_package\n\n') - - -def test_list_packages_from_env_default(capsys): - _test_list_packages(capsys, None, '\nflask\nmandatory_package\nrequests\n\n', '\nmandatory_pip_package\n\n') +def test_list_packages_from_env(capsys, pkg_key): + _test_list_packages(capsys, + 'bar', + '\ndjango\nhttplib\nmandatory_package\n\n', + '\nmandatory_pip_package\n\n', + pkg_key=pkg_key) + _test_list_packages(capsys, + 'foo', + '\nflask\nmandatory_package\nrequests\n\n', + '\nmandatory_pip_package\n\n', + pkg_key=pkg_key) + + +def test_list_packages_from_env_default(capsys, pkg_key): + _test_list_packages(capsys, + None, + '\nflask\nmandatory_package\nrequests\n\n', + '\nmandatory_pip_package\n\n', + pkg_key=pkg_key) def test_list_packages_with_project_file_problems(capsys, monkeypatch): diff --git a/anaconda_project/internal/cli/test/test_prepare.py b/anaconda_project/internal/cli/test/test_prepare.py index d11e6ca5..2e563959 100644 --- a/anaconda_project/internal/cli/test/test_prepare.py +++ b/anaconda_project/internal/cli/test/test_prepare.py @@ -116,7 +116,7 @@ def main_redis_url(dirname): assert "All ports from 6380 to 6449 were in use" in err -def test_prepare_command_choose_environment(capsys, monkeypatch): +def test_prepare_command_choose_environment(capsys, monkeypatch, pkg_key): def mock_conda_create(prefix, pkgs, channels, stdout_callback, stderr_callback): from anaconda_project.internal.makedirs import makedirs_ok_if_exists metadir = os.path.join(prefix, "conda-meta") @@ -146,12 +146,12 @@ def check_prepare_choose_environment(dirname): """ env_specs: foo: - packages: + : - nonexistent_foo bar: - packages: + : - nonexistent_bar -""" +""".replace("", pkg_key) }, check_prepare_choose_environment) out, err = capsys.readouterr() @@ -160,7 +160,7 @@ def check_prepare_choose_environment(dirname): assert err == "" -def test_prepare_command_all_environments(capsys, monkeypatch): +def test_prepare_command_all_environments(capsys, monkeypatch, pkg_key): def mock_conda_create(prefix, pkgs, channels, stdout_callback, stderr_callback): from anaconda_project.internal.makedirs import makedirs_ok_if_exists metadir = os.path.join(prefix, "conda-meta") @@ -192,12 +192,12 @@ def check_prepare_choose_environment(dirname): """ env_specs: foo: - packages: + : - nonexistent_foo bar: - packages: + : - nonexistent_bar -""" +""".replace("", pkg_key) }, check_prepare_choose_environment) out, err = capsys.readouterr() @@ -206,7 +206,7 @@ def check_prepare_choose_environment(dirname): assert err == "" -def test_prepare_command_all_environments_refresh(capsys, monkeypatch): +def test_prepare_command_all_environments_refresh(capsys, monkeypatch, pkg_key): def mock_conda_create(prefix, pkgs, channels, stdout_callback, stderr_callback): from anaconda_project.internal.makedirs import makedirs_ok_if_exists metadir = os.path.join(prefix, "conda-meta") @@ -242,12 +242,12 @@ def check_prepare_choose_environment(dirname): """ env_specs: foo: - packages: + : - nonexistent_foo bar: - packages: + : - nonexistent_bar -""" +""".replace("", pkg_key) }, check_prepare_choose_environment) out, err = capsys.readouterr() @@ -258,7 +258,7 @@ def check_prepare_choose_environment(dirname): assert err == "" -def test_prepare_command_default_environment_refresh(capsys, monkeypatch): +def test_prepare_command_default_environment_refresh(capsys, monkeypatch, pkg_key): def mock_conda_create(prefix, pkgs, channels, stdout_callback, stderr_callback): from anaconda_project.internal.makedirs import makedirs_ok_if_exists metadir = os.path.join(prefix, "conda-meta") @@ -284,10 +284,11 @@ def check_prepare_choose_environment(dirname): foo_package_json = os.path.join(default_envdir, "conda-meta", "nonexistent_foo-0.1-pyNN.json") assert os.path.isfile(foo_package_json) - with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ -packages: + with_directory_contents_completing_project_file( + {DEFAULT_PROJECT_FILENAME: """ +: - nonexistent_foo -"""}, check_prepare_choose_environment) +""".replace("", pkg_key)}, check_prepare_choose_environment) out, err = capsys.readouterr() assert out == ("The project is ready to run commands.\n" + @@ -297,7 +298,7 @@ def check_prepare_choose_environment(dirname): assert err == "" -def test_prepare_command_choose_environment_does_not_exist(capsys): +def test_prepare_command_choose_environment_does_not_exist(capsys, pkg_key): def check_prepare_choose_environment_does_not_exist(dirname): project_dir_disable_dedicated_env(dirname) result = _parse_args_and_run_subcommand( @@ -316,17 +317,17 @@ def check_prepare_choose_environment_does_not_exist(dirname): """ env_specs: foo: - packages: + : - nonexistent_foo bar: - packages: + : - nonexistent_bar -""" +""".replace("", pkg_key) }, check_prepare_choose_environment_does_not_exist) @pytest.mark.slow -def test_prepare_command_choose_command_chooses_env_spec(capsys): +def test_prepare_command_choose_command_chooses_env_spec(capsys, pkg_key): def check(dirname): project_dir_disable_dedicated_env(dirname) result = _parse_args_and_run_subcommand( @@ -351,10 +352,10 @@ def check(dirname): """ env_specs: foo: - packages: + : - nonexistent_foo bar: - packages: + : - nonexistent_bar commands: with_foo: @@ -364,7 +365,7 @@ def check(dirname): conda_app_entry: python --version env_spec: bar -""" +""".replace("", pkg_key) }, check) @@ -605,7 +606,7 @@ def mock_console_input(prompt, encrypted): @pytest.mark.slow -def test_no_ask_conda_prefix_interactively(monkeypatch, capsys): +def test_no_ask_conda_prefix_interactively(monkeypatch, capsys, pkg_key): def check(dirname): project_dir_disable_dedicated_env(dirname) @@ -628,12 +629,12 @@ def mock_console_input(prompt, encrypted): with_directory_contents_completing_project_file( {DEFAULT_PROJECT_FILENAME: """ -packages: +: - nonexistent_package_name -"""}, check) +""".replace("", pkg_key)}, check) -def test_display_suggestions(monkeypatch, capsys): +def test_display_suggestions(monkeypatch, capsys, pkg_key): def check(dirname): project_dir_disable_dedicated_env(dirname) @@ -660,7 +661,8 @@ def mock_console_input(prompt, encrypted): """ == out assert '' == err - with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ -packages: [] + with_directory_contents_completing_project_file( + {DEFAULT_PROJECT_FILENAME: """ +: [] weird_field: 42 -"""}, check) +""".replace("", pkg_key)}, check) diff --git a/anaconda_project/project.py b/anaconda_project/project.py index 8c3c21ad..c340d005 100644 --- a/anaconda_project/project.py +++ b/anaconda_project/project.py @@ -529,11 +529,11 @@ def _update_lock_sets(self, problems, lock_file): platforms = self._parse_platforms(problems, lock_file, lock_set) conda_packages_by_platform = dict() - packages_by_platform = lock_set.get('packages', {}) + packages_by_platform = lock_set.get(lock_file.pkg_key, {}) if not is_dict(packages_by_platform): _file_problem( problems, lock_file, - "'packages:' section in env spec '%s' in lock file should be a dictionary, found %r" % + f"'{lock_file.pkg_key}:' section in env spec '%s' in lock file should be a dictionary, found %r" % (name, packages_by_platform)) continue @@ -564,6 +564,9 @@ def _update_lock_sets(self, problems, lock_file): self.lock_sets[name] = lock_set_object def _update_env_specs(self, problems, project_file, lock_file): + # Should we check/set lock_file.pkg_key? + pkg_key = project_file.pkg_key + def _parse_string_list(parent_dict, key, what): return self._parse_string_list(problems, project_file, parent_dict, key, what) @@ -576,14 +579,13 @@ def _parse_platforms(parent_dict): def _parse_packages(parent_dict): # dependencies allows environment.yml-like project files. It is not # expected to have both dependencies and packages - pkg_key = 'dependencies' if project_file.get_value('dependencies') else 'packages' - return self._parse_packages(problems, project_file, pkg_key, parent_dict) + return self._parse_packages(problems, project_file, project_file.pkg_key, parent_dict) (shared_deps, shared_pip_deps) = _parse_packages(project_file.root) shared_channels = _parse_channels(project_file.root) shared_platforms = _parse_platforms(project_file.root) - _default_env_spec = CommentedMap([('default', CommentedMap([('packages', []), ('channels', [])]))]) + _default_env_spec = CommentedMap([('default', CommentedMap([(pkg_key, []), ('channels', [])]))]) env_specs = project_file.get_value('env_specs', default=_default_env_spec) first_env_spec_name = None @@ -776,8 +778,8 @@ def set_env_spec_platforms(project): for platform in env_spec.lock_set.platforms: conda_packages = env_spec.lock_set.package_specs_for_platform(platform) if len(conda_packages) == 0: - text = ("Lock file lists no packages for env spec '%s' on platform %s") % (env_spec.name, - platform) + text = (f"Lock file lists no {lock_file.pkg_key} for env spec '%s' on platform %s" % + (env_spec.name, platform)) problems.append(ProjectProblem(text=text, filename=lock_file.filename, only_a_suggestion=True)) else: # If conda ever had RPM-like "Obsoletes" then this situation _may_ happen @@ -789,8 +791,9 @@ def set_env_spec_platforms(project): lock_set_names.add(parsed.name) unlocked_names = env_spec.conda_package_names_set - lock_set_names if len(unlocked_names) > 0: - text = "Lock file is missing %s packages for env spec %s on %s (%s)" % ( - len(unlocked_names), env_spec.name, platform, ",".join(sorted(list(unlocked_names)))) + text = ( + f"Lock file is missing %s {lock_file.pkg_key} for env spec %s on %s (%s)" % + (len(unlocked_names), env_spec.name, platform, ",".join(sorted(list(unlocked_names))))) problems.append( ProjectProblem(text=text, filename=lock_file.filename, only_a_suggestion=True)) @@ -838,7 +841,8 @@ def set_env_spec_platforms(project): prompt = "Overwrite env spec %s with the changes from %s?" % (importable_spec.name, importable_filename) def overwrite_env_spec_from_importable(project): - project.project_file.set_value(['env_specs', importable_spec.name], importable_spec.to_json()) + project.project_file.set_value(['env_specs', importable_spec.name], + importable_spec.to_json(pkg_key=project.pkg_key)) def remember_no_import_importable(project): project.project_file.set_value(['skip_imports', 'environment'], importable_spec.logical_hash) @@ -859,7 +863,8 @@ def remember_no_import_importable(project): # import, above. def add_default_env_spec(project): default_spec = _anaconda_default_env_spec(self.global_base_env_spec) - project.project_file.set_value(['env_specs', default_spec.name], default_spec.to_json()) + project.project_file.set_value(['env_specs', default_spec.name], + default_spec.to_json(pkg_key=project.pkg_key)) # This section should now be inaccessible # since env_spec will be added at runtime if missing @@ -1134,16 +1139,16 @@ def _verify_command_dependencies(self, problems, project_file): def add_packages_to_env_spec(project): env_dict = project.project_file.get_value(['env_specs', env_spec.name]) assert env_dict is not None - packages = env_dict.get('packages', []) + packages = env_dict.get(project_file.pkg_key, []) for m in missing: # m would already be in there if we fixed the same env spec # twice because two commands used it, for example. if m not in packages: packages.append(m) - project.project_file.set_value(['env_specs', env_spec.name, 'packages'], packages) + project.project_file.set_value(['env_specs', env_spec.name, project_file.pkg_key], packages) problem = ProjectProblem( - text=("Command %s uses env spec %s which does not have the packages: %s" % + text=(f"Command %s uses env spec %s which does not have the {project_file.pkg_key}: %s" % (command.name, env_spec.name, ", ".join(missing))), filename=project_file.filename, fix_prompt=("Add %s to env spec %s in %s?" % @@ -1199,6 +1204,11 @@ def load_default_specs(): assert isinstance(frontend, Frontend) self._frontends = [frontend] + @property + def pkg_key(self): + """Return the project `pkg_key` - 'packages' or 'dependencies'.""" + return self.project_file.pkg_key + def _updated_cache(self): self._config_cache.update(self._project_file, self._lock_file) return self._config_cache @@ -1579,7 +1589,7 @@ def publication_info(self): json['commands'] = commands envs = dict() for key, env in self.env_specs.items(): - envs[key] = dict(packages=list(env.conda_packages), + envs[key] = dict({self._project_file.pkg_key: list(env.conda_packages)}, channels=list(env.channels), description=env.description, locked=env.lock_set.enabled, diff --git a/anaconda_project/project_file.py b/anaconda_project/project_file.py index 0d1ee771..4a65a537 100644 --- a/anaconda_project/project_file.py +++ b/anaconda_project/project_file.py @@ -92,7 +92,7 @@ class ProjectFile(YamlFile): # before your code runs. # Use `anaconda-project add-packages` to add packages. # -packages: [] +: [] # # In the channels section, list any Conda channel URLs to be searched @@ -119,7 +119,7 @@ class ProjectFile(YamlFile): # Use `anaconda-project add-env-spec` to add environment specs. # env_specs: {} -''' +'''.replace('', YamlFile.pkg_key) @classmethod def load_for_directory(cls, directory, default_env_specs_func=_empty_default_env_spec, scan_parents=True): @@ -190,7 +190,7 @@ def _fill_default_content(self, as_json): default_env_specs = self._default_env_specs_func() assert default_env_specs is not None for env_spec in default_env_specs: - as_json['env_specs'][env_spec.name] = env_spec.to_json() + as_json['env_specs'][env_spec.name] = env_spec.to_json(pkg_key=self.pkg_key) if len(default_env_specs) == 1: # if there's only one env spec, keep it for name/description @@ -204,8 +204,8 @@ def move_list_elements(src, dest): dest.extend(src) del src[:] - if 'packages' in spec_json: - move_list_elements(spec_json['packages'], as_json['packages']) + if self.pkg_key in spec_json: + move_list_elements(spec_json[self.pkg_key], as_json[self.pkg_key]) if 'channels' in spec_json: move_list_elements(spec_json['channels'], as_json['channels']) if 'platforms' in spec_json: diff --git a/anaconda_project/project_lock_file.py b/anaconda_project/project_lock_file.py index aa948616..39fc3c45 100644 --- a/anaconda_project/project_lock_file.py +++ b/anaconda_project/project_lock_file.py @@ -136,11 +136,11 @@ def _set_lock_set(self, env_spec_name, lock_set, all_names): single_env['locked'] = False # now set up the one env - as_json = lock_set.to_json() + as_json = lock_set.to_json(pkg_key=self.pkg_key) self.set_value(['env_specs', env_spec_name], as_json) def _add_pip_packages(self, env_spec_name, pip_packages): - self.set_value(['env_specs', env_spec_name, 'packages', 'pip'], pip_packages) + self.set_value(['env_specs', env_spec_name, self.pkg_key, 'pip'], pip_packages) def _disable_locking(self, env_spec_name): """Library-internal method.""" @@ -149,5 +149,5 @@ def _disable_locking(self, env_spec_name): self.unset_value(['env_specs']) else: self.set_value(['env_specs', env_spec_name, 'locked'], False) - self.unset_value(['env_specs', env_spec_name, 'packages']) + self.unset_value(['env_specs', env_spec_name, self.pkg_key]) self.unset_value(['env_specs', env_spec_name, 'platforms']) diff --git a/anaconda_project/project_ops.py b/anaconda_project/project_ops.py index 2d54287c..c21c3571 100644 --- a/anaconda_project/project_ops.py +++ b/anaconda_project/project_ops.py @@ -79,7 +79,7 @@ def create(directory_path, project = Project(directory_path, scan_parents=False) if with_anaconda_package: - project.project_file.set_value('packages', ['anaconda']) + project.project_file.set_value(project.project_file.pkg_key, ['anaconda']) if name is not None: project.project_file.set_value('name', name) if icon is not None: @@ -394,6 +394,9 @@ def _updating_project_lock_file(project): def _update_env_spec(project, name, packages, channels, create, pip=False): + # Get the name of the package key from the file ('dependencies' vs 'packages') + pkg_key = project.project_file.pkg_key + failed = _check_problems(project) if failed is not None: return failed @@ -424,7 +427,7 @@ def _update_env_spec(project, name, packages, channels, create, pip=False): # packages may be a "CommentedSeq" and we don't want to lose the comments, # so don't convert this thing to a regular list. - old_packages = env_dict.get('packages', []) + old_packages = env_dict.get(pkg_key, []) if pip: pip_idx = None for idx, dep in enumerate(old_packages): @@ -480,7 +483,7 @@ def replace_spec(old): else: old_packages.extend(new_specs) - env_dict['packages'] = old_packages + env_dict[pkg_key] = old_packages # channels may be a "CommentedSeq" and we don't want to lose the comments, # so don't convert this thing to a regular list. @@ -678,6 +681,9 @@ def remove_packages(project, env_spec_name, packages, pip=False): # be nicer to rewrite this whole thing when we add version pinning # anyway. + # Get the name of the package key from the file ('dependencies' vs 'packages') + pkg_key = project.project_file.pkg_key + failed = _check_problems(project) if failed is not None: return failed @@ -729,7 +735,7 @@ def envs_to_their_dicts(envs): assert len(env_dicts) > 0 def _get_deps(env_dict, pip=False): - _pkgs = env_dict.get('packages', []) + _pkgs = env_dict.get(pkg_key, []) if pip: pip_dicts = [dep for dep in _pkgs if is_dict(dep) and 'pip' in dep] assert len(pip_dicts) == 1, 'There should only be one pip: key' @@ -742,7 +748,7 @@ def _get_deps(env_dict, pip=False): for env_dict in env_dicts: # packages may be a "CommentedSeq" and we don't want to lose the comments, # so don't convert this thing to a regular list. - old_packages = env_dict.get('packages', []) + old_packages = env_dict.get(pkg_key, []) removed_set = set(packages) if pip: @@ -753,16 +759,16 @@ def _get_deps(env_dict, pip=False): if pip_idx is None: if len(old_packages): - env_dict['packages'].append({'pip': []}) + env_dict[pkg_key].append({'pip': []}) else: - env_dict['packages'] = [{'pip': []}] + env_dict[pkg_key] = [{'pip': []}] else: pip_packages = old_packages.pop(pip_idx)['pip'] _filter_inplace(lambda dep: not (is_string(dep) and dep in removed_set), pip_packages) - env_dict['packages'].append({'pip': pip_packages}) + env_dict[pkg_key].append({'pip': pip_packages}) else: _filter_inplace(lambda dep: not (is_string(dep) and dep in removed_set), old_packages) - env_dict['packages'] = old_packages + env_dict[pkg_key] = old_packages # if we removed any deps from global, add them to the # individual envs that were not supposed to be affected. @@ -771,7 +777,7 @@ def _get_deps(env_dict, pip=False): for env_dict in unaffected_env_dicts: # old_packages may be a "CommentedSeq" and we don't want to lose the comments, # so don't convert this thing to a regular list. - old_packages = env_dict.get('packages', []) + old_packages = env_dict.get(pkg_key, []) if pip: pip_idx = None for idx, dep in enumerate(old_packages): @@ -783,7 +789,7 @@ def _get_deps(env_dict, pip=False): old_packages[pip_idx]['pip'].extend(list(removed_from_global)) else: old_packages.extend(list(removed_from_global)) - env_dict['packages'] = old_packages + env_dict[pkg_key] = old_packages if status_holder.status is not None: project.load() # revert diff --git a/anaconda_project/requirements_registry/providers/test/test_conda_env.py b/anaconda_project/requirements_registry/providers/test/test_conda_env.py index 1f57e7d4..47049d78 100644 --- a/anaconda_project/requirements_registry/providers/test/test_conda_env.py +++ b/anaconda_project/requirements_registry/providers/test/test_conda_env.py @@ -178,7 +178,7 @@ def prepare_project_scoped_env_not_attempted(dirname): @pytest.mark.slow -def test_prepare_project_scoped_env_with_packages(monkeypatch): +def test_prepare_project_scoped_env_with_packages(monkeypatch, pkg_key): monkeypatch_conda_not_to_use_links(monkeypatch) def prepare_project_scoped_env_with_packages(dirname): @@ -224,14 +224,14 @@ def prepare_project_scoped_env_with_packages(dirname): { DEFAULT_PROJECT_FILENAME: """ -packages: +: - python=3.7 - ipython - numpy=1.15 - pip - pip: - flake8 -""" +""".replace('', pkg_key) }, prepare_project_scoped_env_with_packages) @@ -301,7 +301,7 @@ def check(dirname): with_directory_contents_completing_project_file(dict(), check) -def test_configure_different_env_spec(monkeypatch): +def test_configure_different_env_spec(monkeypatch, pkg_key): def mock_conda_create(prefix, pkgs, channels, stdout_callback, stderr_callback): from anaconda_project.internal.makedirs import makedirs_ok_if_exists metadir = os.path.join(prefix, "conda-meta") @@ -346,15 +346,15 @@ def check(dirname): """ env_specs: default: - packages: [] + : [] channels: [] foo: - packages: [] + : [] channels: [] bar: - packages: [] + : [] channels: [] -""" +""".replace('', pkg_key) }, check) @@ -378,7 +378,7 @@ def _readonly_env(env_name, packages): @pytest.mark.slow @pytest.mark.skipif(platform.system() == 'Windows', reason='Windows has a hard time with read-only directories') -def test_clone_readonly_environment_with_deviations(monkeypatch): +def test_clone_readonly_environment_with_deviations(monkeypatch, pkg_key): def clone_readonly_and_prepare(dirname): with _readonly_env(env_name='default', packages=('python=3.7', )) as ro_prefix: readonly = conda_api.installed(ro_prefix) @@ -402,18 +402,21 @@ def clone_readonly_and_prepare(dirname): assert 'requests' in cloned with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: """ -packages: + { + DEFAULT_PROJECT_FILENAME: + """ +: - python=3.7 - requests env_specs: default: {} -"""}, clone_readonly_and_prepare) +""".replace('', pkg_key) + }, clone_readonly_and_prepare) @pytest.mark.slow @pytest.mark.skipif(platform.system() == 'Windows', reason='Windows has a hard time with read-only directories') -def test_replace_readonly_environment_with_deviations(monkeypatch): +def test_replace_readonly_environment_with_deviations(monkeypatch, pkg_key): def replace_readonly_and_prepare(dirname): with _readonly_env(env_name='default', packages=('python=3.7', )) as ro_prefix: readonly = conda_api.installed(ro_prefix) @@ -437,18 +440,21 @@ def replace_readonly_and_prepare(dirname): assert 'requests' in replaced with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: """ -packages: + { + DEFAULT_PROJECT_FILENAME: + """ +: - python=3.7 - requests env_specs: default: {} -"""}, replace_readonly_and_prepare) +""".replace('', pkg_key) + }, replace_readonly_and_prepare) @pytest.mark.slow @pytest.mark.skipif(platform.system() == 'Windows', reason='Windows has a hard time with read-only directories') -def test_fail_readonly_environment_with_deviations_unset_policy(monkeypatch): +def test_fail_readonly_environment_with_deviations_unset_policy(monkeypatch, pkg_key): def clone_readonly_and_prepare(dirname): with _readonly_env(env_name='default', packages=('python=3.7', )) as ro_prefix: readonly = conda_api.installed(ro_prefix) @@ -465,18 +471,21 @@ def clone_readonly_and_prepare(dirname): assert ' Conda environment is missing packages: requests and the environment is read-only' in result.errors with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: """ -packages: + { + DEFAULT_PROJECT_FILENAME: + """ +: - python=3.7 - requests env_specs: default: {} -"""}, clone_readonly_and_prepare) +""".replace('', pkg_key) + }, clone_readonly_and_prepare) @pytest.mark.slow @pytest.mark.skipif(platform.system() == 'Windows', reason='Windows has a hard time with read-only directories') -def test_fail_readonly_environment_with_deviations_set_policy(monkeypatch): +def test_fail_readonly_environment_with_deviations_set_policy(monkeypatch, pkg_key): def clone_readonly_and_prepare(dirname): with _readonly_env(env_name='default', packages=('python=3.7', )) as ro_prefix: readonly = conda_api.installed(ro_prefix) @@ -495,10 +504,13 @@ def clone_readonly_and_prepare(dirname): assert ' Conda environment is missing packages: requests and the environment is read-only' in result.errors with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: """ -packages: + { + DEFAULT_PROJECT_FILENAME: + """ +: - python=3.7 - requests env_specs: default: {} -"""}, clone_readonly_and_prepare) +""".replace('', pkg_key) + }, clone_readonly_and_prepare) diff --git a/anaconda_project/test/test_env_spec.py b/anaconda_project/test/test_env_spec.py index 2e2fa1c9..9f281baa 100644 --- a/anaconda_project/test/test_env_spec.py +++ b/anaconda_project/test/test_env_spec.py @@ -250,7 +250,7 @@ def test_find_out_of_sync_does_not_exist(): assert name is None -def test_to_json(): +def test_to_json(pkg_key): # the stuff from this parent env spec should NOT end up in the JSON hi = EnvSpec(name="hi", conda_packages=['q', 'r'], @@ -265,19 +265,19 @@ def test_to_json(): channels=['x', 'y'], inherit_from_names=('hi', ), inherit_from=(hi, )) - json = spec.to_json() + json = spec.to_json(pkg_key=pkg_key) assert { 'description': "The Foo Spec", 'channels': ['x', 'y'], 'inherit_from': 'hi', - 'packages': ['a', 'b', { + pkg_key: ['a', 'b', { 'pip': ['c', 'd'] }] } == json -def test_to_json_no_description_no_pip_no_inherit(): +def test_to_json_no_description_no_pip_no_inherit(pkg_key): # should be able to jsonify a spec with no description spec = EnvSpec(name="foo", conda_packages=['a', 'b'], @@ -285,26 +285,20 @@ def test_to_json_no_description_no_pip_no_inherit(): channels=['x', 'y'], inherit_from_names=(), inherit_from=()) - json = spec.to_json() + json = spec.to_json(pkg_key=pkg_key) - assert {'channels': ['x', 'y'], 'packages': ['a', 'b']} == json + assert {'channels': ['x', 'y'], pkg_key: ['a', 'b']} == json -def test_to_json_multiple_inheritance(): +def test_to_json_multiple_inheritance(pkg_key): spec = EnvSpec(name="foo", conda_packages=['a', 'b'], pip_packages=['c', 'd'], channels=['x', 'y'], inherit_from_names=('hi', 'hello')) - json = spec.to_json() + json = spec.to_json(pkg_key=pkg_key) - assert { - 'channels': ['x', 'y'], - 'inherit_from': ['hi', 'hello'], - 'packages': ['a', 'b', { - 'pip': ['c', 'd'] - }] - } == json + assert {'channels': ['x', 'y'], 'inherit_from': ['hi', 'hello'], pkg_key: ['a', 'b', {'pip': ['c', 'd']}]} == json def test_diff_from(): diff --git a/anaconda_project/test/test_prepare.py b/anaconda_project/test/test_prepare.py index 3309a176..5549339f 100644 --- a/anaconda_project/test/test_prepare.py +++ b/anaconda_project/test/test_prepare.py @@ -138,9 +138,14 @@ def prepare_system_environ(dirname): if original in ('root', 'base') and updated in ('root', 'base'): print("we have a root/base environment name issue here") continue - if key == 'PATH' and platform.system() == 'Windows': - print("prepare changed PATH on Windows and ideally it would not.") - continue + if key == 'PATH': + if platform.system() == 'Windows': + print("prepare changed PATH on Windows and ideally it would not.") + continue + if result.environ.get("CONDA_STACKED_2", False): + print("prepare changed PATH with stacked environments.") + continue + updated = updated.split(os.pathsep) original = original.split(os.pathsep) print("ORIGINAL {}: {}".format(key, repr(original))) diff --git a/anaconda_project/test/test_project.py b/anaconda_project/test/test_project.py index 2413434d..72331e06 100644 --- a/anaconda_project/test/test_project.py +++ b/anaconda_project/test/test_project.py @@ -242,7 +242,7 @@ def check_problem(dirname): """}, check_problem) -def test_problem_empty_names(): +def test_problem_empty_names(pkg_key): def check_problem(dirname): project = project_no_dedicated_env(dirname) @@ -258,7 +258,7 @@ def _fn(s): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ + f""" variables: ' ': 'thing' downloads: @@ -267,7 +267,7 @@ def _fn(s): ' ': redis env_specs: ' ': - packages: + {pkg_key}: - python commands: ' ': @@ -475,7 +475,7 @@ def check_set_icon(dirname): }, check_set_icon) -def test_get_dependencies_requirements_from_project_file(): +def test_get_package_requirements_from_project_file(pkg_key): def check_get_packages(dirname): project = project_no_dedicated_env(dirname) env = project.env_specs['default'] @@ -501,52 +501,8 @@ def check_get_packages(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ -dependencies: - - foo - - hello >= 1.0 - - world - - pip: - - pip1 - - pip2==1.3 - - pip: - - pip3 - -channels: - - mtv - - hbo - """ - }, check_get_packages) - - -def test_get_package_requirements_from_project_file(): - def check_get_packages(dirname): - project = project_no_dedicated_env(dirname) - env = project.env_specs['default'] - assert env.name == 'default' - assert ("mtv", "hbo") == env.channels - assert ("foo", "hello >= 1.0", "world") == env.conda_packages - assert ("pip1", "pip2==1.3", "pip3") == env.pip_packages - assert set(["foo", "hello", "world"]) == env.conda_package_names_set - assert set(["pip1", "pip2", "pip3"]) == env.pip_package_names_set - - requirements = project.requirements(project.default_env_spec_name) - - # find CondaEnvRequirement - conda_env_req = None - for r in requirements: - if isinstance(r, CondaEnvRequirement): - assert conda_env_req is None # only one - conda_env_req = r - assert len(conda_env_req.env_specs) == 1 - assert 'default' in conda_env_req.env_specs - assert conda_env_req.env_specs['default'] is env - - with_directory_contents_completing_project_file( - { - DEFAULT_PROJECT_FILENAME: - """ -packages: + f""" +{pkg_key}: - foo - hello >= 1.0 - world @@ -571,31 +527,31 @@ def check_get_packages(dirname): with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: ""}, check_get_packages) -def test_complain_about_packages_not_a_list(): +def test_complain_about_packages_not_a_list(pkg_key): def check_get_packages(dirname): project = project_no_dedicated_env(dirname) assert 1 == len(project.problems) "should be a list of strings not" in project.problems[0] - with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ -packages: + with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: foo: bar """}, check_get_packages) -def test_complain_about_pip_deps_not_a_list(): +def test_complain_about_pip_deps_not_a_list(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert 1 == len(project.problems) "should be a list of strings not" in project.problems[0] - with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ -packages: + with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: - pip: bar """}, check) -def test_complain_about_pip_deps_not_a_string(): +def test_complain_about_pip_deps_not_a_string(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert 1 == len(project.problems) @@ -603,13 +559,13 @@ def check(dirname): with_directory_contents_completing_project_file( {DEFAULT_PROJECT_FILENAME: """ -packages: +: - pip: - {} - """}, check) + """.replace('', pkg_key)}, check) -def test_complain_about_packages_bad_spec(): +def test_complain_about_packages_bad_spec(pkg_key): def check_get_packages(dirname): project = project_no_dedicated_env(dirname) filename = project.project_file.basename @@ -620,8 +576,8 @@ def check_get_packages(dirname): ] == project.problems with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: """ -packages: + {DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: - "=" - foo bar - pip: @@ -665,7 +621,8 @@ def check_complain_about_conda_env_var(dirname): }, check_complain_about_conda_env_var) -def test_load_environments(): +@pytest._change_default_pkg_key +def test_load_environments(pkg_key): def check_environments(dirname): project = project_no_dedicated_env(dirname) assert 0 == len(project.problems) @@ -735,7 +692,7 @@ def check_environments(dirname): { DEFAULT_PROJECT_FILENAME: """ -packages: +: - global1=1.0 - global2=1.0 channels: @@ -744,7 +701,7 @@ def check_environments(dirname): env_specs: foo: description: "THE FOO" - packages: + : - python - dog - cat @@ -752,7 +709,7 @@ def check_environments(dirname): bar: {} foo_child: inherit_from: foo - packages: + : - dog=2.0 - global1=2.0 - lion @@ -761,7 +718,7 @@ def check_environments(dirname): channels: - abc mixin: - packages: + : - bunny - walrus=1.0 - global2=2.0 @@ -771,18 +728,18 @@ def check_environments(dirname): - hbo foo_grandchild: inherit_from: [foo_child, mixin] - packages: + : - walrus=2.0 - dog=3.0 - pip: - seahorse channels: - nbc - """ + """.replace('', pkg_key) }, check_environments) -def test_load_environments_merging_in_global(): +def test_load_environments_merging_in_global(pkg_key): def check_environments(dirname): project = project_no_dedicated_env(dirname) assert 0 == len(project.problems) @@ -805,7 +762,7 @@ def check_environments(dirname): { DEFAULT_PROJECT_FILENAME: """ -packages: +: - dead-parrot - elephant @@ -814,7 +771,7 @@ def check_environments(dirname): env_specs: foo: - packages: + : - python - dog - cat @@ -823,11 +780,11 @@ def check_environments(dirname): - hbo bar: {} default: - packages: + : - lion channels: - cartoons - """ + """.replace('', pkg_key) }, check_environments) @@ -895,14 +852,14 @@ def check_environments(dirname): """}, check_environments) -def test_complain_about_packages_list_of_wrong_thing(): +def test_complain_about_packages_list_of_wrong_thing(pkg_key): def check_get_packages(dirname): project = project_no_dedicated_env(dirname) assert 1 == len(project.problems) "should be a string not '42'" in project.problems[0] - with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ -packages: + with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: - 42 """}, check_get_packages) @@ -1245,7 +1202,7 @@ def check(dirname): check) -def test_command_with_custom_description(): +def test_command_with_custom_description(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert [] == project.problems @@ -1256,7 +1213,7 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - "commands:\n default:\n bokeh_app: test.py\n description: hi\npackages:\n - bokeh\n" + f"commands:\n default:\n bokeh_app: test.py\n description: hi\n{pkg_key}:\n - bokeh\n" }, check) @@ -1635,7 +1592,7 @@ def check(dirname): {DEFAULT_PROJECT_FILENAME: "commands:\n default:\n notebook: test.ipynb\n"}, check) -def test_notebook_guess_command(): +def test_notebook_guess_command(pkg_key): def check_notebook_guess_command(dirname): project = project_no_dedicated_env(dirname) @@ -1666,7 +1623,7 @@ def check_notebook_guess_command(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - "commands:\n default:\n unix: echo 'pass'\nservices:\n REDIS_URL: redis\npackages: ['notebook']\n", + f"commands:\n default:\n unix: echo 'pass'\nservices:\n REDIS_URL: redis\n{pkg_key}: ['notebook']\n", 'test.ipynb': '{}', 'envs/should_ignore_this.ipynb': '{}', 'services/should_ignore_this.ipynb': '{}', @@ -1677,7 +1634,7 @@ def check_notebook_guess_command(dirname): # anaconda-project run data.ipynb -def test_notebook_guess_command_can_be_default(): +def test_notebook_guess_command_can_be_default(pkg_key): def check_notebook_guess_command_can_be_default(dirname): project = project_no_dedicated_env(dirname) @@ -1698,7 +1655,7 @@ def check_notebook_guess_command_can_be_default(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: "packages: ['notebook']\n", + DEFAULT_PROJECT_FILENAME: f"{pkg_key}: ['notebook']\n", # we pick the first command alphabetically in this case # so the test looks for that 'a.ipynb': '{}', @@ -1735,7 +1692,7 @@ def mock_find_executable(*args, **kwargs): {DEFAULT_PROJECT_FILENAME: "commands:\n default:\n notebook: test.ipynb\n"}, check_notebook_command) -def test_multiple_notebooks_suggestion_rejected(): +def test_multiple_notebooks_suggestion_rejected(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -1759,7 +1716,7 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - "commands:\n default:\n unix: echo 'pass'\nservices:\n REDIS_URL: redis\npackages: ['notebook']\n", + f"commands:\n default:\n unix: echo 'pass'\nservices:\n REDIS_URL: redis\n{pkg_key}: ['notebook']\n", 'test.ipynb': '{}', 'foo/test2.ipynb': '{}', 'envs/should_ignore_this.ipynb': 'pretend this is more notebook data', @@ -1769,7 +1726,7 @@ def check(dirname): }, check) -def test_skip_all_notebook_imports(): +def test_skip_all_notebook_imports(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -1785,12 +1742,12 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - "commands:\n default:\n unix: echo 'pass'\nservices:\n REDIS_URL: redis\npackages: ['notebook']\n", + f"commands:\n default:\n unix: echo 'pass'\nservices:\n REDIS_URL: redis\n{pkg_key}: ['notebook']\n", 'test.ipynb': '{}' }, check) -def test_invalid_skip_imports_notebooks(): +def test_invalid_skip_imports_notebooks(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert 1 == len(project.problems) @@ -1799,12 +1756,21 @@ def check(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: ("commands:\n default:\n unix: echo 'pass'\nservices:\n" + - " REDIS_URL: redis\npackages: ['notebook']\nskip_imports:\n notebooks: {}\n") + DEFAULT_PROJECT_FILENAME: + """ +commands: + default: + unix: echo 'pass' +services: + REDIS_URL: redis +: ['notebook'] +skip_imports: + notebooks: {} +""".replace('', pkg_key) }, check) -def test_single_notebook_suggestion_rejected(): +def test_single_notebook_suggestion_rejected(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -1826,7 +1792,7 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - "commands:\n default:\n unix: echo 'pass'\nservices:\n REDIS_URL: redis\npackages: ['notebook']\n", + f"commands:\n default:\n unix: echo 'pass'\nservices:\n REDIS_URL: redis\n{pkg_key}: ['notebook']\n", 'test.ipynb': '{}', 'envs/should_ignore_this.ipynb': 'pretend this is more notebook data', 'services/should_ignore_this.ipynb': 'pretend this is more notebook data', @@ -2361,7 +2327,8 @@ def check_run_with_stuff_missing(dirname): """}, check_run_with_stuff_missing) -def test_get_publication_info_from_empty_project(): +@pytest._change_default_pkg_key +def test_get_publication_info_from_empty_project(pkg_key): def check_publication_info_from_empty(dirname): project = project_no_dedicated_env(dirname) expected = { @@ -2373,7 +2340,7 @@ def check_publication_info_from_empty(dirname): 'env_specs': { 'default': { 'channels': [], - 'packages': [], + pkg_key: [], 'description': 'Default', 'locked': False, 'platforms': [], @@ -2414,7 +2381,7 @@ def check_publication_info_from_empty(dirname): notebook: foo.ipynb registers_fusion_function: true -packages: +: - foo channels: @@ -2423,17 +2390,17 @@ def check_publication_info_from_empty(dirname): env_specs: default: description: "Default" - packages: + : - notebook woot: - packages: + : - blah - bokeh channels: - woohoo w00t: description: "double 0" - packages: + : - something lol: {} @@ -2450,7 +2417,7 @@ def check_publication_info_from_empty(dirname): """ -def test_get_publication_info_from_complex_project(): +def test_get_publication_info_from_complex_project(pkg_key): def check_publication_info_from_complex(dirname): project = project_no_dedicated_env(dirname) @@ -2495,7 +2462,7 @@ def check_publication_info_from_complex(dirname): 'env_specs': { 'default': { 'channels': ['bar'], - 'packages': ['foo', 'notebook'], + f'{pkg_key}': ['foo', 'notebook'], 'description': 'Default', 'locked': False, 'platforms': ['linux-64', 'osx-64', 'win-64'], @@ -2531,7 +2498,7 @@ def check_publication_info_from_complex(dirname): }, 'lol': { 'channels': ['bar'], - 'packages': ['foo'], + pkg_key: ['foo'], 'description': 'lol', 'locked': False, 'platforms': ['linux-64', 'osx-64', 'win-64'], @@ -2567,7 +2534,7 @@ def check_publication_info_from_complex(dirname): }, 'w00t': { 'channels': ['bar'], - 'packages': ['foo', 'something'], + pkg_key: ['foo', 'something'], 'description': 'double 0', 'locked': False, 'platforms': ['linux-64', 'osx-64', 'win-64'], @@ -2603,7 +2570,7 @@ def check_publication_info_from_complex(dirname): }, 'woot': { 'channels': ['bar', 'woohoo'], - 'packages': ['foo', 'blah', 'bokeh'], + pkg_key: ['foo', 'blah', 'bokeh'], 'description': 'woot', 'locked': False, 'platforms': ['linux-64', 'osx-64', 'win-64'], @@ -2645,13 +2612,13 @@ def check_publication_info_from_complex(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: _complicated_project_contents, + DEFAULT_PROJECT_FILENAME: _complicated_project_contents.replace('', pkg_key), "main.py": "", "foo.ipynb": "" }, check_publication_info_from_complex) -def test_find_requirements(): +def test_find_requirements(pkg_key): def check_find_requirements(dirname): project = project_no_dedicated_env(dirname) assert [] == project.problems @@ -2680,13 +2647,13 @@ def check_find_requirements(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: _complicated_project_contents, + DEFAULT_PROJECT_FILENAME: _complicated_project_contents.replace('', pkg_key), "main.py": "", "foo.ipynb": "" }, check_find_requirements) -def test_requirements_subsets(): +def test_requirements_subsets(pkg_key): def check_requirements_subsets(dirname): project = project_no_dedicated_env(dirname) assert [] == project.problems @@ -2712,13 +2679,13 @@ def check_requirements_subsets(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: _complicated_project_contents, + DEFAULT_PROJECT_FILENAME: _complicated_project_contents.replace('', pkg_key), "main.py": "", "foo.ipynb": "" }, check_requirements_subsets) -def test_env_var_name_list_properties(): +def test_env_var_name_list_properties(pkg_key): def check_env_var_name_list_properties(dirname): project = project_no_dedicated_env(dirname) assert [] == project.problems @@ -2738,7 +2705,7 @@ def check_env_var_name_list_properties(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: _complicated_project_contents, + DEFAULT_PROJECT_FILENAME: _complicated_project_contents.replace('', pkg_key), "main.py": "", "foo.ipynb": "" }, check_env_var_name_list_properties) @@ -2847,7 +2814,7 @@ def check(dirname): }, check) -def test_auto_fix_env_spec_out_of_sync(): +def test_auto_fix_env_spec_out_of_sync(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert len(project.problems) == 1 @@ -2857,7 +2824,6 @@ def check(dirname): assert ("Environment spec 'stuff' from environment.yml is out of sync with anaconda-project.yml. Diff:\n" + " channels:\n + bar\n+ a\n+ b\n pip:\n + foo") == problem.text assert problem.can_fix - problem.fix(project) project.project_file.save() @@ -2871,8 +2837,13 @@ def check(dirname): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - "name: foo\nenv_specs: { 'stuff': { 'packages':[] } }\nplatforms: [linux-32,linux-64,osx-64,win-32,win-64]\n", - "environment.yml": """ + """ +name: foo +env_specs: { 'stuff': { '':[] } } +platforms: [linux-32,linux-64,osx-64,win-32,win-64] +""".lstrip().replace('', pkg_key), + "environment.yml": + """ name: stuff dependencies: - a @@ -2885,7 +2856,7 @@ def check(dirname): }, check) -def test_auto_fix_env_spec_import_saying_no(): +def test_auto_fix_env_spec_import_saying_no(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert len(project.problems) == 1 @@ -2908,8 +2879,13 @@ def check(dirname): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - "name: foo\nenv_specs: {'default':{'packages':[]}}\nplatforms: [linux-32,linux-64,osx-64,win-32,win-64]\n", - "environment.yml": """ + """ +name: foo +env_specs: {'default':{'':[]}} +platforms: [linux-32,linux-64,osx-64,win-32,win-64] +""".lstrip().replace('', pkg_key), + "environment.yml": + """ name: stuff dependencies: - a @@ -2922,7 +2898,8 @@ def check(dirname): }, check) -def test_no_auto_fix_env_spec_with_notebook_bokeh_injection(): +@pytest._change_default_pkg_key +def test_no_auto_fix_env_spec_with_notebook_bokeh_injection(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert len(project.problems) == 1 @@ -2944,9 +2921,9 @@ def check(dirname): assert spec.channels == ('bar', ) # add bokeh and notebook, which we should ignore - packages = project.project_file.get_value(['env_specs', 'stuff', 'packages']) - packages.extend(['bokeh', 'notebook']) - project.project_file.set_value(['env_specs', 'stuff', 'packages'], packages) + the_packages = project.project_file.get_value(['env_specs', 'stuff', pkg_key]) + the_packages.extend(['bokeh', 'notebook']) + project.project_file.set_value(['env_specs', 'stuff', pkg_key], the_packages) project.project_file.save() # no problems despite the diff @@ -2955,17 +2932,17 @@ def check(dirname): assert spec.conda_packages == ('a', 'b', 'bokeh', 'notebook') # add some other package, should NOT ignore - packages = project.project_file.get_value(['env_specs', 'stuff', 'packages']) - packages.extend(['someother']) - project.project_file.set_value(['env_specs', 'stuff', 'packages'], packages) + the_packages = project.project_file.get_value(['env_specs', 'stuff', pkg_key]) + the_packages.extend(['someother']) + project.project_file.set_value(['env_specs', 'stuff', pkg_key], the_packages) project.project_file.save() assert len(project.problems) == 1 # remove that again - packages = project.project_file.get_value(['env_specs', 'stuff', 'packages']) - packages.remove('someother') - project.project_file.set_value(['env_specs', 'stuff', 'packages'], packages) + the_packages = project.project_file.get_value(['env_specs', 'stuff', pkg_key]) + the_packages.remove('someother') + project.project_file.set_value(['env_specs', 'stuff', pkg_key], the_packages) project.project_file.save() assert len(project.problems) == 0 @@ -2989,9 +2966,9 @@ def check(dirname): assert len(project.problems) == 0 # add a pip package, should NOT ignore - packages = project.project_file.get_value(['env_specs', 'stuff', 'packages']) + the_packages = project.project_file.get_value(['env_specs', 'stuff', pkg_key]) pip_list = None - for p in packages: + for p in the_packages: if isinstance(p, dict): pip_list = p['pip'] pip_list.append('someother') @@ -3005,8 +2982,13 @@ def check(dirname): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - "name: foo\nenv_specs: { 'stuff': { 'packages':[] } }\nplatforms: [linux-32,linux-64,osx-64,win-32,win-64]\n", - "environment.yml": """ + """ +name: foo +env_specs: { 'stuff': { '':[] } } +platforms: [linux-32,linux-64,osx-64,win-32,win-64] +""".lstrip().replace('', pkg_key), + "environment.yml": + """ name: stuff dependencies: - a @@ -3051,7 +3033,7 @@ def check(dirname): }, check) -def test_no_auto_fix_notebook_dep_if_we_have_anaconda(): +def test_no_auto_fix_notebook_dep_if_we_have_anaconda(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -3061,7 +3043,7 @@ def check(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: ('packages:\n' + DEFAULT_PROJECT_FILENAME: (f'{pkg_key}:\n' ' - anaconda\n' 'commands:\n' ' foo.ipynb:\n' @@ -3071,7 +3053,7 @@ def check(dirname): }, check) -def test_no_auto_fix_notebook_dep_if_we_have_notebook(): +def test_no_auto_fix_notebook_dep_if_we_have_notebook(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -3080,7 +3062,7 @@ def check(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: ('packages:\n' + DEFAULT_PROJECT_FILENAME: (f'{pkg_key}:\n' ' - notebook\n' 'commands:\n' ' foo.ipynb:\n' @@ -3142,7 +3124,7 @@ def check(dirname): {DEFAULT_PROJECT_FILENAME: "commands:\n foo:\n unix: something\n somejunk: True\n"}, check) -def test_unknown_field_in_env_spec(): +def test_unknown_field_in_env_spec(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert [] == project.problems @@ -3150,10 +3132,10 @@ def check(dirname): assert [expected_suggestion] == project.suggestions with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: "env_specs:\n foo:\n packages: [something]\n somejunk: True\n"}, check) + {DEFAULT_PROJECT_FILENAME: f"env_specs:\n foo:\n {pkg_key}: [something]\n somejunk: True\n"}, check) -def test_unknown_field_in_env_spec_allowed(): +def test_unknown_field_in_env_spec_allowed(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert [] == project.problems @@ -3162,7 +3144,7 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - "env_specs:\n foo:\n packages: [something]\n somejunk: True\n user_fields: [somejunk]\n" + f"env_specs:\n foo:\n {pkg_key}: [something]\n somejunk: True\n user_fields: [somejunk]\n" }, check) @@ -3204,7 +3186,7 @@ def check(dirname): with_directory_contents({DEFAULT_PROJECT_FILENAME: ""}, check) -def test_with_one_locked_env_spec_has_problems(): +def test_with_one_locked_env_spec_has_problems(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert [ @@ -3214,10 +3196,10 @@ def check(dirname): with_directory_contents( { - DEFAULT_PROJECT_FILENAME: """ + DEFAULT_PROJECT_FILENAME: f""" env_specs: foo: - packages: [] + {pkg_key}: [] """, DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n" }, check) @@ -3237,19 +3219,19 @@ def check(dirname): }, check) -def test_with_locking_disabled_no_platforms_required(): +def test_with_locking_disabled_no_platforms_required(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert [] == project.problems with_directory_contents( { - DEFAULT_PROJECT_FILENAME: "name: foo\nenv_specs:\n default:\n packages: []\n", + DEFAULT_PROJECT_FILENAME: f"name: foo\nenv_specs:\n default:\n {pkg_key}: []\n", DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: false\n" }, check) -def test_load_weird_platform(): +def test_load_weird_platform(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -3268,17 +3250,17 @@ def check(dirname): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - """ + f""" name: foo platforms: [linux-64, weird-valid, weirdinvalid] env_specs: default: - packages: [foo] + {pkg_key}: [foo] """ }, check) -def test_only_some_env_specs_have_platforms_locking_disabled(): +def test_only_some_env_specs_have_platforms_locking_disabled(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert [] == project.problems @@ -3286,20 +3268,20 @@ def check(dirname): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - """ + f""" name: foo env_specs: default: platforms: [linux-64] - packages: [foo] + {pkg_key}: [foo] no_platforms: - packages: [bar] + {pkg_key}: [bar] """ }, check) -def test_only_some_env_specs_have_platforms_locking_enabled(): +def test_only_some_env_specs_have_platforms_locking_enabled(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert [("anaconda-project.yml: Env spec no_platforms does not have anything in its " + "'platforms:' field.") @@ -3307,15 +3289,15 @@ def check(dirname): with_directory_contents( { - DEFAULT_PROJECT_FILENAME: """ + DEFAULT_PROJECT_FILENAME: f""" name: foo env_specs: default: platforms: [linux-64] - packages: [foo] + {pkg_key}: [foo] no_platforms: - packages: [bar] + {pkg_key}: [bar] """, DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n" }, check) @@ -3401,22 +3383,24 @@ def check(dirname): """}, check) -def test_lock_file_non_dict_lock_set_packages(): +@pytest._change_default_pkg_key +def test_lock_file_non_dict_lock_set_packages(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) - expected_error = ("%s: 'packages:' section in env spec 'default' in lock file should be a dictionary, " + + expected_error = (f"%s: '{pkg_key}:' section in env spec 'default' in lock file should be a dictionary, " + "found %r") % (project.lock_file.basename, 42) assert [expected_error] == project.problems with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_LOCK_FILENAME: """ + {DEFAULT_PROJECT_LOCK_FILENAME: f""" env_specs: default: - packages: 42 + {pkg_key}: 42 """}, check) -def test_lock_file_has_pip_packages(): +@pytest._change_default_pkg_key +def test_lock_file_has_pip_packages(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert [] == project.problems @@ -3428,11 +3412,11 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_LOCK_FILENAME: - """ + f""" env_specs: default: platforms: [linux-64,osx-64,win-64] - packages: + {pkg_key}: all: - pip: - foobar @@ -3440,7 +3424,8 @@ def check(dirname): }, check) -def test_lock_file_has_invalid_packages(): +@pytest._change_default_pkg_key +def test_lock_file_has_invalid_packages(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -3454,10 +3439,10 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_LOCK_FILENAME: - """ + f""" env_specs: default: - packages: + {pkg_key}: all: - "=" - foo bar @@ -3467,7 +3452,7 @@ def check(dirname): }, check) -def test_lock_file_has_wrong_platforms(): +def test_lock_file_has_wrong_platforms(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -3481,17 +3466,17 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_LOCK_FILENAME: - """ + f""" env_specs: default: platforms: ["win-64"] - packages: + {pkg_key}: all: [foo] """ }, check) -def test_lock_file_has_zero_platforms(): +def test_lock_file_has_zero_platforms(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -3502,16 +3487,18 @@ def check(dirname): ] == project.suggestions with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_LOCK_FILENAME: """ + { + DEFAULT_PROJECT_LOCK_FILENAME: f""" env_specs: default: platforms: [] - packages: + {pkg_key}: all: [foo] -"""}, check) +""" + }, check) -def test_lock_file_has_wrong_hash(): +def test_lock_file_has_wrong_hash(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -3525,29 +3512,30 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_LOCK_FILENAME: - """ + f""" env_specs: default: env_spec_hash: wrong platforms: [linux-64,osx-64,win-64] - packages: + {pkg_key}: all: [foo] """ }, check) -def test_lock_file_has_empty_and_wrong_package_lists(): +@pytest._change_default_pkg_key +def test_lock_file_has_empty_and_wrong_package_lists(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) assert [] == project.problems # yapf: disable assert [ - 'anaconda-project-lock.yml: Lock file lists no packages for env spec ' + f'anaconda-project-lock.yml: Lock file lists no {pkg_key} for env spec ' "'default' on platform linux-64", - 'anaconda-project-lock.yml: Lock file is missing 1 packages for env spec ' + f'anaconda-project-lock.yml: Lock file is missing 1 {pkg_key} for env spec ' 'default on osx-64 (hello)', - 'anaconda-project-lock.yml: Lock file lists no packages for env spec ' + f'anaconda-project-lock.yml: Lock file lists no {pkg_key} for env spec ' "'default' on platform win-64" ] == project.suggestions # yapf: enable @@ -3555,18 +3543,18 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ + f""" env_specs: default: - packages: + {pkg_key}: - hello """, DEFAULT_PROJECT_LOCK_FILENAME: - """ + f""" env_specs: default: platforms: [linux-64,osx-64,win-64] - packages: + {pkg_key}: linux-64: [] win-64: [] win-32: [foo] @@ -3576,7 +3564,7 @@ def check(dirname): }, check) -def test_lock_file_has_orphan_env_spec(): +def test_lock_file_has_orphan_env_spec(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -3587,10 +3575,10 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ + f""" env_specs: default: - packages: + {pkg_key}: - hello """, DEFAULT_PROJECT_LOCK_FILENAME: @@ -3598,16 +3586,16 @@ def check(dirname): env_specs: default: platforms: [linux-64,osx-64,win-64] - packages: + : all: [hello] orphan: platforms: [linux-64,osx-64,win-64] - packages: {} -""" + : {} +""".replace('', pkg_key) }, check) -def test_fix_project_file_with_no_platforms(): +def test_fix_project_file_with_no_platforms(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -3624,11 +3612,11 @@ def check(dirname): with_directory_contents( { - DEFAULT_PROJECT_FILENAME: """ + DEFAULT_PROJECT_FILENAME: f""" name: foo env_specs: default: - packages: + {pkg_key}: - hello """, DEFAULT_PROJECT_LOCK_FILENAME: """ @@ -3637,7 +3625,7 @@ def check(dirname): }, check) -def test_fix_env_spec_with_no_platforms(): +def test_fix_env_spec_with_no_platforms(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) @@ -3660,20 +3648,20 @@ def check(dirname): with_directory_contents( { - DEFAULT_PROJECT_FILENAME: """ + DEFAULT_PROJECT_FILENAME: f""" name: foo env_specs: default: platforms: ['linux-64'] - packages: + {pkg_key}: - hello foo: platforms: [] - packages: + {pkg_key}: - apackage bar: platforms: [] - packages: + {pkg_key}: - package2 """, DEFAULT_PROJECT_LOCK_FILENAME: """ diff --git a/anaconda_project/test/test_project_ops.py b/anaconda_project/test/test_project_ops.py index d13e7105..3597a304 100644 --- a/anaconda_project/test/test_project_ops.py +++ b/anaconda_project/test/test_project_ops.py @@ -349,7 +349,7 @@ def check_add_var(dirname): with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: ""}, check_add_var) -def test_add_variables_to_env_spec(): +def test_add_variables_to_env_spec(pkg_key): def check_add_var(dirname): project = project_no_dedicated_env(dirname) status = project_ops.add_variables(project, 'myspec', ['foo', 'baz'], dict(foo='bar')) @@ -363,13 +363,13 @@ def check_add_var(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ + f""" env_specs: default: - packages: [python] + {pkg_key}: [python] channels: [] myspec: - packages: [python] + {pkg_key}: [python] channels: [] """ }, check_add_var) @@ -467,7 +467,7 @@ def check_remove_var(dirname): ' foo: baz\n bar: qux')}, check_remove_var) -def test_remove_variables_with_env_spec(): +def test_remove_variables_with_env_spec(pkg_key): def check_remove_var(dirname): project = project_no_dedicated_env(dirname) @@ -485,13 +485,13 @@ def check_remove_var(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ + f""" env_specs: default: - packages: [python] + {pkg_key}: [python] channels: [] myspec: - packages: [python] + {pkg_key}: [python] channels: [] variables: foo: baz @@ -716,7 +716,7 @@ def check_add_command(dirname): with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: ""}, check_add_command) -def test_add_command_bokeh_overwrites(): +def test_add_command_bokeh_overwrites(pkg_key): def check_add_command(dirname): project = project_no_dedicated_env(dirname) result = project_ops.add_command(project, 'bokeh_test', 'bokeh_app', 'file.py') @@ -733,12 +733,12 @@ def check_add_command(dirname): DEFAULT_PROJECT_FILENAME: ('commands:\n' ' bokeh_test:\n' ' bokeh_app: replaced.py\n' - 'packages:\n' + f'{pkg_key}:\n' ' - bokeh\n') }, check_add_command) -def test_add_command_sets_env_spec(): +def test_add_command_sets_env_spec(pkg_key): def check_add_command(dirname): project = project_no_dedicated_env(dirname) result = project_ops.add_command(project, 'bokeh_test', 'bokeh_app', 'file.py', env_spec_name='foo') @@ -752,14 +752,14 @@ def check_add_command(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: ('env_specs:\n' - ' foo: { "packages" : ["bokeh"] }\n' + f' foo: {{ "{pkg_key}" : ["bokeh"] }}\n' 'commands:\n' ' bokeh_test:\n' ' bokeh_app: replaced.py\n') }, check_add_command) -def test_add_command_leaves_env_spec(): +def test_add_command_leaves_env_spec(pkg_key): def check_add_command(dirname): project = project_no_dedicated_env(dirname) result = project_ops.add_command(project, 'bokeh_test', 'bokeh_app', 'file.py', env_spec_name=None) @@ -773,7 +773,7 @@ def check_add_command(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: ('env_specs:\n' - ' foo: { "packages" : ["bokeh"] }\n' + f' foo: {{ "{pkg_key}" : ["bokeh"] }}\n' 'commands:\n' ' bokeh_test:\n' ' env_spec: "foo"\n' @@ -781,7 +781,8 @@ def check_add_command(dirname): }, check_add_command) -def test_add_command_generates_env_spec_suggestion(): +@pytest._change_default_pkg_key +def test_add_command_generates_env_spec_suggestion(pkg_key): def check_add_command(dirname): project = project_no_dedicated_env(dirname) assert project.problems == [] @@ -798,22 +799,22 @@ def check_add_command(dirname): command = re_loaded.get_value(['commands', 'bokeh_test']) assert command['bokeh_app'] == 'file.py' assert command['env_spec'] == 'bar' - assert re_loaded.get_value(['env_specs', 'bar', 'packages']) is None + assert re_loaded.get_value(['env_specs', 'bar', pkg_key]) is None assert project.problems == [] assert project.suggestions == [('%s: Command ' % project.project_file.basename) + - 'bokeh_test uses env spec bar which does not have the packages: bokeh'] + f'bokeh_test uses env spec bar which does not have the {pkg_key}: bokeh'] project.fix_problems_and_suggestions() project.project_file.save() re_loaded = ProjectFile.load_for_directory(project.directory_path) - assert re_loaded.get_value(['env_specs', 'bar', 'packages']) == ['bokeh'] + assert re_loaded.get_value(['env_specs', 'bar', pkg_key]) == ['bokeh'] with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: ('env_specs:\n' - ' foo: { "packages" : ["bokeh"] }\n' + f' foo: {{ "{pkg_key}" : ["bokeh"] }}\n' ' bar: {}\n' 'commands:\n' ' bokeh_test:\n' @@ -822,7 +823,7 @@ def check_add_command(dirname): }, check_add_command) -def test_add_command_leaves_supports_http_options(): +def test_add_command_leaves_supports_http_options(pkg_key): def check_add_command(dirname): project = project_no_dedicated_env(dirname) result = project_ops.add_command(project, @@ -842,7 +843,7 @@ def check_add_command(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: ('env_specs:\n' - ' foo: { "packages" : ["bokeh"] }\n' + f' foo: {{ "{pkg_key}" : ["bokeh"] }}\n' 'commands:\n' ' bokeh_test:\n' ' supports_http_options: false\n' @@ -850,7 +851,7 @@ def check_add_command(dirname): }, check_add_command) -def test_add_command_leaves_supports_http_options_unset(): +def test_add_command_leaves_supports_http_options_unset(pkg_key): def check_add_command(dirname): project = project_no_dedicated_env(dirname) result = project_ops.add_command(project, @@ -870,14 +871,14 @@ def check_add_command(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: ('env_specs:\n' - ' foo: { "packages" : ["bokeh"] }\n' + f' foo: {{ "{pkg_key}" : ["bokeh"] }}\n' 'commands:\n' ' bokeh_test:\n' ' bokeh_app: replaced.py\n') }, check_add_command) -def test_add_command_modifies_supports_http_options(): +def test_add_command_modifies_supports_http_options(pkg_key): def check_add_command(dirname): project = project_no_dedicated_env(dirname) result = project_ops.add_command(project, @@ -897,7 +898,7 @@ def check_add_command(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: ('env_specs:\n' - ' foo: { "packages" : ["bokeh"] }\n' + f' foo: {{ "{pkg_key}" : ["bokeh"] }}\n' 'commands:\n' ' bokeh_test:\n' ' supports_http_options: false\n' @@ -1252,7 +1253,7 @@ def check(dirname): with_directory_contents_completing_project_file(dict(), check) -def test_add_download_to_env_spec(monkeypatch): +def test_add_download_to_env_spec(monkeypatch, pkg_key): def check(dirname): _monkeypatch_download_file(monkeypatch, dirname) @@ -1272,13 +1273,13 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ + r""" env_specs: default: - packages: [python] + {pkg_key}: [python] channels: [] myspec: - packages: [python] + {pkg_key}: [python] channels: [] """ }, check) @@ -1489,7 +1490,7 @@ def check(dirname): """}, check) -def test_remove_download_with_env_spec(monkeypatch): +def test_remove_download_with_env_spec(monkeypatch, pkg_key): def check(dirname): config_path = ['env_specs', 'myspec', 'downloads', 'MYDATA'] project = project_no_dedicated_env(dirname) @@ -1506,13 +1507,13 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ + f""" env_specs: default: - packages: [python] + {pkg_key}: [python] channels: [] myspec: - packages: [python] + {pkg_key}: [python] channels: [] downloads: MYDATA: "http://localhost:123456" @@ -1528,7 +1529,7 @@ def check(dirname): # then we add a latest version of pandas. If anaconda-project does the right thing, # conda will install an earlier version of pandas to respect the numpy version pin. @pytest.mark.slow -def test_add_env_spec_with_real_conda_manager(monkeypatch): +def test_add_env_spec_with_real_conda_manager(monkeypatch, pkg_key): monkeypatch_conda_not_to_use_links(monkeypatch) _monkeypatch_reduced_environment(monkeypatch) @@ -1538,9 +1539,9 @@ def check(dirname): pip_spec = ['chardet'] for spec in specs: if spec == specs[0]: - status = project_ops.add_env_spec(project, name='foo', packages=[spec], channels=[]) + status = project_ops.add_env_spec(project, name='foo', pkg_key=[spec], channels=[]) else: - status = project_ops.add_packages(project, 'foo', packages=[spec], channels=[]) + status = project_ops.add_packages(project, 'foo', pkg_key=[spec], channels=[]) if not status: print(status.status_description) print(repr(status.errors)) @@ -1554,7 +1555,7 @@ def check(dirname): # be sure it was really done project2 = Project(dirname) env_commented_map = project2.project_file.get_value(['env_specs', 'foo']) - assert spec in env_commented_map['packages'], env_commented_map['packages'] + assert spec in env_commented_map[pkg_key], env_commented_map[pkg_key] # ensure numpy <1.11.3 is present in both passes meta_path = os.path.join(dirname, 'envs', 'foo', 'conda-meta') @@ -1634,7 +1635,8 @@ def _with_conda_test(f, _pop_conda_test() -def test_add_env_spec(): +@pytest._change_default_pkg_key +def test_add_env_spec(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -1648,23 +1650,24 @@ def attempt(): # be sure we really made the config changes project2 = Project(dirname) - assert dict(packages=[], channels=[]) == dict(project2.project_file.get_value(['env_specs', 'foo'])) - assert dict(packages=[], channels=[]) == dict(project2.project_file.get_value(['env_specs', 'bar'])) - assert dict(locked=True, + assert dict({pkg_key: []}, channels=[]) == dict(project2.project_file.get_value(['env_specs', 'foo'])) + assert dict({pkg_key: []}, channels=[]) == dict(project2.project_file.get_value(['env_specs', 'bar'])) + assert dict({pkg_key: dict(all=[])}, + locked=True, env_spec_hash='a30f02c961ef4f3fe07ceb09e0906394c3885a79', - packages=dict(all=[]), platforms=['linux-64', 'osx-64', 'win-64']) == dict(project2.lock_file.get_value(['env_specs', 'foo'])) - assert dict(locked=True, + assert dict({pkg_key: dict(all=[])}, + locked=True, env_spec_hash='a30f02c961ef4f3fe07ceb09e0906394c3885a79', - packages=dict(all=[]), platforms=['linux-64', 'osx-64', 'win-64']) == dict(project2.lock_file.get_value(['env_specs', 'bar'])) with_directory_contents_completing_project_file({DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n"}, check) -def test_add_env_spec_no_global_platforms(): +@pytest._change_default_pkg_key +def test_add_env_spec_no_global_platforms(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -1685,20 +1688,21 @@ def attempt(): # be sure we really made the config changes project2 = Project(dirname) - assert dict(packages=[], channels=[], + assert dict({pkg_key: []}, channels=[], platforms=['linux-64', 'osx-64', 'win-64']) == dict(project2.project_file.get_value(['env_specs', 'foo'])) - assert dict(locked=True, + assert dict({pkg_key: dict(all=[])}, + locked=True, env_spec_hash='a30f02c961ef4f3fe07ceb09e0906394c3885a79', - packages=dict(all=[]), platforms=['linux-64', 'osx-64', 'win-64']) == dict(project2.lock_file.get_value(['env_specs', 'foo'])) with_directory_contents_completing_project_file({DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n"}, check) -def test_add_env_spec_with_packages_and_channels(): +@pytest._change_default_pkg_key +def test_add_env_spec_with_packages_and_channels(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -1712,7 +1716,7 @@ def attempt(): # be sure download was added to the file and saved project2 = Project(dirname) - assert dict(packages=['a', 'b', 'c'], + assert dict({pkg_key: ['a', 'b', 'c']}, channels=['c1', 'c2', 'c3']) == dict(project2.project_file.get_value(['env_specs', 'foo'])) env_spec = project2.env_specs['foo'] @@ -1723,7 +1727,8 @@ def attempt(): with_directory_contents_completing_project_file({DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n"}, check) -def test_add_env_spec_extending_existing_lists(): +@pytest._change_default_pkg_key +def test_add_env_spec_extending_existing_lists(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -1737,19 +1742,20 @@ def attempt(): # be sure download was added to the file and saved project2 = Project(dirname) - assert dict(packages=['b', 'a', 'c'], + assert dict({pkg_key: ['b', 'a', 'c']}, channels=['c3', 'c1', 'c2']) == dict(project2.project_file.get_value(['env_specs', 'foo'])) with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: """ + {DEFAULT_PROJECT_FILENAME: f""" env_specs: foo: - packages: [ 'b' ] + {pkg_key}: [ 'b' ] channels: [ 'c3'] """}, check) -def test_add_env_spec_extending_existing_lists_with_versions(): +@pytest._change_default_pkg_key +def test_add_env_spec_extending_existing_lists_with_versions(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -1763,14 +1769,14 @@ def attempt(): # be sure download was added to the file and saved project2 = Project(dirname) - assert dict(packages=['b=2.0', 'a', 'c'], + assert dict({pkg_key: ['b=2.0', 'a', 'c']}, channels=['c3', 'c1', 'c2']) == dict(project2.project_file.get_value(['env_specs', 'foo'])) with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: """ + {DEFAULT_PROJECT_FILENAME: f""" env_specs: foo: - packages: [ 'b=1.0' ] + {pkg_key}: [ 'b=1.0' ] channels: [ 'c3'] """}, check) @@ -1795,7 +1801,8 @@ def attempt(): with_directory_contents_completing_project_file({DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n"}, check) -def test_remove_env_spec(): +@pytest._change_default_pkg_key +def test_remove_env_spec(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -1821,30 +1828,30 @@ def attempt(): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ + f""" name: foo env_specs: hello: - packages: + {pkg_key}: - a another: - packages: + {pkg_key}: - b """, DEFAULT_PROJECT_LOCK_FILENAME: - """ + f""" locking_enabled: true env_specs: hello: platforms: [linux-32,linux-64,osx-64,win-32,win-64] - packages: + {pkg_key}: all: - a=1.0=1 """ }, check) -def test_remove_only_env_spec(): +def test_remove_only_env_spec(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -1865,16 +1872,17 @@ def attempt(): assert project2.lock_file.get_value(['env_specs', 'hello'], None) is None with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: """ + {DEFAULT_PROJECT_FILENAME: f""" name: foo env_specs: hello: - packages: + {pkg_key}: - a """}, check) -def test_remove_env_spec_causes_problem(): +@pytest._change_default_pkg_key +def test_remove_env_spec_causes_problem(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -1902,7 +1910,7 @@ def attempt(): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ + f""" name: foo commands: default: @@ -1910,26 +1918,26 @@ def attempt(): env_spec: hello env_specs: hello: - packages: + {pkg_key}: - a another: - packages: + {pkg_key}: - b """, DEFAULT_PROJECT_LOCK_FILENAME: - """ + f""" locking_enabled: true env_specs: hello: platforms: [linux-32,linux-64,osx-64,win-32,win-64] - packages: + {pkg_key}: all: - a=1.0=1 """ }, check) -def test_add_packages_to_all_environments(): +def test_add_packages_to_all_environments(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -1944,7 +1952,7 @@ def attempt(): # be sure we really made the config changes project2 = Project(dirname) - assert [dict(pip=[]), 'foo', 'bar'] == list(project2.project_file.get_value('packages')) + assert [dict(pip=[]), 'foo', 'bar'] == list(project2.project_file.get_value(pkg_key)) assert ['hello', 'world'] == list(project2.project_file.get_value('channels')) for env_spec in project2.env_specs.values(): @@ -1954,15 +1962,15 @@ def attempt(): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ -packages: + DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: - pip: [] # be sure we don't break with this in the list """, DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n" }, check) -def test_add_pip_packages_to_all_environments(): +def test_add_pip_packages_to_all_environments(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -1980,7 +1988,7 @@ def attempt(): project2 = Project(dirname) assert [ dict(pip=['foo', 'bar']), - ] == list(project2.project_file.get_value('packages')) + ] == list(project2.project_file.get_value(pkg_key)) # assert ['hello', 'world'] == list(project2.project_file.get_value('channels')) for env_spec in project2.env_specs.values(): @@ -1990,15 +1998,15 @@ def attempt(): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ -packages: + DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: - pip: [] # be sure we don't break with this in the list """, DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n" }, check) -def test_add_packages_cannot_resolve_deps(): +def test_add_packages_cannot_resolve_deps(pkg_key): def check(dirname): def attempt(): project = Project(dirname, frontend=FakeFrontend()) @@ -2015,7 +2023,7 @@ def attempt(): # be sure we didn't make the config changes project2 = Project(dirname) - assert project2.project_file.get_value('packages', None) is None + assert project2.project_file.get_value(pkg_key, None) is None assert project2.project_file.get_value('channels', None) is None for env_spec in project2.env_specs.values(): @@ -2055,7 +2063,7 @@ def attempt(): with_directory_contents_completing_project_file(dict(), check) -def test_remove_packages_from_all_environments(): +def test_remove_packages_from_all_environments(pkg_key): def check(dirname): def attempt(): os.makedirs(os.path.join(dirname, 'envs', 'hello')) # forces us to really run remove_packages @@ -2064,8 +2072,8 @@ def attempt(): assert env_spec.lock_set.enabled assert env_spec.lock_set.platforms == () - assert ['foo', 'bar', 'baz'] == list(project.project_file.get_value('packages')) - assert ['foo', 'woot'] == list(project.project_file.get_value(['env_specs', 'hello', 'packages'], [])) + assert ['foo', 'bar', 'baz'] == list(project.project_file.get_value(pkg_key)) + assert ['foo', 'woot'] == list(project.project_file.get_value(['env_specs', 'hello', pkg_key], [])) status = project_ops.remove_packages(project, env_spec_name=None, packages=['foo', 'bar']) assert [] == status.errors assert status @@ -2074,8 +2082,8 @@ def attempt(): # be sure we really made the config changes project2 = Project(dirname) - assert ['baz'] == list(project2.project_file.get_value('packages')) - assert ['woot'] == list(project2.project_file.get_value(['env_specs', 'hello', 'packages'])) + assert ['baz'] == list(project2.project_file.get_value(pkg_key)) + assert ['woot'] == list(project2.project_file.get_value(['env_specs', 'hello', pkg_key])) for env_spec in project2.env_specs.values(): assert env_spec.lock_set.enabled @@ -2084,18 +2092,18 @@ def attempt(): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ -packages: + DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: - foo - bar - baz env_specs: hello: - packages: + {pkg_key}: - foo - woot hello2: - packages: + {pkg_key}: - foo - bar - pip: [] # make sure we don't choke on non-string items in list @@ -2104,7 +2112,7 @@ def attempt(): }, check) -def test_remove_conda_packages_from_global_with_pip_packages(): +def test_remove_conda_packages_from_global_with_pip_packages(pkg_key): def check(dirname): def attempt(): os.makedirs(os.path.join(dirname, 'envs', 'hello')) # forces us to really run remove_packages @@ -2113,8 +2121,8 @@ def attempt(): assert env_spec.lock_set.enabled assert env_spec.lock_set.platforms == () - assert ['foo', 'bar', 'baz', OrderedDict([('pip', [])])] == list(project.project_file.get_value('packages')) - assert ['foo', 'woot'] == list(project.project_file.get_value(['env_specs', 'hello', 'packages'], [])) + assert ['foo', 'bar', 'baz', OrderedDict([('pip', [])])] == list(project.project_file.get_value(pkg_key)) + assert ['foo', 'woot'] == list(project.project_file.get_value(['env_specs', 'hello', pkg_key], [])) status = project_ops.remove_packages(project, env_spec_name=None, packages=['foo', 'bar']) assert [] == status.errors assert status @@ -2123,8 +2131,8 @@ def attempt(): # be sure we really made the config changes project2 = Project(dirname) - assert ['baz', OrderedDict([('pip', [])])] == list(project2.project_file.get_value('packages')) - assert ['woot'] == list(project2.project_file.get_value(['env_specs', 'hello', 'packages'])) + assert ['baz', OrderedDict([('pip', [])])] == list(project2.project_file.get_value(pkg_key)) + assert ['woot'] == list(project2.project_file.get_value(['env_specs', 'hello', pkg_key])) for env_spec in project2.env_specs.values(): assert env_spec.lock_set.enabled @@ -2133,19 +2141,19 @@ def attempt(): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ -packages: + DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: - foo - bar - baz - pip: [] env_specs: hello: - packages: + {pkg_key}: - foo - woot hello2: - packages: + {pkg_key}: - foo - bar - pip: [] # make sure we don't choke on non-string items in list @@ -2154,7 +2162,7 @@ def attempt(): }, check) -def test_remove_pip_packages_from_global(): +def test_remove_pip_packages_from_global(pkg_key): def check(dirname): def attempt(): os.makedirs(os.path.join(dirname, 'envs', 'hello')) # forces us to really run remove_packages @@ -2163,9 +2171,9 @@ def attempt(): assert env_spec.lock_set.enabled assert env_spec.lock_set.platforms == () - assert ['foo', OrderedDict([('pip', ['bar', 'baz'])])] == list(project.project_file.get_value('packages')) + assert ['foo', OrderedDict([('pip', ['bar', 'baz'])])] == list(project.project_file.get_value(pkg_key)) assert ['foo', OrderedDict([('pip', ['bar', 'woot'])]) - ] == list(project.project_file.get_value(['env_specs', 'hello', 'packages'], [])) + ] == list(project.project_file.get_value(['env_specs', 'hello', pkg_key], [])) status = project_ops.remove_packages(project, env_spec_name=None, packages=['bar'], pip=True) assert [] == status.errors assert status @@ -2174,9 +2182,9 @@ def attempt(): # be sure we really made the config changes project2 = Project(dirname) - assert ['foo', OrderedDict([('pip', ['baz'])])] == list(project2.project_file.get_value('packages')) + assert ['foo', OrderedDict([('pip', ['baz'])])] == list(project2.project_file.get_value(pkg_key)) assert ['foo', OrderedDict([('pip', ['woot'])]) - ] == list(project2.project_file.get_value(['env_specs', 'hello', 'packages'])) + ] == list(project2.project_file.get_value(['env_specs', 'hello', pkg_key])) for env_spec in project2.env_specs.values(): assert env_spec.lock_set.enabled @@ -2185,26 +2193,26 @@ def attempt(): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ -packages: + DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: - foo - pip: - bar - baz env_specs: hello: - packages: + {pkg_key}: - foo - pip: - bar - woot - hello2: {} + hello2: {{}} """, DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n" }, check) -def test_remove_pip_packages_from_one_environment(): +def test_remove_pip_packages_from_one_environment(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -2214,7 +2222,7 @@ def attempt(): assert env_spec.lock_set.platforms == () assert ['qbert', OrderedDict([('pip', ['pbert', 'foo', - 'bar'])])] == list(project.project_file.get_value('packages')) + 'bar'])])] == list(project.project_file.get_value(pkg_key)) status = project_ops.remove_packages(project, env_spec_name='hello', packages=['foo', 'bar'], pip=True) assert status assert [] == status.errors @@ -2225,9 +2233,9 @@ def attempt(): project2 = Project(dirname) # note that hello will still inherit the deps from the global packages, # and that's fine - assert ['qbert', OrderedDict([('pip', ['pbert'])])] == list(project2.project_file.get_value('packages')) - assert [OrderedDict([('pip', [])]) - ] == list(project2.project_file.get_value(['env_specs', 'hello', 'packages'], [])) + assert ['qbert', OrderedDict([('pip', ['pbert'])])] == list(project2.project_file.get_value(pkg_key)) + assert [OrderedDict([('pip', [])])] == list(project2.project_file.get_value(['env_specs', 'hello', pkg_key], + [])) # be sure we didn't delete comments from global packages section content = codecs.open(project2.project_file.filename, 'r', 'utf-8').read() @@ -2244,8 +2252,8 @@ def attempt(): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ -packages: + DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: # this is a pre comment - qbert # this is a post comment - pip: @@ -2254,7 +2262,7 @@ def attempt(): - bar env_specs: hello: - packages: + {pkg_key}: - pip: - foo """, @@ -2262,7 +2270,7 @@ def attempt(): }, check) -def test_remove_pip_packages_from_one_environment_with_pkgs(): +def test_remove_pip_packages_from_one_environment_with_pkgs(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -2272,7 +2280,7 @@ def attempt(): assert env_spec.lock_set.platforms == () assert ['qbert', OrderedDict([('pip', ['pbert', 'foo', - 'bar'])])] == list(project.project_file.get_value('packages')) + 'bar'])])] == list(project.project_file.get_value(pkg_key)) status = project_ops.remove_packages(project, env_spec_name='hello', packages=['foo', 'bar'], pip=True) assert status assert [] == status.errors @@ -2283,9 +2291,10 @@ def attempt(): project2 = Project(dirname) # note that hello will still inherit the deps from the global packages, # and that's fine - assert ['qbert', OrderedDict([('pip', ['pbert'])])] == list(project2.project_file.get_value('packages')) - assert ['qbert', OrderedDict([('pip', [])]) - ] == list(project2.project_file.get_value(['env_specs', 'hello', 'packages'], [])) + assert ['qbert', OrderedDict([('pip', ['pbert'])])] == list(project2.project_file.get_value(pkg_key)) + assert ['qbert', + OrderedDict([('pip', [])])] == list(project2.project_file.get_value(['env_specs', 'hello', pkg_key], + [])) # be sure we didn't delete comments from global packages section content = codecs.open(project2.project_file.filename, 'r', 'utf-8').read() @@ -2302,8 +2311,8 @@ def attempt(): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ -packages: + DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: # this is a pre comment - qbert # this is a post comment - pip: @@ -2312,14 +2321,14 @@ def attempt(): - bar env_specs: hello: - packages: + {pkg_key}: - qbert """, DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n" }, check) -def test_remove_pip_packages_from_one_environment_empty_pkgs(): +def test_remove_pip_packages_from_one_environment_empty_pkgs(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -2329,7 +2338,7 @@ def attempt(): assert env_spec.lock_set.platforms == () assert ['qbert', OrderedDict([('pip', ['pbert', 'foo', - 'bar'])])] == list(project.project_file.get_value('packages')) + 'bar'])])] == list(project.project_file.get_value(pkg_key)) status = project_ops.remove_packages(project, env_spec_name='hello', packages=['foo', 'bar'], pip=True) assert status assert [] == status.errors @@ -2340,9 +2349,9 @@ def attempt(): project2 = Project(dirname) # note that hello will still inherit the deps from the global packages, # and that's fine - assert ['qbert', OrderedDict([('pip', ['pbert'])])] == list(project2.project_file.get_value('packages')) - assert [OrderedDict([('pip', [])]) - ] == list(project2.project_file.get_value(['env_specs', 'hello', 'packages'], [])) + assert ['qbert', OrderedDict([('pip', ['pbert'])])] == list(project2.project_file.get_value(pkg_key)) + assert [OrderedDict([('pip', [])])] == list(project2.project_file.get_value(['env_specs', 'hello', pkg_key], + [])) # be sure we didn't delete comments from global packages section content = codecs.open(project2.project_file.filename, 'r', 'utf-8').read() @@ -2359,8 +2368,8 @@ def attempt(): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ -packages: + DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: # this is a pre comment - qbert # this is a post comment - pip: @@ -2369,13 +2378,13 @@ def attempt(): - bar env_specs: hello: - packages: [] + {pkg_key}: [] """, DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n" }, check) -def test_remove_packages_from_one_environment(): +def test_remove_packages_from_one_environment(pkg_key): def check(dirname): def attempt(): project = Project(dirname) @@ -2384,8 +2393,8 @@ def attempt(): assert env_spec.lock_set.enabled assert env_spec.lock_set.platforms == () - assert ['qbert', 'foo', 'bar'] == list(project.project_file.get_value('packages')) - assert ['foo'] == list(project.project_file.get_value(['env_specs', 'hello', 'packages'], [])) + assert ['qbert', 'foo', 'bar'] == list(project.project_file.get_value(pkg_key)) + assert ['foo'] == list(project.project_file.get_value(['env_specs', 'hello', pkg_key], [])) status = project_ops.remove_packages(project, env_spec_name='hello', packages=['foo', 'bar']) assert status assert [] == status.errors @@ -2396,8 +2405,8 @@ def attempt(): project2 = Project(dirname) # note that hello will still inherit the deps from the global packages, # and that's fine - assert ['qbert'] == list(project2.project_file.get_value('packages')) - assert [] == list(project2.project_file.get_value(['env_specs', 'hello', 'packages'], [])) + assert ['qbert'] == list(project2.project_file.get_value(pkg_key)) + assert [] == list(project2.project_file.get_value(['env_specs', 'hello', pkg_key], [])) # be sure we didn't delete comments from global packages section content = codecs.open(project2.project_file.filename, 'r', 'utf-8').read() @@ -2414,27 +2423,27 @@ def attempt(): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ -packages: + DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: # this is a pre comment - qbert # this is a post comment - foo - bar env_specs: hello: - packages: + {pkg_key}: - foo """, DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n" }, check) -def test_remove_packages_from_one_environment_leaving_others_unaffected(): +def test_remove_packages_from_one_environment_leaving_others_unaffected(pkg_key): def check(dirname): def attempt(): project = Project(dirname) - assert ['qbert', 'foo', 'bar'] == list(project.project_file.get_value('packages')) - assert ['foo'] == list(project.project_file.get_value(['env_specs', 'hello', 'packages'], [])) + assert ['qbert', 'foo', 'bar'] == list(project.project_file.get_value(pkg_key)) + assert ['foo'] == list(project.project_file.get_value(['env_specs', 'hello', pkg_key], [])) status = project_ops.remove_packages(project, env_spec_name='hello', packages=['foo', 'bar']) assert status assert [] == status.errors @@ -2443,10 +2452,9 @@ def attempt(): # be sure we really made the config changes project2 = Project(dirname) - assert ['qbert'] == list(project2.project_file.get_value('packages')) - assert [] == list(project2.project_file.get_value(['env_specs', 'hello', 'packages'], [])) - assert set(['baz', 'foo', - 'bar']) == set(project2.project_file.get_value(['env_specs', 'another', 'packages'], [])) + assert ['qbert'] == list(project2.project_file.get_value(pkg_key)) + assert [] == list(project2.project_file.get_value(['env_specs', 'hello', pkg_key], [])) + assert set(['baz', 'foo', 'bar']) == set(project2.project_file.get_value(['env_specs', 'another', pkg_key], [])) assert project2.env_specs['another'].conda_package_names_set == set(['qbert', 'foo', 'bar', 'baz']) assert project2.env_specs['hello'].conda_package_names_set == set(['qbert']) @@ -2458,29 +2466,29 @@ def attempt(): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ -packages: + f""" +{pkg_key}: - qbert - foo - bar env_specs: hello: - packages: + {pkg_key}: - foo another: - packages: + {pkg_key}: # this is a pre comment - baz # this is a post comment """ }, check) -def test_remove_pip_packages_from_one_environment_leaving_others_unaffected(): +def test_remove_pip_packages_from_one_environment_leaving_others_unaffected(pkg_key): def check(dirname): def attempt(): project = Project(dirname) assert ['qbert', OrderedDict([('pip', ['pbert', 'foo', - 'bar'])])] == list(project.project_file.get_value('packages')) + 'bar'])])] == list(project.project_file.get_value(pkg_key)) status = project_ops.remove_packages(project, env_spec_name='hello', packages=['foo', 'bar'], pip=True) assert status assert [] == status.errors @@ -2489,11 +2497,11 @@ def attempt(): # be sure we really made the config changes project2 = Project(dirname) - assert ['qbert', OrderedDict([('pip', ['pbert'])])] == list(project2.project_file.get_value('packages')) - assert [OrderedDict([('pip', [])]) - ] == list(project2.project_file.get_value(['env_specs', 'hello', 'packages'], [])) + assert ['qbert', OrderedDict([('pip', ['pbert'])])] == list(project2.project_file.get_value(pkg_key)) + assert [OrderedDict([('pip', [])])] == list(project2.project_file.get_value(['env_specs', 'hello', pkg_key], + [])) assert set(['baz', 'foo', - 'bar']) == set(project2.project_file.get_value(['env_specs', 'another', 'packages'], [])[0]['pip']) + 'bar']) == set(project2.project_file.get_value(['env_specs', 'another', pkg_key], [])[0]['pip']) assert project2.env_specs['another'].pip_package_names_set == set(['foo', 'bar', 'baz', 'pbert']) assert project2.env_specs['hello'].pip_package_names_set == set(['pbert']) @@ -2505,8 +2513,8 @@ def attempt(): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ -packages: + f""" +{pkg_key}: - qbert - pip: - pbert @@ -2514,11 +2522,11 @@ def attempt(): - bar env_specs: hello: - packages: + {pkg_key}: - pip: - foo another: - packages: + {pkg_key}: - pip: # this is a pre comment - baz # this is a post comment @@ -2526,7 +2534,7 @@ def attempt(): }, check) -def test_remove_packages_cannot_resolve_deps(): +def test_remove_packages_cannot_resolve_deps(pkg_key): def check(dirname): def attempt(): os.makedirs(os.path.join(dirname, 'envs', 'hello')) # forces us to really run remove_packages @@ -2535,8 +2543,8 @@ def attempt(): assert env_spec.lock_set.enabled assert env_spec.lock_set.platforms == () - assert ['foo', 'bar', 'baz'] == list(project.project_file.get_value('packages')) - assert ['foo', 'woot'] == list(project.project_file.get_value(['env_specs', 'hello', 'packages'], [])) + assert ['foo', 'bar', 'baz'] == list(project.project_file.get_value(pkg_key)) + assert ['foo', 'woot'] == list(project.project_file.get_value(['env_specs', 'hello', pkg_key], [])) status = project_ops.remove_packages(project, env_spec_name=None, packages=['foo', 'bar']) assert status.status_description == "Error resolving dependencies for hello: NOPE." assert status.errors == [] @@ -2547,8 +2555,8 @@ def attempt(): # be sure we didn't make the config changes project2 = Project(dirname) - assert ['foo', 'bar', 'baz'] == list(project2.project_file.get_value('packages')) - assert ['foo', 'woot'] == list(project2.project_file.get_value(['env_specs', 'hello', 'packages'])) + assert ['foo', 'bar', 'baz'] == list(project2.project_file.get_value(pkg_key)) + assert ['foo', 'woot'] == list(project2.project_file.get_value(['env_specs', 'hello', pkg_key])) for env_spec in project2.env_specs.values(): assert env_spec.lock_set.enabled @@ -2556,14 +2564,14 @@ def attempt(): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ -packages: + DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: - foo - bar - baz env_specs: hello: - packages: + {pkg_key}: - foo - woot """, @@ -2571,11 +2579,11 @@ def attempt(): }, check) -def test_remove_packages_from_nonexistent_environment(): +def test_remove_packages_from_nonexistent_environment(pkg_key): def check(dirname): def attempt(): project = Project(dirname) - assert ['foo', 'bar'] == list(project.project_file.get_value('packages')) + assert ['foo', 'bar'] == list(project.project_file.get_value(pkg_key)) status = project_ops.remove_packages(project, env_spec_name='not_an_environment', packages=['foo', 'bar']) assert not status assert [] == status.errors @@ -2585,10 +2593,10 @@ def attempt(): # be sure we didn't make the config changes project2 = Project(dirname) - assert ['foo', 'bar'] == list(project2.project_file.get_value('packages')) + assert ['foo', 'bar'] == list(project2.project_file.get_value(pkg_key)) - with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: """ -packages: + with_directory_contents_completing_project_file({DEFAULT_PROJECT_FILENAME: f""" +{pkg_key}: - foo - bar """}, check) @@ -2924,7 +2932,8 @@ def attempt(): with_directory_contents({DEFAULT_PROJECT_FILENAME: ""}, check) -def test_lock_and_update_and_unlock_all_envs(): +@pytest._change_default_pkg_key +def test_lock_and_update_and_unlock_all_envs(pkg_key): def check(dirname): resolve_results = {'all': ['a=1.0=1'], 'pip': ['cc==1.0']} @@ -2933,7 +2942,6 @@ def attempt(): assert not os.path.isfile(filename) project = Project(dirname, frontend=FakeFrontend()) - assert project.env_specs['foo'].platforms == () assert project.env_specs['bar'].platforms == () @@ -2951,7 +2959,7 @@ def attempt(): '+ linux-64', '+ osx-64', '+ win-64', - ' packages:', + f' {pkg_key}:', '+ all:', '+ a=1.0=1', '+ pip:', @@ -2963,7 +2971,7 @@ def attempt(): '+ linux-64', '+ osx-64', '+ win-64', - ' packages:', + f' {pkg_key}:', '+ all:', '+ a=1.0=1', '+ pip:', @@ -3047,16 +3055,16 @@ def attempt(): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - """ + f""" name: locktest env_specs: foo: - packages: + {pkg_key}: - a - pip: - cc bar: - packages: + {pkg_key}: - b - pip: - dd @@ -3064,7 +3072,8 @@ def attempt(): }, check) -def test_lock_and_unlock_single_env(): +@pytest._change_default_pkg_key +def test_lock_and_unlock_single_env(pkg_key): def check(dirname): def attempt(): filename = os.path.join(dirname, DEFAULT_PROJECT_LOCK_FILENAME) @@ -3088,7 +3097,7 @@ def attempt(): '+ linux-64', '+ osx-64', '+ win-64', - ' packages:', + f' {pkg_key}:', '+ all:', '+ a=1.0=1', 'Added locked dependencies for env spec foo to anaconda-project-lock.yml.'] == project.frontend.logs @@ -3167,21 +3176,22 @@ def attempt(): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - """ + f""" name: locktest env_specs: foo: - packages: + {pkg_key}: - a bar: platforms: [osx-64] - packages: + {pkg_key}: - b """ }, check) -def test_locking_with_missing_lock_set_does_an_update(): +@pytest._change_default_pkg_key +def test_locking_with_missing_lock_set_does_an_update(pkg_key): def check(dirname): def attempt(): filename = os.path.join(dirname, DEFAULT_PROJECT_LOCK_FILENAME) @@ -3206,7 +3216,7 @@ def attempt(): '+ linux-64', '+ osx-64', '+ win-64', - ' packages:', + f' {pkg_key}:', '+ all:', '+ a=1.0=1', 'Added locked dependencies for env spec foo to anaconda-project-lock.yml.'] == project.frontend.logs @@ -3227,12 +3237,12 @@ def attempt(): with_directory_contents( { - DEFAULT_PROJECT_FILENAME: """ + DEFAULT_PROJECT_FILENAME: f""" name: locktest platforms: [linux-64,osx-64,win-64] env_specs: foo: - packages: + {pkg_key}: - a """, DEFAULT_PROJECT_LOCK_FILENAME: """ @@ -3242,7 +3252,8 @@ def attempt(): }, check) -def test_update_changes_only_the_hash(): +@pytest._change_default_pkg_key +def test_update_changes_only_the_hash(pkg_key): def check(dirname): def attempt(): project = Project(dirname, frontend=FakeFrontend()) @@ -3273,28 +3284,28 @@ def attempt(): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - """ + f""" name: locktest platforms: [linux-32,linux-64,osx-64,win-32,win-64] env_specs: foo: - packages: + {pkg_key}: - a """, DEFAULT_PROJECT_LOCK_FILENAME: - """ + f""" locking_enabled: true env_specs: foo: platforms: [linux-32,linux-64,osx-64,win-32,win-64] env_spec_hash: old - packages: + {pkg_key}: all: ['a=1.0=1'] """ }, check) -def test_lock_conda_error(): +def test_lock_conda_error(pkg_key): def check(dirname): def attempt(): filename = os.path.join(dirname, DEFAULT_PROJECT_LOCK_FILENAME) @@ -3316,21 +3327,21 @@ def attempt(): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - """ + f""" name: locktest platforms: [linux-32,linux-64,osx-64,win-32,win-64] env_specs: foo: - packages: + {pkg_key}: - a bar: - packages: + {pkg_key}: - b """ }, check) -def test_lock_resolve_dependencies_error(monkeypatch): +def test_lock_resolve_dependencies_error(monkeypatch, pkg_key): def check(dirname): def attempt(): filename = os.path.join(dirname, DEFAULT_PROJECT_LOCK_FILENAME) @@ -3349,21 +3360,21 @@ def attempt(): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - """ + f""" name: locktest platforms: [linux-32,linux-64,osx-64,win-32,win-64] env_specs: foo: - packages: + {pkg_key}: - a bar: - packages: + {pkg_key}: - b """ }, check) -def test_unlock_conda_error(): +def test_unlock_conda_error(pkg_key): def check(dirname): def attempt(): filename = os.path.join(dirname, DEFAULT_PROJECT_LOCK_FILENAME) @@ -3392,38 +3403,38 @@ def attempt(): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - """ + f""" name: locktest platforms: [linux-32,linux-64,osx-64,win-32,win-64] env_specs: foo: - packages: + {pkg_key}: - a bar: - packages: + {pkg_key}: - b """, DEFAULT_PROJECT_LOCK_FILENAME: - """ + f""" locking_enabled: true env_specs: foo: locked: true platforms: [linux-32,linux-64,osx-64,win-32,win-64] - packages: + {pkg_key}: all: - c bar: locked: true platforms: [linux-32,linux-64,osx-64,win-32,win-64] - packages: + {pkg_key}: all: - d """ }, check) -def test_update_unlocked_envs(): +def test_update_unlocked_envs(pkg_key): def check(dirname): resolve_results = {'all': ['a=1.0=1']} @@ -3459,21 +3470,22 @@ def attempt(): with_directory_contents( { DEFAULT_PROJECT_FILENAME: - """ + f""" name: locktest platforms: [linux-32,linux-64,osx-64,win-32,win-64] env_specs: foo: - packages: + {pkg_key}: - a bar: - packages: + {pkg_key}: - b """ }, check) -def test_update_empty_lock_sets(): +@pytest._change_default_pkg_key +def test_update_empty_lock_sets(pkg_key): def check(dirname): resolve_results = {'all': ['a=1.0=1']} @@ -3499,7 +3511,7 @@ def attempt(): '+ linux-64', '+ osx-64', '+ win-64', - ' packages:', + f' {pkg_key}:', '+ all:', '+ a=1.0=1', 'Updated locked dependencies for env spec bar in anaconda-project-lock.yml.', @@ -3509,7 +3521,7 @@ def attempt(): '+ linux-64', '+ osx-64', '+ win-64', - ' packages:', + f' {pkg_key}:', '+ all:', '+ a=1.0=1', 'Updated locked dependencies for env spec foo in anaconda-project-lock.yml.' @@ -3525,22 +3537,22 @@ def attempt(): with_directory_contents( { - DEFAULT_PROJECT_FILENAME: """ + DEFAULT_PROJECT_FILENAME: f""" name: locktest platforms: [linux-64,osx-64,win-64] env_specs: foo: - packages: + {pkg_key}: - a bar: - packages: + {pkg_key}: - b """, DEFAULT_PROJECT_LOCK_FILENAME: "locking_enabled: true\n" }, check) -def test_export_env_spec(): +def test_export_env_spec(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) exported = os.path.join(dirname, "exported.yml") @@ -3549,17 +3561,17 @@ def check(dirname): assert status.status_description == ('Exported environment spec default to %s.' % exported) with_directory_contents_completing_project_file( - {"anaconda-project.yml": """ + {"anaconda-project.yml": f""" env_specs: default: - packages: + {pkg_key}: - blah channels: - boo """}, check) -def test_export_nonexistent_env_spec(): +def test_export_nonexistent_env_spec(pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) exported = os.path.join(dirname, "exported.yml") @@ -3569,17 +3581,17 @@ def check(dirname): assert status.status_description == "Environment spec bar doesn't exist." with_directory_contents_completing_project_file( - {"anaconda-project.yml": """ + {"anaconda-project.yml": f""" env_specs: default: - packages: + {pkg_key}: - blah channels: - boo """}, check) -def test_export_env_spec_io_error(monkeypatch): +def test_export_env_spec_io_error(monkeypatch, pkg_key): def check(dirname): project = project_no_dedicated_env(dirname) exported = os.path.join(dirname, "exported.yml") @@ -3594,10 +3606,10 @@ def mock_atomic_replace(*args, **kwargs): assert status.status_description == ("Failed to save %s: NOOO." % exported) with_directory_contents_completing_project_file( - {"anaconda-project.yml": """ + {"anaconda-project.yml": f""" env_specs: default: - packages: + {pkg_key}: - blah channels: - boo @@ -3644,7 +3656,7 @@ def check(dirname): with_directory_contents_completing_project_file(dict(), check) -def test_add_service_with_env_spec(monkeypatch): +def test_add_service_with_env_spec(monkeypatch, pkg_key): def check(dirname): _monkeypatch_can_connect_to_socket_on_standard_redis_port(monkeypatch) @@ -3662,13 +3674,13 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ + f""" env_specs: default: - packages: [python] + {pkg_key}: [python] channels: [] myspec: - packages: [python] + {pkg_key}: [python] channels: [] """ }, check) @@ -3818,7 +3830,7 @@ def check(dirname): """}, check) -def test_remove_service_with_env_spec(monkeypatch): +def test_remove_service_with_env_spec(monkeypatch, pkg_key): def check(dirname): config_path = ['env_specs', 'myspec', 'services', 'REDIS_URL'] project = project_no_dedicated_env(dirname) @@ -3834,13 +3846,13 @@ def check(dirname): with_directory_contents_completing_project_file( { DEFAULT_PROJECT_FILENAME: - """ + f""" env_specs: default: - packages: [python] + {pkg_key}: [python] channels: [] myspec: - packages: [python] + {pkg_key}: [python] channels: [] services: REDIS_URL: redis @@ -4105,7 +4117,7 @@ def check(dirname): with_directory_contents_completing_project_file(dict(), archivetest) -def test_archive_unlocked_warning(): +def test_archive_unlocked_warning(pkg_key): def archivetest(archive_dest_dir): archivefile = os.path.join(archive_dest_dir, "foo.zip") @@ -4134,21 +4146,21 @@ def check(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ + DEFAULT_PROJECT_FILENAME: f""" name: archivedproj env_specs: foo: - packages: [] + {pkg_key}: [] bar: - packages: [] + {pkg_key}: [] """, - DEFAULT_PROJECT_LOCK_FILENAME: """ + DEFAULT_PROJECT_LOCK_FILENAME: f""" locking_enabled: false env_specs: foo: locked: true platforms: [linux-32,linux-64,osx-64,win-32,win-64] - packages: + {pkg_key}: all: [] """, "foo.py": "print('hello')\n" @@ -4573,7 +4585,7 @@ def check(dirname): with_directory_contents(dict(), archivetest) -def test_archive_with_unsaved_project(monkeypatch): +def test_archive_with_unsaved_project(monkeypatch, pkg_key): def archivetest(archive_dest_dir): archivefile = os.path.join(archive_dest_dir, "foo.zip") @@ -4590,10 +4602,10 @@ def check(dirname): assert status.errors == ["%s has been modified but not saved." % DEFAULT_PROJECT_FILENAME] with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: """ + {DEFAULT_PROJECT_FILENAME: f""" env_specs: default: - packages: [] + {pkg_key}: [] """}, check) with_directory_contents(dict(), archivetest) @@ -4692,7 +4704,7 @@ def check(dirname): @pytest.mark.skipif((sys.version_info.major == 2) and (platform.system() == 'Linux'), reason='Something wrong with pip freeze on linux for py2') @pytest.mark.parametrize('suffix', ['zip', 'tar.bz2', 'tar.gz']) -def test_archive_unarchive_conda_pack_with_pip(suffix): +def test_archive_unarchive_conda_pack_with_pip(suffix, pkg_key): def archivetest(archive_dest_dir): archivefile = os.path.join(archive_dest_dir, "foo.{}".format(suffix)) @@ -4719,9 +4731,9 @@ def check(dirname): assert status with_directory_contents_completing_project_file( - {DEFAULT_PROJECT_FILENAME: """ + {DEFAULT_PROJECT_FILENAME: f""" name: archivedproj -packages: +{pkg_key}: - python=3.7 - pip: - pep8 @@ -4732,7 +4744,7 @@ def check(dirname): @pytest.mark.slow @pytest.mark.parametrize('suffix', ['zip', 'tar.bz2', 'tar.gz']) -def test_archive_unarchive_conda_pack(suffix): +def test_archive_unarchive_conda_pack(suffix, pkg_key): def archivetest(archive_dest_dir): archivefile = os.path.join(archive_dest_dir, "foo.{}".format(suffix)) @@ -4808,9 +4820,9 @@ def check(dirname): with_directory_contents_completing_project_file( { - DEFAULT_PROJECT_FILENAME: """ + DEFAULT_PROJECT_FILENAME: f""" name: archivedproj -packages: +{pkg_key}: - font-ttf-ubuntu=0.83=h8b1ccd4_0 """, "foo.py": "print('hello')\n", diff --git a/anaconda_project/yaml_file.py b/anaconda_project/yaml_file.py index 4736f34a..dd1e24e7 100644 --- a/anaconda_project/yaml_file.py +++ b/anaconda_project/yaml_file.py @@ -115,6 +115,8 @@ class YamlFile(object): """ + pkg_key = "packages" # Default: "packages" or "dependencies" + # The dummy entry works around a bug/quirk in ruamel.yaml that drops the # top comment for an empty dictionary template = '# yaml file\n__dummy__: dummy' @@ -137,6 +139,11 @@ def __init__(self, filename): self._change_count = 0 self.load() + pkg_key = self._get_pkg_key() + if pkg_key: + # Only set if defined, otherwise fallback to the class default. + self.pkg_key = pkg_key + def load(self): """Reload the file from disk, discarding any unsaved changes. @@ -198,6 +205,30 @@ def load(self): # pretend we already saved self._previous_content = _dump_string(self._yaml) + def _get_pkg_key(self): + """Return the `pkg_key` ('dependencies' or 'packages') for the file. + + We search for an appropriately named key at the top level, or in the + `env_specs`. Return `None` if none specified. + """ + pkg_key = None + try: + for _pkg_key in ['dependencies', 'packages']: + if self.get_value(_pkg_key): + assert not pkg_key or pkg_key == _pkg_key + pkg_key = _pkg_key + env_specs = self.get_value('env_specs') + if env_specs: + for _spec in env_specs: + for _pkg_key in ['dependencies', 'packages']: + if _pkg_key in env_specs[_spec]: + assert not pkg_key or pkg_key == _pkg_key + pkg_key = _pkg_key + except Exception: + # Don't do error checking here... + pass + return pkg_key + def _load_template(self): # ruamel.yaml returns None if you load an empty file, # so we have to build this ourselves diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..539c1355 --- /dev/null +++ b/conftest.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright (c) 2017, Anaconda, Inc. All rights reserved. +# +# Licensed under the terms of the BSD 3-Clause License. +# (See LICENSE.txt for details) +# ----------------------------------------------------------------------------- +import functools + +import pytest + + +@pytest.fixture(params=["packages", "dependencies"]) +def pkg_key(request): + """Ensure equivalence between `dependencies` and `packages`""" + yield request.param + + +def _change_default_pkg_key(test_function): + from anaconda_project.yaml_file import YamlFile + + @functools.wraps(test_function) + def wrapper(*v, **kw): + old_pkg_key, YamlFile.pkg_key = YamlFile.pkg_key, kw['pkg_key'] + try: + return test_function(*v, **kw) + finally: + YamlFile.pkg_key = old_pkg_key + + return wrapper + + +pytest._change_default_pkg_key = _change_default_pkg_key diff --git a/examples/quote_api/quote.py b/examples/quote_api/quote.py index a4108805..feaa536f 100644 --- a/examples/quote_api/quote.py +++ b/examples/quote_api/quote.py @@ -88,17 +88,20 @@ def load(self): # arg parser for the standard project options parser = ArgumentParser(prog="quote-api", description="API server that returns a quote.") parser.add_argument('--anaconda-project-host', action='append', help='Hostname to allow in requests') -parser.add_argument( - '--anaconda-project-no-browser', action='store_true', default=False, help='Disable opening in a browser') -parser.add_argument( - '--anaconda-project-use-xheaders', action='store_true', default=False, help='Trust X-headers from reverse proxy') +parser.add_argument('--anaconda-project-no-browser', + action='store_true', + default=False, + help='Disable opening in a browser') +parser.add_argument('--anaconda-project-use-xheaders', + action='store_true', + default=False, + help='Trust X-headers from reverse proxy') parser.add_argument('--anaconda-project-url-prefix', action='store', default='', help='Prefix in front of urls') parser.add_argument('--anaconda-project-port', action='store', default='8080', help='Port to listen on') parser.add_argument('--anaconda-project-address', action='store', default='0.0.0.0', help='IP to listen on') -parser.add_argument( - '--anaconda-project-iframe-hosts', - action='append', - help='Space-separated hosts which can embed us in an iframe per our Content-Security-Policy') +parser.add_argument('--anaconda-project-iframe-hosts', + action='append', + help='Space-separated hosts which can embed us in an iframe per our Content-Security-Policy') if __name__ == '__main__': # This app accepts but ignores --anaconda-project-no-browser because we never bother to open a browser, @@ -106,11 +109,10 @@ def load(self): args = parser.parse_args(sys.argv[1:]) if not args.anaconda_project_host: args.anaconda_project_host = ['localhost:' + args.anaconda_project_port] - app = QuoteApplication( - port=args.anaconda_project_port, - address=args.anaconda_project_address, - prefix=args.anaconda_project_url_prefix, - hosts=args.anaconda_project_host) + app = QuoteApplication(port=args.anaconda_project_port, + address=args.anaconda_project_address, + prefix=args.anaconda_project_url_prefix, + hosts=args.anaconda_project_host) print("Starting API server. Try http://localhost:%s%s" % (app.port, app.prefix + '/quote')) try: app.run() diff --git a/examples/stocks/main.py b/examples/stocks/main.py index 74f37fc9..6b9cb500 100644 --- a/examples/stocks/main.py +++ b/examples/stocks/main.py @@ -75,15 +75,14 @@ def get_data(t1, t2): tools = 'pan,wheel_zoom,xbox_select,reset' corr = figure(plot_width=350, plot_height=350, tools='pan,wheel_zoom,box_select,reset') -corr.circle( - 't1_returns', - 't2_returns', - size=2, - source=source, - selection_color="orange", - alpha=0.6, - nonselection_alpha=0.1, - selection_alpha=0.4) +corr.circle('t1_returns', + 't2_returns', + size=2, + source=source, + selection_color="orange", + alpha=0.6, + nonselection_alpha=0.1, + selection_alpha=0.4) ts1 = figure(plot_width=900, plot_height=200, tools=tools, x_axis_type='datetime', active_drag="xbox_select") ts1.line('date', 't1', source=source_static) diff --git a/scripts/create_conda_packages.py b/scripts/create_conda_packages.py index 0efd227a..02e84b7c 100644 --- a/scripts/create_conda_packages.py +++ b/scripts/create_conda_packages.py @@ -90,8 +90,8 @@ def _real_run(self): final_package_path = os.path.join(python_scoped_package_dir, build_arch, os.path.basename(package_path)) all_final_package_paths.append(final_package_path) if os.path.isfile(final_package_path): - print("Package for python %s platform %s already exists: %s" % (python_version, build_arch, - final_package_path)) + print("Package for python %s platform %s already exists: %s" % + (python_version, build_arch, final_package_path)) else: if os.path.isfile(package_path): print("Already built for python %s at %s" % (python_version, package_path)) @@ -128,8 +128,8 @@ def _real_run(self): 'conda', 'convert', '--platform', arch, final_package_path, '--output-dir', converted_output_dir ]) if code != 0: - raise Exception( - "Failed to convert from %s to %s to create %s" % (build_arch, arch, converted_package_path)) + raise Exception("Failed to convert from %s to %s to create %s" % + (build_arch, arch, converted_package_path)) all_final_package_paths.append(converted_package_path) print("Packages in " + self.packages_dir) @@ -144,8 +144,11 @@ def _safe_makedirs(self, path): def main(): parser = argparse.ArgumentParser(description='Script to create conda packages for all platforms and ' 'python version 2.7, 3.5 and 3.6') - parser.add_argument( - action="store", dest="packages_dir", default=None, help="location directory for built packages", nargs='?') + parser.add_argument(action="store", + dest="packages_dir", + default=None, + help="location directory for built packages", + nargs='?') options = parser.parse_args() cpc = CondaPackageCreator(packages_dir=options.packages_dir) diff --git a/scripts/run_tests.py b/scripts/run_tests.py index eeef5294..fc5d6d44 100644 --- a/scripts/run_tests.py +++ b/scripts/run_tests.py @@ -435,12 +435,11 @@ def main(): ) options = parser.parse_args() - tr = TestRunner( - pytest_args=options.pytest_args, - format_only=options.format_only, - git_staged_only=options.git_staged_only, - skip_slow_tests=options.skip_slow_tests, - profile_formatting=options.profile_formatting) + tr = TestRunner(pytest_args=options.pytest_args, + format_only=options.format_only, + git_staged_only=options.git_staged_only, + skip_slow_tests=options.skip_slow_tests, + profile_formatting=options.profile_formatting) tr.run_tests() diff --git a/versioneer.py b/versioneer.py index df710512..85232ed9 100644 --- a/versioneer.py +++ b/versioneer.py @@ -378,7 +378,6 @@ class NotThisMethod(Exception): def register_vcs_handler(vcs, method): # decorator """Decorator to mark a method as the handler for a particular VCS.""" - def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: @@ -397,8 +396,11 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env= try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen( - [c] + args, cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=(subprocess.PIPE if hide_stderr else None)) + p = subprocess.Popen([c] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None)) break except EnvironmentError: e = sys.exc_info()[1]