Skip to content

Commit

Permalink
Add support for declaring sources as optional
Browse files Browse the repository at this point in the history
  • Loading branch information
Sébastien Eustace authored and sdispater committed May 16, 2019
1 parent 01373f9 commit 77a75fa
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 9 deletions.
4 changes: 4 additions & 0 deletions poetry/json/schemas/poetry-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,10 @@
"default": {
"type": "boolean",
"description": "Make this repository the default (disable PyPI)"
},
"optional": {
"type": "boolean",
"description": "Make this repository optional, i.e. it must be explicitely opted in for dependencies that require it"
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion poetry/poetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ def __init__(
self._pool = Pool()
for source in self._local_config.get("source", []):
repository = self.create_legacy_repository(source)
self._pool.add_repository(repository, source.get("default", False))
self._pool.add_repository(
repository,
source.get("default", False),
optional=source.get("optional", False),
)

# Always put PyPI last to prefer private repositories
# but only if we have no other default source
Expand Down
37 changes: 29 additions & 8 deletions poetry/repositories/pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from typing import List
from typing import Optional

from poetry.utils._compat import OrderedDict

from .base_repository import BaseRepository
from .exceptions import PackageNotFound
from .repository import Repository
Expand All @@ -17,6 +19,7 @@ def __init__(
self._lookup = {} # type: Dict[str, int]
self._repositories = [] # type: List[Repository]
self._default = None
self._optional = OrderedDict()

for repository in repositories:
self.add_repository(repository)
Expand All @@ -40,17 +43,25 @@ def has_default(self): # type: () -> bool
return self._default is not None

def repository(self, name): # type: (str) -> Repository
if name not in self._lookup:
raise ValueError('Repository "{}" does not exist.'.format(name))
if name in self._lookup:
return self._repositories[self._lookup[name]]

if name in self._optional:
return self._optional[name]

return self._repositories[self._lookup[name]]
raise ValueError('Repository "{}" does not exist.'.format(name))

def add_repository(
self, repository, default=False
self, repository, default=False, optional=False
): # type: (Repository, bool) -> Pool
"""
Adds a repository to the pool.
"""
if optional:
self._optional[repository.name] = repository

return self

if default:
if self.has_default():
raise ValueError("Only one repository can be the default")
Expand Down Expand Up @@ -88,15 +99,19 @@ def package(
if (
repository is not None
and repository not in self._lookup
and repository not in self._optional
and not self._ignore_repository_names
):
raise ValueError('Repository "{}" does not exist.'.format(repository))

if repository is not None and not self._ignore_repository_names:
try:
return self._repositories[self._lookup[repository]].package(
name, version, extras=extras
)
if repository in self._lookup:
return self._repositories[self._lookup[repository]].package(
name, version, extras=extras
)

return self._optional[repository].package(name, version, extras=extras)
except PackageNotFound:
pass
else:
Expand Down Expand Up @@ -124,12 +139,18 @@ def find_packages(
if (
repository is not None
and repository not in self._lookup
and repository not in self._optional
and not self._ignore_repository_names
):
raise ValueError('Repository "{}" does not exist.'.format(repository))

if repository is not None and not self._ignore_repository_names:
return self._repositories[self._lookup[repository]].find_packages(
if repository in self._lookup:
return self._repositories[self._lookup[repository]].find_packages(
name, constraint, extras=extras, allow_prereleases=allow_prereleases
)

return self._optional[repository].find_packages(
name, constraint, extras=extras, allow_prereleases=allow_prereleases
)

Expand Down
22 changes: 22 additions & 0 deletions tests/puzzle/test_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -1643,3 +1643,25 @@ def test_solver_chooses_from_correct_repository_if_forced_and_transitive_depende

assert "" == ops[1].package.source_type
assert "" == ops[1].package.source_url


def test_solver_does_not_choose_from_optional_repository_by_default(
package, installed, locked, io
):
package.python_versions = "^3.7"
package.add_dependency("tomlkit", {"version": "^0.5"})

pool = Pool()
pool.add_repository(MockPyPIRepository(), optional=True)
pool.add_repository(MockLegacyRepository())

solver = Solver(package, pool, installed, locked, io)

ops = solver.solve()

check_solver_result(
ops, [{"job": "install", "package": get_package("tomlkit", "0.5.2")}]
)

assert "legacy" == ops[0].package.source_type
assert "http://foo.bar" == ops[0].package.source_url
24 changes: 24 additions & 0 deletions tests/repositories/test_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from poetry.repositories import Pool
from poetry.repositories import Repository
from poetry.repositories.exceptions import PackageNotFound
from poetry.repositories.legacy_repository import LegacyRepository


def test_pool_raises_package_not_found_when_no_package_is_found():
Expand All @@ -26,3 +27,26 @@ def test_pool_with_initial_repositories():

assert 1 == len(pool.repositories)
assert pool.default is None


def test_repository_no_repository():
pool = Pool()

with pytest.raises(ValueError):
pool.repository("foo")


def test_repository_from_normal_pool():
repo = LegacyRepository("foo", "https://foo.bar")
pool = Pool()
pool.add_repository(repo)

assert pool.repository("foo") is repo


def test_repository_from_optional_pool():
repo = LegacyRepository("foo", "https://foo.bar")
pool = Pool()
pool.add_repository(repo, optional=True)

assert pool.repository("foo") is repo

0 comments on commit 77a75fa

Please sign in to comment.