diff --git a/.gitignore b/.gitignore index 816cc97aab1..6ff12ce9b7d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ MANIFEST.in pyproject.lock /tests/fixtures/simple_project/setup.py .mypy_cache + +.venv diff --git a/docs/docs/repositories.md b/docs/docs/repositories.md index 4aae8ccca94..238ddeec1a1 100644 --- a/docs/docs/repositories.md +++ b/docs/docs/repositories.md @@ -63,3 +63,8 @@ url = "https://foo.bar/simple/" ``` From now on, Poetry will also look for packages in your private repository. + +If your private repository requires HTTP Basic Auth be sure to add the username and +password to your `http-basic` config using the example above (be sure to use the +same name than in the `tool.poetry.source` section). Poetry will use these values +to authenticate to your private repository when downloading or looking for packages. diff --git a/poetry/installation/pip_installer.py b/poetry/installation/pip_installer.py index 175d2bcedfa..4343b5f9673 100644 --- a/poetry/installation/pip_installer.py +++ b/poetry/installation/pip_installer.py @@ -3,6 +3,9 @@ from subprocess import CalledProcessError +from poetry.utils.helpers import get_http_basic_auth + + try: import urllib.parse as urlparse except ImportError: @@ -32,7 +35,19 @@ def install(self, package, update=False): ) args += ["--trusted-host", parsed.netloc] - args += ["--index-url", package.source_url] + auth = get_http_basic_auth(package.source_reference) + if auth: + index_url = "{scheme}://{username}:{password}@{netloc}{path}".format( + scheme=parsed.scheme, + username=auth[0], + password=auth[1], + netloc=parsed.netloc, + path=parsed.path, + ) + else: + index_url = package.source_url + + args += ["--index-url", index_url] if update: args.append("-U") diff --git a/poetry/masonry/publishing/publisher.py b/poetry/masonry/publishing/publisher.py index 475841a631a..426bc4db7d6 100644 --- a/poetry/masonry/publishing/publisher.py +++ b/poetry/masonry/publishing/publisher.py @@ -1,5 +1,6 @@ from poetry.locations import CONFIG_DIR from poetry.utils._compat import Path +from poetry.utils.helpers import get_http_basic_auth from poetry.utils.toml_file import TomlFile from .uploader import Uploader @@ -64,18 +65,10 @@ def publish(self, repository_name, username, password): url = config["repositories"][repository_name]["url"] if not (username and password): - auth_file = TomlFile(Path(CONFIG_DIR) / "auth.toml") - if auth_file.exists(): - auth_config = auth_file.read(raw=True) - - if ( - "http-basic" in auth_config - and repository_name in auth_config["http-basic"] - ): - config = auth_config["http-basic"][repository_name] - - username = config.get("username") - password = config.get("password") + auth = get_http_basic_auth(repository_name) + if auth: + username = auth[0] + password = auth[1] # Requesting missing credentials if not username: diff --git a/poetry/repositories/legacy_repository.py b/poetry/repositories/legacy_repository.py index 199472a15bd..b0857a72152 100644 --- a/poetry/repositories/legacy_repository.py +++ b/poetry/repositories/legacy_repository.py @@ -28,6 +28,7 @@ import poetry.packages +from poetry.config import Config from poetry.locations import CACHE_DIR from poetry.masonry.publishing.uploader import wheel_file_re from poetry.packages import Package @@ -37,7 +38,7 @@ from poetry.semver import Version from poetry.semver import VersionConstraint from poetry.utils._compat import Path -from poetry.utils.helpers import canonicalize_name +from poetry.utils.helpers import canonicalize_name, get_http_basic_auth from poetry.version.markers import InvalidMarker from .pypi_repository import PyPiRepository @@ -159,6 +160,10 @@ def __init__(self, name, url, disable_cache=False): requests.session(), cache=FileCache(str(self._cache_dir / "_http")) ) + url_parts = urlparse.urlparse(self._url) + if not url_parts.username: + self._session.auth = get_http_basic_auth(self.name) + self._disable_cache = disable_cache @property @@ -237,6 +242,7 @@ def package( package = poetry.packages.Package(name, version, version) package.source_type = "legacy" package.source_url = self._url + package.source_reference = self.name requires_dist = release_info["requires_dist"] or [] for req in requires_dist: @@ -333,6 +339,13 @@ def _get_release_info(self, name, version): # type: (str, str) -> dict return data + def _download(self, url, dest): # type: (str, str) -> None + r = self._session.get(url, stream=True) + with open(dest, "wb") as f: + for chunk in r.iter_content(chunk_size=1024): + if chunk: + f.write(chunk) + def _get(self, endpoint): # type: (str) -> Union[Page, None] url = self._url + endpoint response = self._session.get(url) diff --git a/poetry/utils/helpers.py b/poetry/utils/helpers.py index e085c05f0e1..48b48a0a0ff 100644 --- a/poetry/utils/helpers.py +++ b/poetry/utils/helpers.py @@ -5,6 +5,7 @@ from contextlib import contextmanager from typing import Union +from poetry.config import Config from poetry.version import Version _canonicalize_regex = re.compile("[-_]+") @@ -77,3 +78,11 @@ def parse_requires(requires): # type: (str) -> Union[list, None] if requires_dist: return requires_dist + + +def get_http_basic_auth(repository_name): # type: (str) -> tuple + config = Config.create("auth.toml") + repo_auth = config.setting("http-basic.{}".format(repository_name)) + if repo_auth: + return repo_auth["username"], repo_auth["password"] + return None diff --git a/tests/repositories/test_legacy_repository.py b/tests/repositories/test_legacy_repository.py index 9fe5d5830bb..77ffaed82c1 100644 --- a/tests/repositories/test_legacy_repository.py +++ b/tests/repositories/test_legacy_repository.py @@ -1,9 +1,6 @@ -import pytest - from poetry.repositories.legacy_repository import LegacyRepository from poetry.repositories.legacy_repository import Page from poetry.utils._compat import Path -from poetry.utils._compat import decode class MockRepository(LegacyRepository): @@ -43,3 +40,13 @@ def test_page_absolute_links_path_are_correct(): for link in page.links: assert link.netloc == "files.pythonhosted.org" assert link.path.startswith("/packages/") + + +def test_http_basic_auth_repo(mocker): + mock = mocker.patch("poetry.repositories.legacy_repository.get_http_basic_auth") + mock.return_value = ("user1", "p4ss") + + repo = MockRepository() + + mock.assert_called_once_with("legacy") + assert repo._session.auth == ("user1", "p4ss")