def test_build_environment_not_called_without_build_script_specified( mocker: MockerFixture, poetry: Poetry, tmp_dir: str): project_env = MockEnv(path=Path(tmp_dir) / "project") ephemeral_env = MockEnv(path=Path(tmp_dir) / "ephemeral") mocker.patch("poetry.utils.env.ephemeral_environment" ).return_value.__enter__.return_value = ephemeral_env with build_environment(poetry, project_env) as env: assert env == project_env assert not env.executed
def test_tags_cpython37(): assert ( get_abi_tag( MockEnv( version_info=(3, 7, 3), python_implementation="CPython", config_vars={"Py_DEBUG": True, "WITH_PYMALLOC": True}, ) ) == "cp37dm" ) assert ( get_abi_tag( MockEnv( version_info=(3, 7, 3), python_implementation="CPython", config_vars={"Py_DEBUG": True, "WITH_PYMALLOC": False}, ) ) == "cp37d" ) assert ( get_abi_tag( MockEnv( version_info=(3, 7, 3), python_implementation="CPython", config_vars={"Py_DEBUG": False, "WITH_PYMALLOC": True}, ) ) == "cp37m" ) assert ( get_abi_tag( MockEnv( version_info=(3, 7, 3), python_implementation="CPython", config_vars={"Py_DEBUG": False, "WITH_PYMALLOC": False}, ) ) == "cp37" ) with pytest.warns(RuntimeWarning): assert ( get_abi_tag( MockEnv( version_info=(3, 7, 3), python_implementation="CPython", config_vars={"Py_DEBUG": False}, ) ) == "cp37m" )
def test_installer_can_handle_old_lock_files(installer, locker, package, repo, installed, config): pool = Pool() pool.add_repository(MockRepository()) package.add_dependency("pytest", "^3.5", category="dev") locker.locked() locker.mock_lock_data(fixture("old-lock")) installer = Installer(NullIO(), MockEnv(), package, locker, pool, config, installed=installed) installer.run() assert 6 == len(installer.installer.installs) installer = Installer( NullIO(), MockEnv(version_info=(2, 7, 18)), package, locker, pool, config, installed=installed, ) installer.run() # funcsigs will be added assert 7 == len(installer.installer.installs) installer = Installer( NullIO(), MockEnv(version_info=(2, 7, 18), platform="win32"), package, locker, pool, config, installed=installed, ) installer.run() # colorama will be added assert 8 == len(installer.installer.installs)
def env(): return MockEnv( supported_tags=[ Tag("cp37", "cp37", "macosx_10_15_x86_64"), Tag("py3", "none", "any"), ] )
def test_get_cached_archive_for_link(config: Config, mocker: MockerFixture, link: str, cached: str): chef = Chef( config, MockEnv( version_info=(3, 8, 3), marker_env={ "interpreter_name": "cpython", "interpreter_version": "3.8.3" }, supported_tags=[ Tag("cp38", "cp38", "macosx_10_15_x86_64"), Tag("py3", "none", "any"), ], ), ) mocker.patch.object( chef, "get_cached_archives_for_link", return_value=[ Path("/cache/demo-0.1.0-py2.py3-none-any"), Path("/cache/demo-0.1.0.tar.gz"), Path("/cache/demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl"), Path("/cache/demo-0.1.0-cp37-cp37-macosx_10_15_x86_64.whl"), ], ) archive = chef.get_cached_archive_for_link(Link(link)) assert Path(cached) == archive
def setup(mocker): mocker.patch( "poetry.utils.env.EnvManager.get", return_value=MockEnv(path=Path("/prefix"), base=Path("/base/prefix"), is_venv=True), )
def test_executor_should_use_cached_link_and_hash( config, io, pool, mocker, fixture_dir, tmp_dir ): # Produce a file:/// URI that is a valid link link_cached = Link( fixture_dir("distributions") .joinpath("demo-0.1.0-py2.py3-none-any.whl") .as_uri() ) mocker.patch.object( Chef, "get_cached_archive_for_link", side_effect=lambda _: link_cached ) env = MockEnv(path=Path(tmp_dir)) executor = Executor(env, pool, config, io) package = Package("demo", "0.1.0") package.files = [ { "file": "demo-0.1.0-py2.py3-none-any.whl", "hash": "md5:15507846fd4299596661d0197bfb4f90", } ] archive = executor._download_link( Install(package), Link("https://example.com/demo-0.1.0-py2.py3-none-any.whl") ) assert archive == link_cached
def test_executor_should_check_every_possible_hash_types( config, io, pool, mocker, fixture_dir, tmp_dir ): mocker.patch.object( Chef, "get_cached_archive_for_link", side_effect=lambda link: link, ) mocker.patch.object( Executor, "_download_archive", return_value=fixture_dir("distributions").joinpath( "demo-0.1.0-py2.py3-none-any.whl" ), ) env = MockEnv(path=Path(tmp_dir)) executor = Executor(env, pool, config, io) package = Package("demo", "0.1.0") package.files = [ { "file": "demo-0.1.0-py2.py3-none-any.whl", "hash": "md5:15507846fd4299596661d0197bfb4f90", } ] archive = executor._download_link( Install(package), Link("https://example.com/demo-0.1.0-py2.py3-none-any.whl") ) assert archive == fixture_dir("distributions").joinpath( "demo-0.1.0-py2.py3-none-any.whl" )
def test_executor_should_delete_incomplete_downloads(config, io, tmp_dir, mocker, pool, mock_file_downloads): fixture = Path(__file__).parent.parent.joinpath( "fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl") destination_fixture = Path(tmp_dir) / "tomlkit-0.5.3-py2.py3-none-any.whl" shutil.copyfile(str(fixture), str(destination_fixture)) mocker.patch( "poetry.installation.executor.Executor._download_archive", side_effect=Exception("Download error"), ) mocker.patch( "poetry.installation.chef.Chef.get_cached_archive_for_link", side_effect=lambda link: link, ) mocker.patch( "poetry.installation.chef.Chef.get_cache_directory_for_link", return_value=Path(tmp_dir), ) config = Config() config.merge({"cache-dir": tmp_dir}) env = MockEnv(path=Path(tmp_dir)) executor = Executor(env, pool, config, io) with pytest.raises(Exception, match="Download error"): executor._download(Install(Package("tomlkit", "0.5.3"))) assert not destination_fixture.exists()
def test_build_should_temporarily_remove_the_pyproject_file(tmp_dir, mocker): move = mocker.patch("shutil.move") tmp_dir = Path(tmp_dir) env = MockEnv(path=tmp_dir, pip_version="19.1", execute=False, sys_path=[]) module_path = fixtures_dir / "extended" builder = EditableBuilder(Factory().create_poetry(module_path), env, NullIO()) builder.build() expected = [[ sys.executable, "-m", "pip", "install", "-e", str(module_path) ]] assert expected == env.executed assert 2 == move.call_count expected_calls = [ mocker.call(str(module_path / "pyproject.toml"), str(module_path / "pyproject.tmp")), mocker.call(str(module_path / "pyproject.tmp"), str(module_path / "pyproject.toml")), ] assert expected_calls == move.call_args_list
def test_get_prefers_explicitly_activated_non_existing_virtualenvs_over_env_var( mocker, tester, current_python, venv_cache, venv_name, venvs_in_cache_config ): os.environ["VIRTUAL_ENV"] = "/environment/prefix" python_minor = ".".join(str(v) for v in current_python[:2]) venv_dir = venv_cache / "{}-py{}".format(venv_name, python_minor) mocker.patch( "poetry.utils.env.EnvManager._env", new_callable=mocker.PropertyMock, return_value=MockEnv( path=Path("/environment/prefix"), base=Path("/base/prefix"), version_info=current_python, is_venv=True, ), ) mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build_venv) tester.execute(python_minor) expected = """\ Creating virtualenv {} in {} Using virtualenv: {} """.format( venv_dir.name, venv_dir.parent, venv_dir, ) assert expected == tester.io.fetch_output()
def test_get_prefers_explicitly_activated_non_existing_virtualenvs_over_env_var( mocker: MockerFixture, tester: CommandTester, current_python: tuple[int, int, int], venv_cache: Path, venv_name: str, venvs_in_cache_config: None, ): os.environ["VIRTUAL_ENV"] = "/environment/prefix" python_minor = ".".join(str(v) for v in current_python[:2]) venv_dir = venv_cache / f"{venv_name}-py{python_minor}" mocker.patch( "poetry.utils.env.EnvManager._env", new_callable=mocker.PropertyMock, return_value=MockEnv( path=Path("/environment/prefix"), base=Path("/base/prefix"), version_info=current_python, is_venv=True, ), ) mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build_venv) tester.execute(python_minor) expected = f"""\ Creating virtualenv {venv_dir.name} in {venv_dir.parent} Using virtualenv: {venv_dir} """ assert tester.io.fetch_output() == expected
def setup(mocker, installer, installed): Env._env = MockEnv() # Set Installer's installer p = mocker.patch("poetry.installation.installer.Installer._get_installer") p.return_value = installer p = mocker.patch("poetry.installation.installer.Installer._get_installed") p.return_value = installed p = mocker.patch( "poetry.repositories.installed_repository.InstalledRepository.load") p.return_value = installed # Patch git module to not actually clone projects mocker.patch("poetry.vcs.git.Git.clone", new=mock_clone) mocker.patch("poetry.vcs.git.Git.checkout", new=lambda *_: None) p = mocker.patch("poetry.vcs.git.Git.rev_parse") p.return_value = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24" # Setting terminal width environ = dict(os.environ) os.environ["COLUMNS"] = "80" yield os.environ.clear() os.environ.update(environ) Env._env = None
def test_get_cached_archives_for_link(config, mocker): chef = Chef( config, MockEnv(marker_env={ "interpreter_name": "cpython", "interpreter_version": "3.8.3" }), ) distributions = Path(__file__).parent.parent.joinpath( "fixtures/distributions") mocker.patch.object( chef, "get_cache_directory_for_link", return_value=distributions, ) archives = chef.get_cached_archives_for_link( Link("https://files.python-poetry.org/demo-0.1.0.tar.gz")) assert archives assert set(archives) == { Link(path.as_uri()) for path in distributions.glob("demo-0.1.0*") }
def test_get_cached_archive_for_link(config, mocker): chef = Chef( config, MockEnv( version_info=(3, 8, 3), marker_env={ "interpreter_name": "cpython", "interpreter_version": "3.8.3" }, supported_tags=[ Tag("cp38", "cp38", "macosx_10_15_x86_64"), Tag("py3", "none", "any"), ], ), ) cwd = Path.cwd() / ".pypoetrycache" mocker.patch.object( chef, "get_cached_archives_for_link", return_value=[ Link(f"file:///{cwd}demo-0.1.0-py2.py3-none-any"), Link(f"file:///{cwd}demo-0.1.0.tar.gz"), Link(f"file:///{cwd}demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl"), Link(f"file:///{cwd}demo-0.1.0-cp37-cp37-macosx_10_15_x86_64.whl"), ], ) archive = chef.get_cached_archive_for_link( Link("https://files.python-poetry.org/demo-0.1.0.tar.gz")) assert Link(f"file:///{cwd}demo-0.1.0-cp38-cp38-macosx_10_15_x86_64.whl" ) == archive
def test_execute_executes_a_batch_of_operations( config, pool, io, tmp_dir, mock_file_downloads ): config = Config() config.merge({"cache-dir": tmp_dir}) env = MockEnv(path=Path(tmp_dir)) executor = Executor(env, pool, config, io) file_package = Package("demo", "0.1.0") file_package.source_type = "file" file_package.source_url = str( Path(__file__) .parent.parent.joinpath( "fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl" ) .resolve() ) directory_package = Package("simple-project", "1.2.3") directory_package.source_type = "directory" directory_package.source_url = str( Path(__file__).parent.parent.joinpath("fixtures/simple_project").resolve() ) git_package = Package("demo", "0.1.0") git_package.source_type = "git" git_package.source_reference = "master" git_package.source_url = "https://github.com/demo/demo.git" assert 0 == executor.execute( [ Install(Package("pytest", "3.5.2")), Uninstall(Package("attrs", "17.4.0")), Update(Package("requests", "2.18.3"), Package("requests", "2.18.4")), Uninstall(Package("clikit", "0.2.3")).skip("Not currently installed"), Install(file_package), Install(directory_package), Install(git_package), ] ) expected = """ Package operations: 4 installs, 1 update, 1 removal • Installing pytest (3.5.2) • Removing attrs (17.4.0) • Updating requests (2.18.3 -> 2.18.4) • Installing demo (0.1.0 {}) • Installing simple-project (1.2.3 {}) • Installing demo (0.1.0 master) """.format( file_package.source_url, directory_package.source_url ) expected = set(expected.splitlines()) output = set(io.fetch_output().splitlines()) assert expected == output assert 5 == len(env.executed)
def test_builder_should_execute_build_scripts(extended_without_setup_poetry): env = MockEnv(path=Path("/foo")) builder = EditableBuilder(extended_without_setup_poetry, env, NullIO()) builder.build() assert [ ["python", str(extended_without_setup_poetry.file.parent / "build.py")] ] == env.executed
def test_tags_cpython38(): assert ( get_abi_tag( MockEnv( version_info=(3, 8, 0), python_implementation="CPython", config_vars={"Py_DEBUG": True}, ) ) == "cp38d" ) assert ( get_abi_tag( MockEnv( version_info=(3, 8, 0), python_implementation="CPython", config_vars={}, ) ) == "cp38" )
def test_build_environment_called_build_script_specified( mocker: MockerFixture, extended_without_setup_poetry: Poetry, tmp_dir: str): project_env = MockEnv(path=Path(tmp_dir) / "project") ephemeral_env = MockEnv(path=Path(tmp_dir) / "ephemeral") mocker.patch("poetry.utils.env.ephemeral_environment" ).return_value.__enter__.return_value = ephemeral_env with build_environment(extended_without_setup_poetry, project_env) as env: assert env == ephemeral_env assert env.executed == [[ "python", env.pip_embedded, "install", "--disable-pip-version-check", "--ignore-installed", *extended_without_setup_poetry.pyproject.build_system.requires, ]]
def test_run_passes_all_args(app, mocker): env = MockEnv(path=Path("/prefix"), base=Path("/base/prefix"), is_venv=True) mocker.patch("poetry.utils.env.EnvManager.get", return_value=env) command = app.find("run") tester = CommandTester(command) tester.execute("python -V") assert [["python", "-V"]] == env.executed
def test_builder_falls_back_on_setup_and_pip_for_packages_with_build_scripts( mocker, extended_poetry, tmp_dir): pip_editable_install = mocker.patch( "poetry.masonry.builders.editable.pip_editable_install") env = MockEnv(path=Path(tmp_dir) / "foo") builder = EditableBuilder(extended_poetry, env, NullIO()) builder.build() pip_editable_install.assert_called_once_with( extended_poetry.pyproject.file.path.parent, env) assert [] == env.executed
def test_get_prefers_explicitly_activated_non_existing_virtualenvs_over_env_var( app, tmp_dir, config, mocker): app.poetry._config = config os.environ["VIRTUAL_ENV"] = "/environment/prefix" venv_name = EnvManager.generate_env_name("simple_project", str(app.poetry.file.parent)) current_python = sys.version_info[:3] python_minor = ".".join(str(v) for v in current_python[:2]) config.add_property("settings.virtualenvs.path", str(tmp_dir)) mocker.patch( "poetry.utils.env.EnvManager._env", new_callable=mocker.PropertyMock, return_value=MockEnv( path=Path("/environment/prefix"), base=Path("/base/prefix"), version_info=current_python, is_venv=True, ), ) mocker.patch( "poetry.utils._compat.subprocess.check_output", side_effect=check_output_wrapper(Version(*current_python)), ) mocker.patch( "poetry.utils._compat.subprocess.Popen.communicate", side_effect=[("/prefix", None), ("/prefix", None), ("/prefix", None)], ) mocker.patch("poetry.utils.env.EnvManager.build_venv", side_effect=build_venv) command = app.find("env use") tester = CommandTester(command) tester.execute(python_minor) expected = """\ Creating virtualenv {} in {} Using virtualenv: {} """.format( "{}-py{}".format(venv_name, python_minor), tmp_dir, os.path.join(tmp_dir, "{}-py{}".format(venv_name, python_minor)), ) assert expected == tester.io.fetch_output()
def test_build_should_delegate_to_pip_for_non_pure_python_packages( tmp_dir, mocker): move = mocker.patch("shutil.move") tmp_dir = Path(tmp_dir) env = MockEnv(path=tmp_dir, pip_version="18.1", execute=False) env.site_packages.mkdir(parents=True) module_path = fixtures_dir / "extended" builder = EditableBuilder(Poetry.create(module_path), env, NullIO()) builder.build() expected = [["python", "-m", "pip", "install", "-e", str(module_path)]] assert expected == env.executed assert 0 == move.call_count
def test_builder_should_execute_build_scripts( mocker: MockerFixture, extended_without_setup_poetry: Poetry, tmp_dir: str): env = MockEnv(path=Path(tmp_dir) / "foo") mocker.patch("poetry.masonry.builders.editable.build_environment" ).return_value.__enter__.return_value = env builder = EditableBuilder(extended_without_setup_poetry, env, NullIO()) builder.build() assert [[ "python", str(extended_without_setup_poetry.file.parent / "build.py") ]] == env.executed
def test_get_cache_directory_for_link(config): chef = Chef( config, MockEnv( marker_env={"interpreter_name": "cpython", "interpreter_version": "3.8.3"} ), ) directory = chef.get_cache_directory_for_link( Link("https://files.python-poetry.org/poetry-1.1.0.tar.gz") ) expected = Path( "/foo/artifacts/ba/63/13/283a3b3b7f95f05e9e6f84182d276f7bb0951d5b0cc24422b33f7a4648" ) assert expected == directory
def test_builder_falls_back_on_setup_and_pip_for_packages_with_build_scripts( extended_poetry, tmp_dir): env = MockEnv(path=Path(tmp_dir) / "foo") builder = EditableBuilder(extended_poetry, env, NullIO()) builder.build() assert [[ "python", "-m", "pip", "install", "-e", str(extended_poetry.file.parent), "--no-deps", ]] == env.executed
def test_chooser_chooses_system_specific_wheel_link_if_available( mock_pypi, mock_legacy, source_type, pool ): env = MockEnv( supported_tags=[Tag("cp37", "cp37m", "win32"), Tag("py3", "none", "any")] ) chooser = Chooser(pool, env) package = Package("pyyaml", "3.13.0") if source_type == "legacy": package.source_type = "legacy" package.source_reference = "foo" package.source_url = "https://foo.bar/simple/" link = chooser.choose_for(package) assert "PyYAML-3.13-cp37-cp37m-win32.whl" == link.filename
def test_execute_should_show_operation_as_cancelled_on_subprocess_keyboard_interrupt( config, mocker, io): env = MockEnv() executor = Executor(env, pool, config, io) executor.verbose() # A return code of -2 means KeyboardInterrupt in the pip subprocess mocker.patch.object(executor, "_install", return_value=-2) assert 1 == executor.execute([Install(Package("clikit", "0.2.3"))]) expected = """ Package operations: 1 install, 0 updates, 0 removals • Installing clikit (0.2.3) • Installing clikit (0.2.3): Cancelled """ assert expected == io.fetch_output()
def test_execute_shows_skipped_operations_if_verbose(config, pool, io): config = Config() config.merge({"cache-dir": "/foo"}) env = MockEnv() executor = Executor(env, pool, config, io) executor.verbose() assert 0 == executor.execute([ Uninstall(Package("clikit", "0.2.3")).skip("Not currently installed") ]) expected = """ Package operations: 0 installs, 0 updates, 0 removals, 1 skipped • Removing clikit (0.2.3): Skipped for the following reason: Not currently installed """ assert expected == io.fetch_output() assert 0 == len(env.executed)
def test_execute_should_show_errors(config, mocker, io): env = MockEnv() executor = Executor(env, pool, config, io) executor.verbose() mocker.patch.object(executor, "_install", side_effect=Exception("It failed!")) assert 1 == executor.execute([Install(Package("clikit", "0.2.3"))]) expected = """ Package operations: 1 install, 0 updates, 0 removals • Installing clikit (0.2.3) Exception It failed! """ assert expected in io.fetch_output()