Exemple #1
0
    def find_latest_package(self, package, include_dev):
        from clikit.io import NullIO

        from poetry.puzzle.provider import Provider
        from poetry.version.version_selector import VersionSelector

        # find the latest version allowed in this pool
        if package.source_type in ("git", "file", "directory"):
            requires = self.poetry.package.requires
            if include_dev:
                requires = requires + self.poetry.package.dev_requires

            for dep in requires:
                if dep.name == package.name:
                    provider = Provider(self.poetry.package, self.poetry.pool,
                                        NullIO())

                    if dep.is_vcs():
                        return provider.search_for_vcs(dep)[0]
                    if dep.is_file():
                        return provider.search_for_file(dep)[0]
                    if dep.is_directory():
                        return provider.search_for_directory(dep)[0]

        name = package.name
        selector = VersionSelector(self.poetry.pool)

        return selector.find_best_candidate(
            name, ">={}".format(package.pretty_version))
def test_publish_publishes_to_pypi_by_default(fixture_dir, mocker, config):
    uploader_auth = mocker.patch(
        "poetry.masonry.publishing.uploader.Uploader.auth")
    uploader_upload = mocker.patch(
        "poetry.masonry.publishing.uploader.Uploader.upload")
    poetry = Factory().create_poetry(fixture_dir("sample_project"))
    poetry._config = config
    poetry.config.merge(
        {"http-basic": {
            "pypi": {
                "username": "******",
                "password": "******"
            }
        }})
    publisher = Publisher(poetry, NullIO())

    publisher.publish(None, None, None)

    assert [("foo", "bar")] == uploader_auth.call_args
    assert [
        ("https://upload.pypi.org/legacy/", ),
        {
            "cert": None,
            "client_cert": None
        },
    ] == uploader_upload.call_args
def test_publish_can_publish_to_given_repository(fixture_dir, mocker, config):
    uploader_auth = mocker.patch(
        "poetry.masonry.publishing.uploader.Uploader.auth")
    uploader_upload = mocker.patch(
        "poetry.masonry.publishing.uploader.Uploader.upload")
    poetry = Factory().create_poetry(fixture_dir("sample_project"))
    poetry._config = config
    poetry.config.merge({
        "repositories": {
            "my-repo": {
                "url": "http://foo.bar"
            }
        },
        "http-basic": {
            "my-repo": {
                "username": "******",
                "password": "******"
            }
        },
    })
    publisher = Publisher(poetry, NullIO())

    publisher.publish("my-repo", None, None)

    assert [("foo", "bar")] == uploader_auth.call_args
    assert [
        ("http://foo.bar", ),
        {
            "cert": None,
            "client_cert": None
        },
    ] == uploader_upload.call_args
def test_publish_uses_client_cert(fixture_dir, mocker, config):
    client_cert = "path/to/client.pem"
    uploader_upload = mocker.patch(
        "poetry.masonry.publishing.uploader.Uploader.upload")
    poetry = Factory().create_poetry(fixture_dir("sample_project"))
    poetry._config = config
    poetry.config.merge({
        "repositories": {
            "foo": {
                "url": "https://foo.bar"
            }
        },
        "certificates": {
            "foo": {
                "client-cert": client_cert
            }
        },
    })
    publisher = Publisher(poetry, NullIO())

    publisher.publish("foo", None, None)

    assert [
        ("https://foo.bar", ),
        {
            "cert": None,
            "client_cert": Path(client_cert)
        },
    ] == uploader_upload.call_args
Exemple #5
0
def test_install_with_client_cert():
    client_path = "path/to/client.pem"
    pool = Pool()

    default = LegacyRepository("default",
                               "https://foo.bar",
                               client_cert=Path(client_path))

    pool.add_repository(default, default=True)

    null_env = NullEnv()

    installer = PipInstaller(null_env, NullIO(), pool)

    foo = Package(
        "foo",
        "0.0.0",
        source_type="legacy",
        source_reference=default.name,
        source_url=default.url,
    )

    installer.install(foo)

    assert len(null_env.executed) == 1
    cmd = null_env.executed[0]
    assert "--client-cert" in cmd
    cert_index = cmd.index("--client-cert")
    # Need to do the str(Path()) bit because Windows paths get modified by Path
    assert cmd[cert_index + 1] == str(Path(client_path))
