diff --git a/docs/cli.md b/docs/cli.md index a4c682a757d..fe9969a428a 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -758,7 +758,7 @@ You cannot use the name `pypi` as it is reserved for use by the default PyPI sou #### Options -* `--default`: Set this source as the [default]({{< relref "repositories#disabling-the-pypi-repository" >}}) (disable PyPI). +* `--default`: Set this source as the default (**Deprecated**, use TODO to disable PyPI). * `--secondary`: Set this source as a [secondary]({{< relref "repositories#install-dependencies-from-a-private-repository" >}}) source. {{% note %}} diff --git a/docs/repositories.md b/docs/repositories.md index 8d52a10994f..722060e7a23 100644 --- a/docs/repositories.md +++ b/docs/repositories.md @@ -120,7 +120,6 @@ This will generate the following configuration snippet in your [[tool.poetry.source]] name = "foo" url = "https://foo.bar/simple/" -default = false secondary = false ``` @@ -129,7 +128,8 @@ Any package source not marked as `secondary` will take precedence over [PyPI](ht {{% note %}} -If you prefer to disable [PyPI](https://pypi.org) completely, you may choose to set one of your package sources to be the [default](#default-package-source). +If you prefer to disable [PyPI](https://pypi.org) completely, TODO new command. +If you disable PyPI, you have to configure at least one other source. If you prefer to specify a package source for a specific dependency, see [Secondary Package Sources](#secondary-package-sources). @@ -144,20 +144,24 @@ you must declare **all** package sources to be [secondary](#secondary-package-so {{% /warning %}} -#### Default Package Source +#### Default Package Source PyPI -By default, Poetry configures [PyPI](https://pypi.org) as the default package source for your -project. You can alter this behaviour and exclusively look up packages only from the configured -package sources by adding a **single** source with `default = true`. +By default, Poetry configures [PyPI](https://pypi.org) as the default package source for +your project. If you configure additional sources, these are preferred to PyPI unless +they are configured as secondary. Nevertheless, packages are looked up on PyPI. + +You can alter this behaviour and exclusively look up packages only +from the configured package sources by TODO new command. +Adding a **single** source with `default = true` (**deprecated**) also disables PyPI. ```bash -poetry source add --default foo https://foo.bar/simple/ +poetry source TODO ``` {{% warning %}} -Configuring a custom package source as default, will effectively disable [PyPI](https://pypi.org) -as a package source for your project. +Configuring a custom package source as default (**deprecated**), +will effectively disable [PyPI](https://pypi.org) as a package source for your project. {{% /warning %}} diff --git a/src/poetry/console/commands/source/add.py b/src/poetry/console/commands/source/add.py index 2848ce8a3be..e910c006d7b 100644 --- a/src/poetry/console/commands/source/add.py +++ b/src/poetry/console/commands/source/add.py @@ -2,7 +2,6 @@ from cleo.helpers import argument from cleo.helpers import option -from cleo.io.null_io import NullIO from tomlkit.items import AoT from poetry.config.source import Source @@ -88,7 +87,7 @@ def handle(self) -> int: self.poetry._pool = RepositoryPool() try: Factory.configure_sources( - self.poetry, sources, self.poetry.config, NullIO() + self.poetry, sources, self.poetry.config, self._io ) self.poetry.pool.repository(name) except ValueError as e: diff --git a/src/poetry/factory.py b/src/poetry/factory.py index c20f224272c..1d96e44b93a 100644 --- a/src/poetry/factory.py +++ b/src/poetry/factory.py @@ -96,6 +96,7 @@ def create_poetry( config, io, disable_cache=disable_cache, + disable_pypi=poetry.local_config.get("disable-pypi", False), ) plugin_manager = PluginManager(Plugin.group, disable_plugins=disable_plugins) @@ -117,6 +118,8 @@ def configure_sources( config: Config, io: IO, disable_cache: bool = False, + *, + disable_pypi: bool = False, ) -> None: if disable_cache: logger.debug("Disabling source caches") @@ -135,13 +138,28 @@ def configure_sources( message += " and setting it as secondary" io.write_line(message) + if is_default: + # TODO: replace command + io.write_error_line( + "" + "The 'default' option is deprecated.\n" + " If you want to disable PyPI, TODO command\n" + " If you want to set a source as the first source to be searched" + " for packages, just make it the first source in your" + " pyproject.toml." + "" + ) poetry.pool.add_repository(repository, is_default, secondary=is_secondary) # Put PyPI last to prefer private repositories # unless we have no default source AND no primary sources # (default = false, secondary = false) - if poetry.pool.has_default(): + if poetry.pool.has_default() or disable_pypi: + if not sources: + raise RuntimeError( + 'If no sources are configured, "disable-pypi" must not be set!' + ) if io.is_debug(): io.write_line("Deactivating the PyPI repository") else: diff --git a/src/poetry/json/schemas/poetry.json b/src/poetry/json/schemas/poetry.json index 7532fd836b4..ab980d693bb 100644 --- a/src/poetry/json/schemas/poetry.json +++ b/src/poetry/json/schemas/poetry.json @@ -4,6 +4,10 @@ "type": "object", "required": [], "properties": { + "disable-pypi": { + "type": "boolean", + "description": "Whether PyPI is implicitly used to search for packages." + }, "source": { "type": "array", "description": "A set of additional repositories where packages can be found.", diff --git a/src/poetry/utils/source.py b/src/poetry/utils/source.py index dc8e1c8c92f..c3c843dd384 100644 --- a/src/poetry/utils/source.py +++ b/src/poetry/utils/source.py @@ -15,6 +15,9 @@ def source_to_table(source: Source) -> Table: source_table: Table = table() for key, value in source.to_dict().items(): + if key == "default" and not value: + # default is deprecated, so we don't add it if it is not set + continue source_table.add(key, value) source_table.add(nl()) return source_table diff --git a/tests/console/commands/source/test_add.py b/tests/console/commands/source/test_add.py index 7e43b9e2014..4c9ca0dde0a 100644 --- a/tests/console/commands/source/test_add.py +++ b/tests/console/commands/source/test_add.py @@ -33,6 +33,9 @@ def assert_source_added( == f"Adding source with name {source_added.name}." ) poetry.pyproject.reload() + for source_table in poetry.pyproject.poetry_config["source"]: + # "default" is deprecated and should only be written if set to True + assert "default" not in source_table or source_table["default"] is True sources = poetry.get_sources() assert sources == [source_existing, source_added] assert tester.status_code == 0 @@ -55,6 +58,7 @@ def test_source_add_default( poetry_with_source: Poetry, ): tester.execute(f"--default {source_default.name} {source_default.url}") + assert "deprecated" in tester.io.fetch_error() assert_source_added(tester, poetry_with_source, source_existing, source_default) @@ -95,6 +99,7 @@ def test_source_add_existing( tester.io.fetch_output().strip() == f"Source with name {source_existing.name} already exists. Updating." ) + assert "deprecated" in tester.io.fetch_error() poetry_with_source.pyproject.reload() sources = poetry_with_source.get_sources() diff --git a/tests/fixtures/with_no_explicit_source_pypi_disabled/pyproject.toml b/tests/fixtures/with_no_explicit_source_pypi_disabled/pyproject.toml new file mode 100644 index 00000000000..3d85a3fc5d3 --- /dev/null +++ b/tests/fixtures/with_no_explicit_source_pypi_disabled/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Your Name " +] +license = "MIT" +disable-pypi = true + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.6" + +[tool.poetry.dev-dependencies] diff --git a/tests/fixtures/with_non_default_source_pypi_disabled/pyproject.toml b/tests/fixtures/with_non_default_source_pypi_disabled/pyproject.toml new file mode 100644 index 00000000000..e98b79fd484 --- /dev/null +++ b/tests/fixtures/with_non_default_source_pypi_disabled/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Your Name " +] +license = "MIT" +disable-pypi = true + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.6" + +[tool.poetry.dev-dependencies] + +[[tool.poetry.source]] +name = "foo" +url = "https://foo.bar/simple/" diff --git a/tests/test_factory.py b/tests/test_factory.py index 7eafc85d210..098be5e3606 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -5,6 +5,7 @@ import pytest +from cleo.io.buffered_io import BufferedIO from deepdiff import DeepDiff from packaging.utils import canonicalize_name from poetry.core.constraints.version import parse_constraint @@ -207,10 +208,25 @@ def test_create_poetry_with_multi_constraints_dependency(): def test_poetry_with_default_source(with_simple_keyring: None): - poetry = Factory().create_poetry(fixtures_dir / "with_default_source") + io = BufferedIO() + poetry = Factory().create_poetry(fixtures_dir / "with_default_source", io=io) assert len(poetry.pool.repositories) == 1 + assert poetry.pool.has_default() + + assert poetry.pool.repositories[0].name == "foo" + assert isinstance(poetry.pool.repositories[0], LegacyRepository) + + assert "deprecated" in io.fetch_error() + + +def test_poetry_with_two_default_sources(with_simple_keyring: None): + with pytest.raises(ValueError) as e: + Factory().create_poetry(fixtures_dir / "with_two_default_sources") + + assert str(e.value) == "Only one repository can be the default." + def test_poetry_with_non_default_source(with_simple_keyring: None): poetry = Factory().create_poetry(fixtures_dir / "with_non_default_source") @@ -226,6 +242,19 @@ def test_poetry_with_non_default_source(with_simple_keyring: None): assert isinstance(poetry.pool.repositories[1], PyPiRepository) +def test_poetry_with_nondefault_source_pypi_disabled(with_simple_keyring: None): + poetry = Factory().create_poetry( + fixtures_dir / "with_non_default_source_pypi_disabled" + ) + + assert len(poetry.pool.repositories) == 1 + + assert not poetry.pool.has_default() + + assert poetry.pool.repositories[0].name == "foo" + assert isinstance(poetry.pool.repositories[0], LegacyRepository) + + def test_poetry_with_non_default_secondary_source(with_simple_keyring: None): poetry = Factory().create_poetry(fixtures_dir / "with_non_default_secondary_source") @@ -284,7 +313,7 @@ def test_poetry_with_non_default_multiple_sources(with_simple_keyring: None): assert isinstance(repository, PyPiRepository) -def test_poetry_with_no_default_source(): +def test_poetry_with_no_explicit_source(): poetry = Factory().create_poetry(fixtures_dir / "sample_project") assert len(poetry.pool.repositories) == 1 @@ -295,11 +324,9 @@ def test_poetry_with_no_default_source(): assert isinstance(poetry.pool.repositories[0], PyPiRepository) -def test_poetry_with_two_default_sources(with_simple_keyring: None): - with pytest.raises(ValueError) as e: - Factory().create_poetry(fixtures_dir / "with_two_default_sources") - - assert str(e.value) == "Only one repository can be the default." +def test_poetry_with_no_explicit_source_pypi_disabled(): + with pytest.raises(RuntimeError, match="no sources"): + Factory().create_poetry(fixtures_dir / "with_no_explicit_source_pypi_disabled") def test_validate():