def test_authenticator_uses_provided_certs_instead_of_config_certs(
        config, mock_remote, http, mocker):
    config.merge({
        "repositories": {
            "foo": {
                "url": "https://foo.bar/simple/"
            }
        },
        "http-basic": {
            "foo": {
                "username": "******",
                "password": "******"
            }
        },
        "certificates": {
            "foo": {
                "cert": "/path/to/cert",
                "client-cert": "/path/to/client-cert",
            }
        },
    })

    authenticator = Authenticator(config, NullIO())
    session_send = mocker.patch.object(authenticator.session, "send")
    authenticator.request(
        "get",
        "https://foo.bar/files/foo-0.1.0.tar.gz",
        verify="/path/to/provided/cert",
        cert="/path/to/provided/client-cert",
    )
    call_args = session_send.call_args
    call_args.kwargs["verify"] == pathlib.Path("/path/to/provided/cert")
    call_args.kwargs["cert"] == pathlib.Path("/path/to/provided/client-cert")
Exemple #7
0
def test_uploader_properly_handles_403_errors(http):
    http.register_uri(http.POST, "https://foo.com", status=403, body="Unauthorized")
    uploader = Uploader(Factory().create_poetry(project("simple_project")), NullIO())

    with pytest.raises(UploadError) as e:
        uploader.upload("https://foo.com")

    assert "HTTP Error 403: Forbidden" == str(e.value)
Exemple #8
0
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
Exemple #9
0
    def install_directory(self, package):
        from poetry.factory import Factory
        from poetry.io.null_io import NullIO

        if package.root_dir:
            req = (package.root_dir / package.source_url).as_posix()
        else:
            req = os.path.realpath(package.source_url)

        args = ["install", "--no-deps", "-U"]

        pyproject = PyProjectTOML(os.path.join(req, "pyproject.toml"))

        if pyproject.is_poetry_project():
            # Even if there is a build system specified
            # some versions of pip (< 19.0.0) don't understand it
            # so we need to check the version of pip to know
            # if we can rely on the build system
            legacy_pip = self._env.pip_version < self._env.pip_version.__class__(
                19, 0, 0)
            package_poetry = Factory().create_poetry(
                pyproject.file.path.parent)

            if package.develop and not package_poetry.package.build_script:
                from poetry.masonry.builders.editable import EditableBuilder

                # This is a Poetry package in editable mode
                # we can use the EditableBuilder without going through pip
                # to install it, unless it has a build script.
                builder = EditableBuilder(package_poetry, self._env, NullIO())
                builder.build()

                return 0
            elif legacy_pip or package_poetry.package.build_script:
                from poetry.core.masonry.builders.sdist import SdistBuilder

                # We need to rely on creating a temporary setup.py
                # file since the version of pip does not support
                # build-systems
                # We also need it for non-PEP-517 packages
                builder = SdistBuilder(package_poetry)

                with builder.setup_py():
                    if package.develop:
                        args.append("-e")

                    args.append(req)

                    return self.run_pip(*args)

        if package.develop:
            args.append("-e")

        args.append(req)

        return self.run(*args)
Exemple #10
0
def test_publish_raises_error_for_undefined_repository(fixture_dir, mocker, config):
    poetry = Factory().create_poetry(fixture_dir("sample_project"))
    poetry._config = config
    poetry.config.merge(
        {"http-basic": {"my-repo": {"username": "******", "password": "******"}}}
    )
    publisher = Publisher(poetry, NullIO())

    with pytest.raises(RuntimeError):
        publisher.publish("my-repo", None, None)
Exemple #11
0
def test_uploader_properly_handles_400_errors(http):
    http.register_uri(http.POST,
                      "https://foo.com",
                      status=400,
                      body="Bad request")
    uploader = Uploader(Poetry.create(project("simple_project")), NullIO())

    with pytest.raises(UploadError) as e:
        uploader.upload("https://foo.com")

    assert "HTTP Error 400: Bad Request" == str(e.value)
Exemple #12
0
def test_requirement_source_type_url():
    installer = PipInstaller(NullEnv(), NullIO(), Pool())

    foo = Package("foo", "0.0.0")
    foo.source_type = "url"
    foo.source_url = "https://somehwere.com/releases/foo-1.0.0.tar.gz"

    result = installer.requirement(foo, formatted=True)
    expected = "{}#egg={}".format(foo.source_url, foo.name)

    assert expected == result
Exemple #13
0
def test_uploader_registers_for_appropriate_400_errors(mocker, http):
    register = mocker.patch("poetry.masonry.publishing.uploader.Uploader._register")
    http.register_uri(
        http.POST, "https://foo.com", status=400, body="No package was ever registered"
    )
    uploader = Uploader(Factory().create_poetry(project("simple_project")), NullIO())

    with pytest.raises(UploadError):
        uploader.upload("https://foo.com")

    assert 1 == register.call_count
def test_authenticator_uses_env_provided_credentials(
        config, environ, mock_remote, http,
        environment_repository_credentials):
    config.merge({"repositories": {"foo": {"url": "https://foo.bar/simple/"}}})

    authenticator = Authenticator(config, NullIO())
    authenticator.request("get", "https://foo.bar/files/foo-0.1.0.tar.gz")

    request = http.last_request()

    assert "Basic YmFyOmJheg==" == request.headers["Authorization"]
Exemple #15
0
def build_packages() -> List[Package]:
    poetry = Factory().create_poetry(Path.cwd())
    env = NullEnv()
    io = NullIO()

    with TemporaryDirectory() as temp_dir_str:
        temp_dir = Path(temp_dir_str)
        wheel_pkg_name = WheelBuilder.make_in(poetry, env, io, temp_dir)
        pkg_path = temp_dir / wheel_pkg_name
        pkg_bytes = pkg_path.read_bytes()
        pkgs = [Package(wheel_pkg_name, pkg_bytes)]

    return pkgs
Exemple #16
0
def test_uploader_properly_handles_301_redirects(http):
    http.register_uri(http.POST,
                      "https://foo.com",
                      status=301,
                      body="Redirect")
    uploader = Uploader(Factory().create_poetry(project("simple_project")),
                        NullIO())

    with pytest.raises(UploadError) as e:
        uploader.upload("https://foo.com")

    assert "Redirects are not supported. Is the URL missing a trailing slash?" == str(
        e.value)
Exemple #17
0
def test_publish_uses_token_if_it_exists(fixture_dir, mocker, config):
    uploader_auth = mocker.patch("poetry.publishing.uploader.Uploader.auth")
    uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload")
    poetry = Factory().create_poetry(fixture_dir("sample_project"))
    poetry._config = config
    poetry.config.merge({"pypi-token": {"pypi": "my-token"}})
    publisher = Publisher(poetry, NullIO())

    publisher.publish(None, None, None)

    assert [("__token__", "my-token")] == uploader_auth.call_args
    assert [
        ("https://upload.pypi.org/legacy/",),
        {"cert": None, "client_cert": None, "dry_run": False},
    ] == uploader_upload.call_args
def test_authenticator_request_raises_exception_when_attempts_exhausted(
        mocker, config, http):
    sleep = mocker.patch("time.sleep")
    sdist_uri = "https://foo.bar/files/{}/foo-0.1.0.tar.gz".format(
        str(uuid.uuid4()))

    def callback(*_, **__):
        raise requests.exceptions.ConnectionError(str(uuid.uuid4()))

    httpretty.register_uri(httpretty.GET, sdist_uri, body=callback)
    authenticator = Authenticator(config, NullIO())

    with pytest.raises(requests.exceptions.ConnectionError):
        authenticator.request("get", sdist_uri)

    assert sleep.call_count == 5
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
Exemple #20
0
def test_publish_read_from_environment_variable(fixture_dir, environ, mocker, config):
    os.environ["POETRY_REPOSITORIES_FOO_URL"] = "https://foo.bar"
    os.environ["POETRY_HTTP_BASIC_FOO_USERNAME"] = "******"
    os.environ["POETRY_HTTP_BASIC_FOO_PASSWORD"] = "******"
    uploader_auth = mocker.patch("poetry.publishing.uploader.Uploader.auth")
    uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload")
    poetry = Factory().create_poetry(fixture_dir("sample_project"))
    publisher = Publisher(poetry, NullIO())

    publisher.publish("foo", None, None)

    assert [("bar", "baz")] == uploader_auth.call_args
    assert [
        ("https://foo.bar",),
        {"cert": None, "client_cert": None, "dry_run": False},
    ] == uploader_upload.call_args
Exemple #21
0
def test_requirement():
    installer = PipInstaller(NullEnv(), NullIO(), Pool())

    package = Package("ipython", "7.5.0")
    package.hashes = [
        "md5:dbdc53e3918f28fa335a173432402a00",
        "e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26",
    ]

    result = installer.requirement(package, formatted=True)
    expected = (
        "ipython==7.5.0 "
        "--hash md5:dbdc53e3918f28fa335a173432402a00 "
        "--hash sha256:e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26"
        "\n")

    assert expected == result
Exemple #22
0
    def build(self):
        # We start by building the tarball
        # We will use it to build the wheel
        sdist_builder = SdistBuilder(self._poetry, self._env, self._io)
        build_for_all_formats = False
        for p in self._package.packages:
            formats = p.get("format", [])
            if not isinstance(formats, list):
                formats = [formats]

            if formats and sdist_builder.format not in formats:
                build_for_all_formats = True
                break

        sdist_file = sdist_builder.build()

        self._io.write_line("")

        dist_dir = self._path / "dist"

        if build_for_all_formats:
            sdist_builder = SdistBuilder(self._poetry,
                                         self._env,
                                         NullIO(),
                                         ignore_packages_formats=True)
            with temporary_directory() as tmp_dir:
                sdist_file = sdist_builder.build(Path(tmp_dir))

                with self.unpacked_tarball(sdist_file) as tmpdir:
                    WheelBuilder.make_in(
                        Factory().create_poetry(tmpdir),
                        self._env,
                        self._io,
                        dist_dir,
                        original=self._poetry,
                    )
        else:
            with self.unpacked_tarball(sdist_file) as tmpdir:
                WheelBuilder.make_in(
                    Factory().create_poetry(tmpdir),
                    self._env,
                    self._io,
                    dist_dir,
                    original=self._poetry,
                )
def test_authenticator_request_retries_on_exception(mocker, config, http):
    sleep = mocker.patch("time.sleep")
    sdist_uri = "https://foo.bar/files/{}/foo-0.1.0.tar.gz".format(
        str(uuid.uuid4()))
    content = str(uuid.uuid4())
    seen = list()

    def callback(request, uri, response_headers):
        if seen.count(uri) < 2:
            seen.append(uri)
            raise requests.exceptions.ConnectionError("Disconnected")
        return [200, response_headers, content]

    httpretty.register_uri(httpretty.GET, sdist_uri, body=callback)

    authenticator = Authenticator(config, NullIO())
    response = authenticator.request("get", sdist_uri)
    assert response.text == content
    assert sleep.call_count == 2
def test_authenticator_request_retries_on_status_code(mocker, config, http,
                                                      status, attempts):
    sleep = mocker.patch("time.sleep")
    sdist_uri = "https://foo.bar/files/{}/foo-0.1.0.tar.gz".format(
        str(uuid.uuid4()))
    content = str(uuid.uuid4())

    def callback(request, uri, response_headers):
        return [status, response_headers, content]

    httpretty.register_uri(httpretty.GET, sdist_uri, body=callback)
    authenticator = Authenticator(config, NullIO())

    with pytest.raises(requests.exceptions.HTTPError) as excinfo:
        authenticator.request("get", sdist_uri)

    assert excinfo.value.response.status_code == status
    assert excinfo.value.response.text == content

    assert sleep.call_count == attempts
Exemple #25
0
def test_uninstall_git_package_nspkg_pth_cleanup(mocker, tmp_venv, pool):
    # this test scenario requires a real installation using the pip installer
    installer = PipInstaller(tmp_venv, NullIO(), pool)

    # use a namepspace package
    package = Package(
        "namespace-package-one",
        "1.0.0",
        source_type="git",
        source_url="https://github.com/demo/namespace-package-one.git",
        source_reference="master",
    )

    # we do this here because the virtual env might not be usable if failure case is triggered
    pth_file_candidate = tmp_venv.site_packages.path / "{}-nspkg.pth".format(
        package.name)

    # in order to reproduce the scenario where the git source is removed prior to proper
    # clean up of nspkg.pth file, we need to make sure the fixture is copied and not
    # symlinked into the git src directory
    def copy_only(source, dest):
        if dest.exists():
            dest.unlink()

        if source.is_dir():
            shutil.copytree(str(source), str(dest))
        else:
            shutil.copyfile(str(source), str(dest))

    mocker.patch("tests.helpers.copy_or_symlink", new=copy_only)

    # install package and then remove it
    installer.install(package)
    installer.remove(package)

    assert not Path(pth_file_candidate).exists()

    # any command in the virtual environment should trigger the error message
    output = tmp_venv.run("python", "-m", "site")
    assert "Error processing line 1 of {}".format(
        pth_file_candidate) not in output
Exemple #26
0
def test_builder_installs_proper_files_when_packages_configured(
    project_with_include, tmp_venv
):
    builder = EditableBuilder(project_with_include, tmp_venv, NullIO())
    builder.build()

    pth_file = tmp_venv.site_packages.joinpath("with_include.pth")
    assert pth_file.is_file()

    paths = set()
    with pth_file.open() as f:
        for line in f.readlines():
            line = line.strip(os.linesep)
            if line:
                paths.add(line)

    project_root = project_with_include.file.parent.resolve()
    expected = {project_root.as_posix(), project_root.joinpath("src").as_posix()}

    assert paths.issubset(expected)
    assert len(paths) == len(expected)
def test_authenticator_uses_empty_strings_as_default_password(
        config, mock_remote, http):
    config.merge({
        "repositories": {
            "foo": {
                "url": "https://foo.bar/simple/"
            }
        },
        "http-basic": {
            "foo": {
                "username": "******"
            }
        },
    })

    authenticator = Authenticator(config, NullIO())
    authenticator.request("get", "https://foo.bar/files/foo-0.1.0.tar.gz")

    request = http.last_request()

    assert "Basic YmFyOg==" == request.headers["Authorization"]
Exemple #28
0
def test_install_with_non_pypi_default_repository():
    pool = Pool()

    default = LegacyRepository("default", "https://default.com")
    another = LegacyRepository("another", "https://another.com")

    pool.add_repository(default, default=True)
    pool.add_repository(another)

    installer = PipInstaller(NullEnv(), NullIO(), pool)

    foo = Package("foo", "0.0.0")
    foo.source_type = "legacy"
    foo.source_reference = default._name
    foo.source_url = default._url
    bar = Package("bar", "0.1.0")
    bar.source_type = "legacy"
    bar.source_reference = another._name
    bar.source_url = another._url

    installer.install(foo)
    installer.install(bar)
Exemple #29
0
def test_publish_uses_cert(fixture_dir, mocker, config):
    cert = "path/to/ca.pem"
    uploader_auth = mocker.patch("poetry.publishing.uploader.Uploader.auth")
    uploader_upload = mocker.patch("poetry.publishing.uploader.Uploader.upload")
    poetry = Factory().create_poetry(fixture_dir("sample_project"))
    poetry._config = config
    poetry.config.merge(
        {
            "repositories": {"foo": {"url": "https://foo.bar"}},
            "http-basic": {"foo": {"username": "******", "password": "******"}},
            "certificates": {"foo": {"cert": cert}},
        }
    )
    publisher = Publisher(poetry, NullIO())

    publisher.publish("foo", None, None)

    assert [("foo", "bar")] == uploader_auth.call_args
    assert [
        ("https://foo.bar",),
        {"cert": Path(cert), "client_cert": None, "dry_run": False},
    ] == uploader_upload.call_args
def test_authenticator_uses_credentials_from_config_if_not_provided(
        config, mock_remote, http):
    config.merge({
        "repositories": {
            "foo": {
                "url": "https://foo.bar/simple/"
            }
        },
        "http-basic": {
            "foo": {
                "username": "******",
                "password": "******"
            }
        },
    })

    authenticator = Authenticator(config, NullIO())
    authenticator.request("get", "https://foo.bar/files/foo-0.1.0.tar.gz")

    request = http.last_request()

    assert "Basic YmFyOmJheg==" == request.headers["Authorization"]