From d600b820c2c3d0cf7e8bc0e84539f1eb6ba83dee Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 5 Apr 2022 13:44:24 +0200 Subject: [PATCH 1/8] Bumping version from 1.2.0-beta.1 to 1.2.0-beta.2.dev0 This is done to allow safe refactoring of export command plugin. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e34bdc30c03..d8c7ec3174f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "poetry" -version = "1.2.0-beta.1" +version = "1.2.0-beta.2.dev0" description = "Python dependency management and packaging made easy." authors = [ "Sébastien Eustace " From 5603138508b29faed4e7d376e1df1b17bbd018ec Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 5 Apr 2022 17:31:38 +0200 Subject: [PATCH 2/8] tests: load plugins when for command tester This change cleans up loading of plugins when testing commands. --- src/poetry/console/application.py | 6 ++- tests/conftest.py | 63 ------------------------------- tests/console/conftest.py | 62 +++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/poetry/console/application.py b/src/poetry/console/application.py index 070a8b89b16..97a8a2d4197 100644 --- a/src/poetry/console/application.py +++ b/src/poetry/console/application.py @@ -16,6 +16,7 @@ from cleo.exceptions import CleoException from cleo.formatters.style import Style from cleo.io.inputs.argv_input import ArgvInput +from cleo.io.null_io import NullIO from poetry.__version__ import __version__ from poetry.console.command_loader import CommandLoader @@ -306,10 +307,13 @@ def _configure_installer(self, command: InstallerCommand, io: IO) -> None: installer.use_executor(poetry.config.get("experimental.new-installer", False)) command.set_installer(installer) - def _load_plugins(self, io: IO) -> None: + def _load_plugins(self, io: IO = None) -> None: if self._plugins_loaded: return + if io is None: + io = NullIO() + self._disable_plugins = io.input.has_parameter_option("--no-plugins") if not self._disable_plugins: diff --git a/tests/conftest.py b/tests/conftest.py index 406f33ade33..f522f4e12b0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,7 +16,6 @@ import httpretty import pytest -from cleo.testers.command_tester import CommandTester from keyring.backend import KeyringBackend from poetry.config.config import Config as BaseConfig @@ -24,14 +23,12 @@ from poetry.factory import Factory from poetry.inspection.info import PackageInfo from poetry.inspection.info import PackageInfoError -from poetry.installation import Installer from poetry.layouts import layout from poetry.repositories import Pool from poetry.repositories import Repository from poetry.utils.env import EnvManager from poetry.utils.env import SystemEnv from poetry.utils.env import VirtualEnv -from tests.helpers import TestExecutor from tests.helpers import TestLocker from tests.helpers import TestRepository from tests.helpers import get_package @@ -42,12 +39,7 @@ if TYPE_CHECKING: from pytest_mock import MockerFixture - from poetry.installation.executor import Executor from poetry.poetry import Poetry - from poetry.utils.env import Env - from poetry.utils.env import MockEnv - from tests.helpers import PoetryTestApplication - from tests.types import CommandTesterFactory from tests.types import FixtureDirGetter from tests.types import ProjectFactory @@ -405,61 +397,6 @@ def _factory( return _factory -@pytest.fixture -def command_tester_factory( - app: PoetryTestApplication, env: MockEnv -) -> CommandTesterFactory: - def _tester( - command: str, - poetry: Poetry | None = None, - installer: Installer | None = None, - executor: Executor | None = None, - environment: Env | None = None, - ) -> CommandTester: - command = app.find(command) - tester = CommandTester(command) - - # Setting the formatter from the application - # TODO: Find a better way to do this in Cleo - app_io = app.create_io() - formatter = app_io.output.formatter - tester.io.output.set_formatter(formatter) - tester.io.error_output.set_formatter(formatter) - - if poetry: - app._poetry = poetry - - poetry = app.poetry - command._pool = poetry.pool - - if hasattr(command, "set_env"): - command.set_env(environment or env) - - if hasattr(command, "set_installer"): - installer = installer or Installer( - tester.io, - env, - poetry.package, - poetry.locker, - poetry.pool, - poetry.config, - executor=executor - or TestExecutor(env, poetry.pool, poetry.config, tester.io), - ) - installer.use_executor(True) - command.set_installer(installer) - - return tester - - return _tester - - -@pytest.fixture -def do_lock(command_tester_factory: CommandTesterFactory, poetry: Poetry) -> None: - command_tester_factory("lock").execute() - assert poetry.locker.lock.exists() - - @pytest.fixture def project_root() -> Path: return Path(__file__).parent.parent diff --git a/tests/console/conftest.py b/tests/console/conftest.py index c492629d5a2..697f7badf81 100644 --- a/tests/console/conftest.py +++ b/tests/console/conftest.py @@ -10,8 +10,10 @@ from cleo.io.null_io import NullIO from cleo.testers.application_tester import ApplicationTester +from cleo.testers.command_tester import CommandTester from poetry.factory import Factory +from poetry.installation import Installer from poetry.installation.noop_installer import NoopInstaller from poetry.repositories import Pool from poetry.utils.env import MockEnv @@ -24,10 +26,13 @@ if TYPE_CHECKING: from pytest_mock import MockerFixture + from poetry.installation.executor import Executor from poetry.poetry import Poetry from poetry.repositories import Repository + from poetry.utils.env import Env from tests.conftest import Config from tests.helpers import TestRepository + from tests.types import CommandTesterFactory @pytest.fixture() @@ -117,7 +122,7 @@ def poetry(repo: TestRepository, project_directory: str, config: Config) -> Poet @pytest.fixture def app(poetry: Poetry) -> PoetryTestApplication: app_ = PoetryTestApplication(poetry) - + app_._load_plugins() return app_ @@ -134,3 +139,58 @@ def new_installer_disabled(config: Config) -> None: @pytest.fixture() def executor(poetry: Poetry, config: Config, env: MockEnv) -> TestExecutor: return TestExecutor(env, poetry.pool, config, NullIO()) + + +@pytest.fixture +def command_tester_factory( + app: PoetryTestApplication, env: MockEnv +) -> CommandTesterFactory: + def _tester( + command: str, + poetry: Poetry | None = None, + installer: Installer | None = None, + executor: Executor | None = None, + environment: Env | None = None, + ) -> CommandTester: + command = app.find(command) + tester = CommandTester(command) + + # Setting the formatter from the application + # TODO: Find a better way to do this in Cleo + app_io = app.create_io() + formatter = app_io.output.formatter + tester.io.output.set_formatter(formatter) + tester.io.error_output.set_formatter(formatter) + + if poetry: + app._poetry = poetry + + poetry = app.poetry + command._pool = poetry.pool + + if hasattr(command, "set_env"): + command.set_env(environment or env) + + if hasattr(command, "set_installer"): + installer = installer or Installer( + tester.io, + env, + poetry.package, + poetry.locker, + poetry.pool, + poetry.config, + executor=executor + or TestExecutor(env, poetry.pool, poetry.config, tester.io), + ) + installer.use_executor(True) + command.set_installer(installer) + + return tester + + return _tester + + +@pytest.fixture +def do_lock(command_tester_factory: CommandTesterFactory, poetry: Poetry) -> None: + command_tester_factory("lock").execute() + assert poetry.locker.lock.exists() From 22968bb5394d0364695f92dec032d1a7063357f4 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 5 Apr 2022 17:48:27 +0200 Subject: [PATCH 3/8] export: add poetry-plugin-export --- docs/cli.md | 15 ++- poetry.lock | 157 +++++++++++++++----------- pyproject.toml | 1 + tests/console/commands/test_export.py | 17 +-- 4 files changed, 105 insertions(+), 85 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index 7675504a834..8d2b4901000 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -621,8 +621,13 @@ poetry export -f requirements.txt --output requirements.txt ``` {{% note %}} -Only the `requirements.txt` format is currently supported. -This command is also available as a pre-commit hook. See [pre-commit hooks](/docs/pre-commit-hooks#poetry-export) for more information. +This command is provided by the [Export Poetry Plugin](https://github.com/python-poetry/poetry-plugin-export) +and is also available as a pre-commit hook. See [pre-commit hooks](/docs/pre-commit-hooks#poetry-export) for more information. +{{% /note %}} + +{{% note %}} +Unlike the `install` command, this command only includes the project's dependencies defined in the implicit `default` +group defined in `tool.poetry.dependencies` when used without specifying any options. {{% /note %}} ### Options @@ -631,8 +636,12 @@ This command is also available as a pre-commit hook. See [pre-commit hooks](/doc Currently, only `requirements.txt` is supported. * `--output (-o)`: The name of the output file. If omitted, print to standard output. -* `--dev`: Include development dependencies. +* `--dev`: Include development dependencies. (**Deprecated**) * `--extras (-E)`: Extra sets of dependencies to include. +* `--without`: The dependency groups to ignore. +* `--with`: The optional dependency groups to include. +* `--only`: The only dependency groups to include. +* `--default`: Only include the default dependencies. (**Deprecated**) * `--without-hashes`: Exclude hashes from the exported file. * `--without-urls`: Exclude source repository urls from the exported file. * `--with-credentials`: Include credentials for extra indices. diff --git a/poetry.lock b/poetry.lock index 3c0a674ebfa..2bef2fb2da8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -129,7 +129,7 @@ python-versions = ">=3.6,<4.0" [[package]] name = "cryptography" -version = "36.0.1" +version = "36.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = false @@ -148,14 +148,14 @@ test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pr [[package]] name = "deepdiff" -version = "5.7.0" +version = "5.8.0" description = "Deep Difference and Search of any Python object/data." category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -ordered-set = "4.0.2" +ordered-set = ">=4.1.0,<4.2.0" [package.extras] cli = ["click (==8.0.3)", "pyyaml (==5.4.1)", "toml (==0.10.2)", "clevercsv (==0.7.1)"] @@ -224,7 +224,7 @@ python-versions = ">=3" [[package]] name = "identify" -version = "2.4.11" +version = "2.4.12" description = "File identification library for Python" category = "dev" optional = false @@ -243,7 +243,7 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "4.11.2" +version = "4.11.3" description = "Read metadata from Python packages" category = "main" optional = false @@ -268,14 +268,14 @@ python-versions = "*" [[package]] name = "jeepney" -version = "0.7.1" +version = "0.8.0" description = "Low-level, pure Python DBus protocol wrapper." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] -test = ["pytest", "pytest-trio", "pytest-asyncio", "testpath", "trio", "async-timeout"] +test = ["pytest", "pytest-trio", "pytest-asyncio (>=0.17)", "testpath", "trio", "async-timeout"] trio = ["trio", "async-generator"] [[package]] @@ -322,11 +322,14 @@ python-versions = "*" [[package]] name = "ordered-set" -version = "4.0.2" -description = "A set that remembers its order, and allows looking up its items by their index in that order." +version = "4.1.0" +description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" + +[package.extras] +dev = ["pytest", "black", "mypy"] [[package]] name = "packaging" @@ -399,13 +402,24 @@ python-versions = ">=3.7,<4.0" [package.dependencies] importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} +[[package]] +name = "poetry-plugin-export" +version = "1.0.1" +description = "Poetry plugin to export the dependencies to various formats" +category = "main" +optional = false +python-versions = ">=3.7,<4.0" + +[package.dependencies] +poetry = ">=1.2.0b1dev0,<2.0.0" + [[package]] name = "pre-commit" -version = "2.17.0" +version = "2.18.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.7" [package.dependencies] cfgv = ">=2.0.0" @@ -450,14 +464,14 @@ python-versions = "*" [[package]] name = "pyparsing" -version = "3.0.7" -description = "Python parsing module" +version = "3.0.8" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.8" [package.extras] -diagrams = ["jinja2", "railroad-diagrams"] +diagrams = ["railroad-diagrams", "jinja2"] [[package]] name = "pytest" @@ -615,7 +629,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tomlkit" -version = "0.10.0" +version = "0.10.1" description = "Style preserving TOML library" category = "main" optional = false @@ -623,7 +637,7 @@ python-versions = ">=3.6,<4.0" [[package]] name = "tox" -version = "3.24.5" +version = "3.25.0" description = "tox is a generic virtualenv management and test command line tool" category = "dev" optional = false @@ -654,20 +668,20 @@ python-versions = ">=3.6" [[package]] name = "urllib3" -version = "1.26.8" +version = "1.26.9" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] -brotli = ["brotlipy (>=0.6.0)"] +brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.13.2" +version = "20.14.1" description = "Virtual Python Environment builder" category = "main" optional = false @@ -694,20 +708,20 @@ python-versions = "*" [[package]] name = "zipp" -version = "3.7.0" +version = "3.8.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "e1979eb8cccef74af26d7fc01bb77d64515a2633fdb28bdbc2837da52fb24b98" +content-hash = "a6debe7eeee8b7d0715ed531cb2ec206284590eb4825749039c0e5006e8c8fcc" [metadata.files] atomicwrites = [ @@ -846,30 +860,30 @@ crashtest = [ {file = "crashtest-0.3.1.tar.gz", hash = "sha256:42ca7b6ce88b6c7433e2ce47ea884e91ec93104a4b754998be498a8e6c3d37dd"}, ] cryptography = [ - {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:73bc2d3f2444bcfeac67dd130ff2ea598ea5f20b40e36d19821b4df8c9c5037b"}, - {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:2d87cdcb378d3cfed944dac30596da1968f88fb96d7fc34fdae30a99054b2e31"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74d6c7e80609c0f4c2434b97b80c7f8fdfaa072ca4baab7e239a15d6d70ed73a"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:6c0c021f35b421ebf5976abf2daacc47e235f8b6082d3396a2fe3ccd537ab173"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59a9d55027a8b88fd9fd2826c4392bd487d74bf628bb9d39beecc62a644c12"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a817b961b46894c5ca8a66b599c745b9a3d9f822725221f0e0fe49dc043a3a3"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:94ae132f0e40fe48f310bba63f477f14a43116f05ddb69d6fa31e93f05848ae2"}, - {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7be0eec337359c155df191d6ae00a5e8bbb63933883f4f5dffc439dac5348c3f"}, - {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e0344c14c9cb89e76eb6a060e67980c9e35b3f36691e15e1b7a9e58a0a6c6dc3"}, - {file = "cryptography-36.0.1-cp36-abi3-win32.whl", hash = "sha256:4caa4b893d8fad33cf1964d3e51842cd78ba87401ab1d2e44556826df849a8ca"}, - {file = "cryptography-36.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:391432971a66cfaf94b21c24ab465a4cc3e8bf4a939c1ca5c3e3a6e0abebdbcf"}, - {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bb5829d027ff82aa872d76158919045a7c1e91fbf241aec32cb07956e9ebd3c9"}, - {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc15b1c22e55c4d5566e3ca4db8689470a0ca2babef8e3a9ee057a8b82ce4b1"}, - {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:596f3cd67e1b950bc372c33f1a28a0692080625592ea6392987dba7f09f17a94"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:30ee1eb3ebe1644d1c3f183d115a8c04e4e603ed6ce8e394ed39eea4a98469ac"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec63da4e7e4a5f924b90af42eddf20b698a70e58d86a72d943857c4c6045b3ee"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca238ceb7ba0bdf6ce88c1b74a87bffcee5afbfa1e41e173b1ceb095b39add46"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:ca28641954f767f9822c24e927ad894d45d5a1e501767599647259cbf030b903"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:39bdf8e70eee6b1c7b289ec6e5d84d49a6bfa11f8b8646b5b3dfe41219153316"}, - {file = "cryptography-36.0.1.tar.gz", hash = "sha256:53e5c1dc3d7a953de055d77bef2ff607ceef7a2aac0353b5d630ab67f7423638"}, + {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6"}, + {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:4881d09298cd0b669bb15b9cfe6166f16fc1277b4ed0d04a22f3d6430cb30f1d"}, + {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea634401ca02367c1567f012317502ef3437522e2fc44a3ea1844de028fa4b84"}, + {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7be666cc4599b415f320839e36367b273db8501127b38316f3b9f22f17a0b815"}, + {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8241cac0aae90b82d6b5c443b853723bcc66963970c67e56e71a2609dc4b5eaf"}, + {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2d54e787a884ffc6e187262823b6feb06c338084bbe80d45166a1cb1c6c5bf"}, + {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:c2c5250ff0d36fd58550252f54915776940e4e866f38f3a7866d92b32a654b86"}, + {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ec6597aa85ce03f3e507566b8bcdf9da2227ec86c4266bd5e6ab4d9e0cc8dab2"}, + {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ca9f686517ec2c4a4ce930207f75c00bf03d94e5063cbc00a1dc42531511b7eb"}, + {file = "cryptography-36.0.2-cp36-abi3-win32.whl", hash = "sha256:f64b232348ee82f13aac22856515ce0195837f6968aeaa94a3d0353ea2ec06a6"}, + {file = "cryptography-36.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:53e0285b49fd0ab6e604f4c5d9c5ddd98de77018542e88366923f152dbeb3c29"}, + {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:32db5cc49c73f39aac27574522cecd0a4bb7384e71198bc65a0d23f901e89bb7"}, + {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b3d199647468d410994dbeb8cec5816fb74feb9368aedf300af709ef507e3e"}, + {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:da73d095f8590ad437cd5e9faf6628a218aa7c387e1fdf67b888b47ba56a17f0"}, + {file = "cryptography-36.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0a3bf09bb0b7a2c93ce7b98cb107e9170a90c51a0162a20af1c61c765b90e60b"}, + {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8897b7b7ec077c819187a123174b645eb680c13df68354ed99f9b40a50898f77"}, + {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82740818f2f240a5da8dfb8943b360e4f24022b093207160c77cadade47d7c85"}, + {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1f64a62b3b75e4005df19d3b5235abd43fa6358d5516cfc43d87aeba8d08dd51"}, + {file = "cryptography-36.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e167b6b710c7f7bc54e67ef593f8731e1f45aa35f8a8a7b72d6e42ec76afd4b3"}, + {file = "cryptography-36.0.2.tar.gz", hash = "sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9"}, ] deepdiff = [ - {file = "deepdiff-5.7.0-py3-none-any.whl", hash = "sha256:1ffb38c3b5d9174eb2df95850c93aee55ec00e19396925036a2e680f725079e0"}, - {file = "deepdiff-5.7.0.tar.gz", hash = "sha256:838766484e323dcd9dec6955926a893a83767dc3f3f94542773e6aa096efe5d4"}, + {file = "deepdiff-5.8.0-py3-none-any.whl", hash = "sha256:6b71714a6a5fb4cd6ab74c97f53303029118f96128082422342a9129a5f25c8f"}, + {file = "deepdiff-5.8.0.tar.gz", hash = "sha256:7e641c0cd6429c9e1b64a07b8f7713382a5626afe18c72bcafa8a4343c05c701"}, ] distlib = [ {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, @@ -894,24 +908,24 @@ httpretty = [ {file = "httpretty-1.1.4.tar.gz", hash = "sha256:20de0e5dd5a18292d36d928cc3d6e52f8b2ac73daec40d41eb62dee154933b68"}, ] identify = [ - {file = "identify-2.4.11-py2.py3-none-any.whl", hash = "sha256:fd906823ed1db23c7a48f9b176a1d71cb8abede1e21ebe614bac7bdd688d9213"}, - {file = "identify-2.4.11.tar.gz", hash = "sha256:2986942d3974c8f2e5019a190523b0b0e2a07cb8e89bf236727fb4b26f27f8fd"}, + {file = "identify-2.4.12-py2.py3-none-any.whl", hash = "sha256:5f06b14366bd1facb88b00540a1de05b69b310cbc2654db3c7e07fa3a4339323"}, + {file = "identify-2.4.12.tar.gz", hash = "sha256:3f3244a559290e7d3deb9e9adc7b33594c1bc85a9dd82e0f1be519bf12a1ec17"}, ] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.11.2-py3-none-any.whl", hash = "sha256:d16e8c1deb60de41b8e8ed21c1a7b947b0bc62fab7e1d470bcdf331cea2e6735"}, - {file = "importlib_metadata-4.11.2.tar.gz", hash = "sha256:b36ffa925fe3139b2f6ff11d6925ffd4fa7bc47870165e3ac260ac7b4f91e6ac"}, + {file = "importlib_metadata-4.11.3-py3-none-any.whl", hash = "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6"}, + {file = "importlib_metadata-4.11.3.tar.gz", hash = "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] jeepney = [ - {file = "jeepney-0.7.1-py3-none-any.whl", hash = "sha256:1b5a0ea5c0e7b166b2f5895b91a08c14de8915afda4407fb5022a195224958ac"}, - {file = "jeepney-0.7.1.tar.gz", hash = "sha256:fa9e232dfa0c498bd0b8a3a73b8d8a31978304dcef0515adc859d4e096f96f4f"}, + {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, + {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, ] keyring = [ {file = "keyring-23.5.0-py3-none-any.whl", hash = "sha256:b0d28928ac3ec8e42ef4cc227822647a19f1d544f21f96457965dc01cf555261"}, @@ -962,7 +976,8 @@ nodeenv = [ {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"}, ] ordered-set = [ - {file = "ordered-set-4.0.2.tar.gz", hash = "sha256:ba93b2df055bca202116ec44b9bead3df33ea63a7d5827ff8e16738b97f33a95"}, + {file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"}, + {file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"}, ] packaging = [ {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, @@ -988,9 +1003,13 @@ poetry-core = [ {file = "poetry-core-1.1.0a7.tar.gz", hash = "sha256:4622ae680842ac9b1b9c3b0e8dc467c2e291d1a5c434b6bd413907a2e5571d92"}, {file = "poetry_core-1.1.0a7-py3-none-any.whl", hash = "sha256:724e8b5368f270461e622396305d0c2e760ec9d4c14d072e6b944da9384c67de"}, ] +poetry-plugin-export = [ + {file = "poetry-plugin-export-1.0.1.tar.gz", hash = "sha256:e2a87bef5b05cb37eee67fb25d9e4f8e8d538ab64a7ec582ab207366722b9dbe"}, + {file = "poetry_plugin_export-1.0.1-py3-none-any.whl", hash = "sha256:83902686faa7820be7e445978562c852dda94568bf63cc48ec47f55b250f1f40"}, +] pre-commit = [ - {file = "pre_commit-2.17.0-py2.py3-none-any.whl", hash = "sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616"}, - {file = "pre_commit-2.17.0.tar.gz", hash = "sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a"}, + {file = "pre_commit-2.18.1-py2.py3-none-any.whl", hash = "sha256:02226e69564ebca1a070bd1f046af866aa1c318dbc430027c50ab832ed2b73f2"}, + {file = "pre_commit-2.18.1.tar.gz", hash = "sha256:5d445ee1fa8738d506881c5d84f83c62bb5be6b2838e32207433647e8e5ebe10"}, ] ptyprocess = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, @@ -1009,8 +1028,8 @@ pylev = [ {file = "pylev-1.4.0.tar.gz", hash = "sha256:9e77e941042ad3a4cc305dcdf2b2dec1aec2fbe3dd9015d2698ad02b173006d1"}, ] pyparsing = [ - {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, - {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, + {file = "pyparsing-3.0.8-py3-none-any.whl", hash = "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"}, + {file = "pyparsing-3.0.8.tar.gz", hash = "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954"}, ] pytest = [ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, @@ -1094,30 +1113,30 @@ toml = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tomlkit = [ - {file = "tomlkit-0.10.0-py3-none-any.whl", hash = "sha256:cac4aeaff42f18fef6e07831c2c2689a51df76cf2ede07a6a4fa5fcb83558870"}, - {file = "tomlkit-0.10.0.tar.gz", hash = "sha256:d99946c6aed3387c98b89d91fb9edff8f901bf9255901081266a84fb5604adcd"}, + {file = "tomlkit-0.10.1-py3-none-any.whl", hash = "sha256:3eba517439dcb2f84cf39f4f85fd2c3398309823a3c75ac3e73003638daf7915"}, + {file = "tomlkit-0.10.1.tar.gz", hash = "sha256:3c517894eadef53e9072d343d37e4427b8f0b6200a70b7c9a19b2ebd1f53b951"}, ] tox = [ - {file = "tox-3.24.5-py2.py3-none-any.whl", hash = "sha256:be3362472a33094bce26727f5f771ca0facf6dafa217f65875314e9a6600c95c"}, - {file = "tox-3.24.5.tar.gz", hash = "sha256:67e0e32c90e278251fea45b696d0fef3879089ccbe979b0c556d35d5a70e2993"}, + {file = "tox-3.25.0-py2.py3-none-any.whl", hash = "sha256:0805727eb4d6b049de304977dfc9ce315a1938e6619c3ab9f38682bb04662a5a"}, + {file = "tox-3.25.0.tar.gz", hash = "sha256:37888f3092aa4e9f835fc8cc6dadbaaa0782651c41ef359e3a5743fcb0308160"}, ] typing-extensions = [ {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, ] urllib3 = [ - {file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"}, - {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"}, + {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, + {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, ] virtualenv = [ - {file = "virtualenv-20.13.2-py2.py3-none-any.whl", hash = "sha256:e7b34c9474e6476ee208c43a4d9ac1510b041c68347eabfe9a9ea0c86aa0a46b"}, - {file = "virtualenv-20.13.2.tar.gz", hash = "sha256:01f5f80744d24a3743ce61858123488e91cb2dd1d3bdf92adaf1bba39ffdedf0"}, + {file = "virtualenv-20.14.1-py2.py3-none-any.whl", hash = "sha256:e617f16e25b42eb4f6e74096b9c9e37713cf10bf30168fb4a739f3fa8f898a3a"}, + {file = "virtualenv-20.14.1.tar.gz", hash = "sha256:ef589a79795589aada0c1c5b319486797c03b67ac3984c48c669c0e4f50df3a5"}, ] webencodings = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] zipp = [ - {file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"}, - {file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"}, + {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, + {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, ] diff --git a/pyproject.toml b/pyproject.toml index d8c7ec3174f..cb5199e5d6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ generate-setup-file = false python = "^3.7" poetry-core = "^1.1.0a7" +poetry-plugin-export = "^1.0" cachecontrol = { version = "^0.12.9", extras = ["filecache"] } cachy = "^0.3.0" cleo = "^1.0.0a4" diff --git a/tests/console/commands/test_export.py b/tests/console/commands/test_export.py index 6e704b5338a..d34d899aa11 100644 --- a/tests/console/commands/test_export.py +++ b/tests/console/commands/test_export.py @@ -1,12 +1,12 @@ from __future__ import annotations from typing import TYPE_CHECKING -from unittest.mock import ANY from unittest.mock import Mock import pytest -from poetry.console.commands.export import Exporter +from poetry_plugin_export.exporter import Exporter + from tests.helpers import get_package @@ -155,15 +155,6 @@ def test_export_with_urls( the Exporter test. """ mock_export = Mock() - monkeypatch.setattr(Exporter, "export", mock_export) + monkeypatch.setattr(Exporter, "with_urls", mock_export) tester.execute("--without-urls") - mock_export.assert_called_once_with( - ANY, - ANY, - ANY, - dev=False, - extras=[], - with_credentials=False, - with_hashes=True, - with_urls=False, - ) + mock_export.assert_called_once_with(False) From e733307c9db1e7b5bec96ce773c9aa1f1e776ac7 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 5 Apr 2022 17:55:35 +0200 Subject: [PATCH 4/8] export: remove in-tree export logic --- src/poetry/console/application.py | 1 - src/poetry/console/commands/export.py | 80 - src/poetry/utils/exporter.py | 182 --- tests/console/commands/test_export.py | 160 -- tests/utils/test_exporter.py | 2168 ------------------------- 5 files changed, 2591 deletions(-) delete mode 100644 src/poetry/console/commands/export.py delete mode 100644 src/poetry/utils/exporter.py delete mode 100644 tests/console/commands/test_export.py delete mode 100644 tests/utils/test_exporter.py diff --git a/src/poetry/console/application.py b/src/poetry/console/application.py index 97a8a2d4197..44a0efd7b0d 100644 --- a/src/poetry/console/application.py +++ b/src/poetry/console/application.py @@ -53,7 +53,6 @@ def _load() -> type[Command]: "build", "check", "config", - "export", "init", "install", "lock", diff --git a/src/poetry/console/commands/export.py b/src/poetry/console/commands/export.py deleted file mode 100644 index c4cdb0f01ac..00000000000 --- a/src/poetry/console/commands/export.py +++ /dev/null @@ -1,80 +0,0 @@ -from __future__ import annotations - -from cleo.helpers import option - -from poetry.console.commands.command import Command -from poetry.utils.exporter import Exporter - - -class ExportCommand(Command): - - name = "export" - description = "Exports the lock file to alternative formats." - - options = [ - option( - "format", - "f", - "Format to export to. Currently, only requirements.txt is supported.", - flag=False, - default=Exporter.FORMAT_REQUIREMENTS_TXT, - ), - option("output", "o", "The name of the output file.", flag=False), - option("without-hashes", None, "Exclude hashes from the exported file."), - option( - "without-urls", - None, - "Exclude source repository urls from the exported file.", - ), - option("dev", None, "Include development dependencies."), - option( - "extras", - "E", - "Extra sets of dependencies to include.", - flag=False, - multiple=True, - ), - option("with-credentials", None, "Include credentials for extra indices."), - ] - - def handle(self) -> None: - fmt = self.option("format") - - if fmt not in Exporter.ACCEPTED_FORMATS: - raise ValueError(f"Invalid export format: {fmt}") - - output = self.option("output") - - locker = self.poetry.locker - if not locker.is_locked(): - self.line_error("The lock file does not exist. Locking.") - options = [] - if self.io.is_debug(): - options.append("-vvv") - elif self.io.is_very_verbose(): - options.append("-vv") - elif self.io.is_verbose(): - options.append("-v") - - self.call("lock", " ".join(options)) - - if not locker.is_fresh(): - self.line_error( - "" - "Warning: poetry.lock is not consistent with pyproject.toml. " - "You may be getting improper dependencies. " - "Run `poetry lock [--no-update]` to fix it." - "" - ) - - exporter = Exporter(self.poetry) - exporter.export( - fmt, - self.poetry.file.parent, - output or self.io, - with_hashes=not self.option("without-hashes"), - dev=self.option("dev"), - extras=self.option("extras"), - with_credentials=self.option("with-credentials"), - with_urls=not self.option("without-urls"), - ) diff --git a/src/poetry/utils/exporter.py b/src/poetry/utils/exporter.py deleted file mode 100644 index c219a8b8522..00000000000 --- a/src/poetry/utils/exporter.py +++ /dev/null @@ -1,182 +0,0 @@ -from __future__ import annotations - -import urllib.parse - -from typing import TYPE_CHECKING -from typing import Sequence - -from poetry.core.packages.utils.utils import path_to_url - -from poetry.utils._compat import decode - - -if TYPE_CHECKING: - from pathlib import Path - - from cleo.io.io import IO - - from poetry.poetry import Poetry - - -class Exporter: - """ - Exporter class to export a lock file to alternative formats. - """ - - FORMAT_REQUIREMENTS_TXT = "requirements.txt" - #: The names of the supported export formats. - ACCEPTED_FORMATS = (FORMAT_REQUIREMENTS_TXT,) - ALLOWED_HASH_ALGORITHMS = ("sha256", "sha384", "sha512") - - def __init__(self, poetry: Poetry) -> None: - self._poetry = poetry - - def export( - self, - fmt: str, - cwd: Path, - output: IO | str, - with_hashes: bool = True, - dev: bool = False, - extras: bool | Sequence[str] | None = None, - with_credentials: bool = False, - with_urls: bool = True, - ) -> None: - if fmt not in self.ACCEPTED_FORMATS: - raise ValueError(f"Invalid export format: {fmt}") - - getattr(self, "_export_" + fmt.replace(".", "_"))( - cwd, - output, - with_hashes=with_hashes, - dev=dev, - extras=extras, - with_credentials=with_credentials, - with_urls=with_urls, - ) - - def _export_requirements_txt( - self, - cwd: Path, - output: IO | str, - with_hashes: bool = True, - dev: bool = False, - extras: bool | Sequence[str] | None = None, - with_credentials: bool = False, - with_urls: bool = True, - ) -> None: - indexes = set() - content = "" - dependency_lines = set() - - # Get project dependencies. - root_package = ( - self._poetry.package.clone() - if dev - else self._poetry.package.with_dependency_groups(["default"], only=True) - ) - - for dependency_package in self._poetry.locker.get_project_dependency_packages( - project_requires=root_package.all_requires, - project_python_marker=root_package.python_marker, - dev=dev, - extras=extras, - ): - line = "" - dependency = dependency_package.dependency - package = dependency_package.package - - if package.develop: - line += "-e " - - requirement = dependency.to_pep_508(with_extras=False) - is_direct_local_reference = ( - dependency.is_file() or dependency.is_directory() - ) - is_direct_remote_reference = dependency.is_vcs() or dependency.is_url() - - if is_direct_remote_reference: - line = requirement - elif is_direct_local_reference: - dependency_uri = path_to_url(dependency.source_url) - line = f"{dependency.name} @ {dependency_uri}" - else: - line = f"{package.name}=={package.version}" - - if not is_direct_remote_reference and ";" in requirement: - markers = requirement.split(";", 1)[1].strip() - if markers: - line += f" ; {markers}" - - if ( - not is_direct_remote_reference - and not is_direct_local_reference - and package.source_url - ): - indexes.add(package.source_url) - - if package.files and with_hashes: - hashes = [] - for f in package.files: - h = f["hash"] - algorithm = "sha256" - if ":" in h: - algorithm, h = h.split(":") - - if algorithm not in self.ALLOWED_HASH_ALGORITHMS: - continue - - hashes.append(f"{algorithm}:{h}") - - if hashes: - sep = " \\\n" - line += sep + sep.join(f" --hash={h}" for h in hashes) - dependency_lines.add(line) - - content += "\n".join(sorted(dependency_lines)) - content += "\n" - - if indexes and with_urls: - # If we have extra indexes, we add them to the beginning of the output - indexes_header = "" - for index in sorted(indexes): - repositories = [ - r - for r in self._poetry.pool.repositories - if r.url == index.rstrip("/") - ] - if not repositories: - continue - repository = repositories[0] - if ( - self._poetry.pool.has_default() - and repository is self._poetry.pool.repositories[0] - ): - url = ( - repository.authenticated_url - if with_credentials - else repository.url - ) - indexes_header = f"--index-url {url}\n" - continue - - url = ( - repository.authenticated_url if with_credentials else repository.url - ) - parsed_url = urllib.parse.urlsplit(url) - if parsed_url.scheme == "http": - indexes_header += f"--trusted-host {parsed_url.netloc}\n" - indexes_header += f"--extra-index-url {url}\n" - - content = indexes_header + "\n" + content - - self._output(content, cwd, output) - - def _output(self, content: str, cwd: Path, output: IO | str) -> None: - decoded = decode(content) - try: - output.write(decoded) - except AttributeError: - filepath = cwd / output - with filepath.open("w", encoding="utf-8") as f: - f.write(decoded) diff --git a/tests/console/commands/test_export.py b/tests/console/commands/test_export.py deleted file mode 100644 index d34d899aa11..00000000000 --- a/tests/console/commands/test_export.py +++ /dev/null @@ -1,160 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING -from unittest.mock import Mock - -import pytest - -from poetry_plugin_export.exporter import Exporter - -from tests.helpers import get_package - - -if TYPE_CHECKING: - from _pytest.monkeypatch import MonkeyPatch - from cleo.testers.command_tester import CommandTester - - from poetry.poetry import Poetry - from tests.helpers import TestRepository - from tests.types import CommandTesterFactory - from tests.types import ProjectFactory - - -PYPROJECT_CONTENT = """\ -[tool.poetry] -name = "simple-project" -version = "1.2.3" -description = "Some description." -authors = [ - "Sébastien Eustace " -] -license = "MIT" - -readme = "README.rst" - -homepage = "https://python-poetry.org" -repository = "https://github.com/python-poetry/poetry" -documentation = "https://python-poetry.org/docs" - -keywords = ["packaging", "dependency", "poetry"] - -classifiers = [ - "Topic :: Software Development :: Build Tools", - "Topic :: Software Development :: Libraries :: Python Modules" -] - -# Requirements -[tool.poetry.dependencies] -python = "~2.7 || ^3.4" -foo = "^1.0" -bar = { version = "^1.1", optional = true } - -[tool.poetry.extras] -feature_bar = ["bar"] -""" - - -@pytest.fixture(autouse=True) -def setup(repo: TestRepository) -> None: - repo.add_package(get_package("foo", "1.0.0")) - repo.add_package(get_package("bar", "1.1.0")) - - -@pytest.fixture -def poetry(project_factory: ProjectFactory) -> Poetry: - return project_factory(name="export", pyproject_content=PYPROJECT_CONTENT) - - -@pytest.fixture -def tester( - command_tester_factory: CommandTesterFactory, poetry: Poetry -) -> CommandTester: - return command_tester_factory("export", poetry=poetry) - - -def _export_requirements(tester: CommandTester, poetry: Poetry) -> None: - tester.execute("--format requirements.txt --output requirements.txt") - - requirements = poetry.file.parent / "requirements.txt" - assert requirements.exists() - - with requirements.open(encoding="utf-8") as f: - content = f.read() - - assert poetry.locker.lock.exists() - - expected = """\ -foo==1.0.0 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.4" and python_version < "4.0" -""" - - assert content == expected - - -def test_export_exports_requirements_txt_file_locks_if_no_lock_file( - tester: CommandTester, poetry: Poetry -): - assert not poetry.locker.lock.exists() - _export_requirements(tester, poetry) - assert "The lock file does not exist. Locking." in tester.io.fetch_error() - - -def test_export_exports_requirements_txt_uses_lock_file( - tester: CommandTester, poetry: Poetry, do_lock: None -): - _export_requirements(tester, poetry) - assert "The lock file does not exist. Locking." not in tester.io.fetch_error() - - -def test_export_fails_on_invalid_format(tester: CommandTester, do_lock: None): - with pytest.raises(ValueError): - tester.execute("--format invalid") - - -def test_export_prints_to_stdout_by_default(tester: CommandTester, do_lock: None): - tester.execute("--format requirements.txt") - expected = """\ -foo==1.0.0 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.4" and python_version < "4.0" -""" - assert tester.io.fetch_output() == expected - - -def test_export_uses_requirements_txt_format_by_default( - tester: CommandTester, do_lock: None -): - tester.execute() - expected = """\ -foo==1.0.0 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.4" and python_version < "4.0" -""" - assert tester.io.fetch_output() == expected - - -def test_export_includes_extras_by_flag(tester: CommandTester, do_lock: None): - tester.execute("--format requirements.txt --extras feature_bar") - expected = """\ -bar==1.1.0 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.4" and python_version < "4.0" -foo==1.0.0 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.4" and python_version < "4.0" -""" - assert tester.io.fetch_output() == expected - - -def test_export_with_urls( - monkeypatch: MonkeyPatch, tester: CommandTester, poetry: Poetry -): - """ - We are just validating that the option gets passed. The option itself is tested in - the Exporter test. - """ - mock_export = Mock() - monkeypatch.setattr(Exporter, "with_urls", mock_export) - tester.execute("--without-urls") - mock_export.assert_called_once_with(False) diff --git a/tests/utils/test_exporter.py b/tests/utils/test_exporter.py deleted file mode 100644 index 1c1cd3f97d2..00000000000 --- a/tests/utils/test_exporter.py +++ /dev/null @@ -1,2168 +0,0 @@ -from __future__ import annotations - -import sys - -from pathlib import Path -from typing import TYPE_CHECKING -from typing import Any -from typing import Iterator - -import pytest - -from poetry.core.packages.dependency import Dependency -from poetry.core.toml.file import TOMLFile - -from poetry.factory import Factory -from poetry.packages import Locker as BaseLocker -from poetry.repositories.legacy_repository import LegacyRepository -from poetry.utils.exporter import Exporter - - -if TYPE_CHECKING: - from _pytest.capture import CaptureFixture - from pytest_mock import MockerFixture - - from poetry.poetry import Poetry - from tests.conftest import Config - from tests.types import FixtureDirGetter - - -class Locker(BaseLocker): - def __init__(self) -> None: - self._lock = TOMLFile(Path.cwd().joinpath("poetry.lock")) - self._locked = True - self._content_hash = self._get_content_hash() - - def locked(self, is_locked: bool = True) -> Locker: - self._locked = is_locked - - return self - - def mock_lock_data(self, data: dict[str, Any]): - self._lock_data = data - - def is_locked(self) -> bool: - return self._locked - - def is_fresh(self) -> bool: - return True - - def _get_content_hash(self) -> str: - return "123456789" - - -@pytest.fixture -def working_directory() -> Path: - return Path(__file__).parent.parent.parent - - -@pytest.fixture(autouse=True) -def mock_path_cwd( - mocker: MockerFixture, working_directory: Path -) -> Iterator[MockerFixture]: - yield mocker.patch("pathlib.Path.cwd", return_value=working_directory) - - -@pytest.fixture() -def locker() -> Locker: - return Locker() - - -@pytest.fixture -def poetry(fixture_dir: FixtureDirGetter, locker: Locker) -> Poetry: - p = Factory().create_poetry(fixture_dir("sample_project")) - p._locker = locker - - return p - - -def set_package_requires(poetry: Poetry, skip: set[str] | None = None) -> None: - skip = skip or set() - packages = poetry.locker.locked_repository(with_dev_reqs=True).packages - package = poetry.package.with_dependency_groups([], only=True) - for pkg in packages: - if pkg.name not in skip: - package.add_dependency(pkg.to_dependency()) - - poetry._package = package - - -def test_exporter_can_export_requirements_txt_with_standard_packages( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "main", - "optional": False, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": [], "bar": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_with_standard_packages_and_markers( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "marker": "python_version < '3.7'", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "main", - "optional": False, - "python-versions": "*", - "marker": "extra =='foo'", - }, - { - "name": "baz", - "version": "7.8.9", - "category": "main", - "optional": False, - "python-versions": "*", - "marker": "sys_platform == 'win32'", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": [], "bar": [], "baz": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -baz==7.8.9 ;\ - python_version >= "2.7" and python_version < "2.8" and sys_platform == "win32" or\ - python_version >= "3.6" and python_version < "4.0" and sys_platform == "win32" -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "3.7" -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_poetry(tmp_dir: str, poetry: Poetry): - """Regression test for #3254""" - - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "poetry", - "version": "1.1.4", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": {"keyring": "*"}, - }, - { - "name": "junit-xml", - "version": "1.9", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": {"six": "*"}, - }, - { - "name": "keyring", - "version": "21.8.0", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": { - "SecretStorage": { - "version": "*", - "markers": "sys_platform == 'linux'", - } - }, - }, - { - "name": "secretstorage", - "version": "3.3.0", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": {"cryptography": "*"}, - }, - { - "name": "cryptography", - "version": "3.2", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": {"six": "*"}, - }, - { - "name": "six", - "version": "1.15.0", - "category": "main", - "optional": False, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": { - "poetry": [], - "keyring": [], - "secretstorage": [], - "cryptography": [], - "six": [], - "junit-xml": [], - }, - }, - } - ) - set_package_requires( - poetry, skip={"keyring", "secretstorage", "cryptography", "six"} - ) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - # The dependency graph: - # junit-xml 1.9 Creates JUnit XML test result documents that can be read by tools - # └── six * such as Jenkins - # poetry 1.1.4 Python dependency management and packaging made easy. - # ├── keyring >=21.2.0,<22.0.0 - # │ ├── importlib-metadata >=1 - # │ │ └── zipp >=0.5 - # │ ├── jeepney >=0.4.2 - # │ ├── pywin32-ctypes <0.1.0 || >0.1.0,<0.1.1 || >0.1.1 - # │ └── secretstorage >=3.2 -- On linux only - # │ ├── cryptography >=2.0 - # │ │ └── six >=1.4.1 - # │ └── jeepney >=0.6 (circular dependency aborted here) - python27 = 'python_version >= "2.7" and python_version < "2.8"' - python36 = 'python_version >= "3.6" and python_version < "4.0"' - linux = 'sys_platform=="linux"' - expected = { - "poetry": Dependency.create_from_pep_508( - f"poetry==1.1.4; {python27} or {python36}" - ), - "junit-xml": Dependency.create_from_pep_508( - f"junit-xml==1.9 ; {python27} or {python36}" - ), - "keyring": Dependency.create_from_pep_508( - f"keyring==21.8.0 ; {python27} or {python36}" - ), - "secretstorage": Dependency.create_from_pep_508( - f"secretstorage==3.3.0 ; {python27} and {linux} or {python36} and {linux}" - ), - "cryptography": Dependency.create_from_pep_508( - f"cryptography==3.2 ; {python27} and {linux} or {python36} and {linux}" - ), - "six": Dependency.create_from_pep_508( - f"six==1.15.0 ; {python27} or {python36} or {python27} and {linux} or" - f" {python36} and {linux}" - ), - } - - for line in content.strip().split("\n"): - dependency = Dependency.create_from_pep_508(line) - assert dependency.name in expected - expected_dependency = expected.pop(dependency.name) - assert dependency == expected_dependency - assert dependency.marker == expected_dependency.marker - - -def test_exporter_can_export_requirements_txt_pyinstaller(tmp_dir: str, poetry: Poetry): - """Regression test for #3254""" - - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "pyinstaller", - "version": "4.0", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": { - "altgraph": "*", - "macholib": { - "version": "*", - "markers": "sys_platform == 'darwin'", - }, - }, - }, - { - "name": "altgraph", - "version": "0.17", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "macholib", - "version": "1.8", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": {"altgraph": ">=0.15"}, - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"pyinstaller": [], "altgraph": [], "macholib": []}, - }, - } - ) - set_package_requires(poetry, skip={"altgraph", "macholib"}) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - # Rationale for the results: - # * PyInstaller has an explicit dependency on altgraph, so it must always be - # installed. - # * PyInstaller requires macholib on Darwin, which in turn requires altgraph. - # The dependency graph: - # pyinstaller 4.0 PyInstaller bundles a Python application and all its - # ├── altgraph * dependencies into a single package. - # ├── macholib >=1.8 -- only on Darwin - # │ └── altgraph >=0.15 - python27 = 'python_version >= "2.7" and python_version < "2.8"' - python36 = 'python_version >= "3.6" and python_version < "4.0"' - darwin = 'sys_platform=="darwin"' - expected = { - "pyinstaller": Dependency.create_from_pep_508( - f"pyinstaller==4.0 ; {python27} or {python36}" - ), - "altgraph": Dependency.create_from_pep_508( - f"altgraph==0.17 ; {python27} or {python36} or {python27} and {darwin} or" - f" {python36} and {darwin}" - ), - "macholib": Dependency.create_from_pep_508( - f"macholib==1.8 ; {python27} and {darwin} or {python36} and {darwin}" - ), - } - - for line in content.strip().split("\n"): - dependency = Dependency.create_from_pep_508(line) - assert dependency.name in expected - expected_dependency = expected.pop(dependency.name) - assert dependency == expected_dependency - assert dependency.marker == expected_dependency.marker - - -def test_exporter_can_export_requirements_txt_with_nested_packages_and_markers( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "a", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "marker": "python_version < '3.7'", - "dependencies": {"b": ">=0.0.0", "c": ">=0.0.0"}, - }, - { - "name": "b", - "version": "4.5.6", - "category": "main", - "optional": False, - "python-versions": "*", - "marker": "platform_system == 'Windows'", - "dependencies": {"d": ">=0.0.0"}, - }, - { - "name": "c", - "version": "7.8.9", - "category": "main", - "optional": False, - "python-versions": "*", - "marker": "sys_platform == 'win32'", - "dependencies": {"d": ">=0.0.0"}, - }, - { - "name": "d", - "version": "0.0.1", - "category": "main", - "optional": False, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"a": [], "b": [], "c": [], "d": []}, - }, - } - ) - set_package_requires(poetry, skip={"b", "c", "d"}) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - python27 = 'python_version >= "2.7" and python_version < "2.8"' - python36 = 'python_version >= "3.6" and python_version < "3.7"' - windows = 'platform_system == "Windows"' - win32 = 'sys_platform == "win32"' - expected = { - "a": Dependency.create_from_pep_508(f"a==1.2.3 ; {python27} or {python36}"), - "b": Dependency.create_from_pep_508( - f"b==4.5.6 ; {python27} and {windows} or {python36} and {windows}" - ), - "c": Dependency.create_from_pep_508( - f"c==7.8.9 ; {python27} and {win32} or {python36} and {win32}" - ), - "d": Dependency.create_from_pep_508( - f"d==0.0.1 ; {python27} and {windows} or {python36} and {windows} or" - f" {python27} and {win32} or {python36} and {win32}" - ), - } - - for line in content.strip().split("\n"): - dependency = Dependency.create_from_pep_508(line) - assert dependency.name in expected - expected_dependency = expected.pop(dependency.name) - assert dependency == expected_dependency - assert dependency.marker == expected_dependency.marker - - assert expected == {} - - -@pytest.mark.parametrize( - ["dev", "lines"], - [ - ( - False, - [ - 'a==1.2.3 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "3.8"' - ], - ), - ( - True, - [ - 'a==1.2.3 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "3.8" or python_version' - ' >= "3.6" and python_version < "4.0"', - 'b==4.5.6 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - ], - ), - ], -) -def test_exporter_can_export_requirements_txt_with_nested_packages_and_markers_any( - tmp_dir: str, poetry: Poetry, dev: bool, lines: list[str] -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "a", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "b", - "version": "4.5.6", - "category": "dev", - "optional": False, - "python-versions": "*", - "dependencies": {"a": ">=1.2.3"}, - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"a": [], "b": []}, - }, - } - ) - - root = poetry.package.with_dependency_groups([], only=True) - root.add_dependency( - Factory.create_dependency( - name="a", constraint={"version": "^1.2.3", "python": "<3.8"} - ) - ) - root.add_dependency( - Factory.create_dependency( - name="b", constraint={"version": "^4.5.6"}, groups=["dev"] - ) - ) - poetry._package = root - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt", dev=dev) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - assert content.strip() == "\n".join(lines) - - -def test_exporter_can_export_requirements_txt_with_standard_packages_and_hashes( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "main", - "optional": False, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": ["12345"], "bar": ["67890"]}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:67890 -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:12345 -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_with_standard_packages_and_hashes_disabled( # noqa: E501 - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "main", - "optional": False, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": ["12345"], "bar": ["67890"]}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export( - "requirements.txt", Path(tmp_dir), "requirements.txt", with_hashes=False - ) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -""" - - assert content == expected - - -def test_exporter_exports_requirements_txt_without_dev_packages_by_default( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "dev", - "optional": False, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": ["12345"], "bar": ["67890"]}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:12345 -""" - - assert content == expected - - -def test_exporter_exports_requirements_txt_with_dev_packages_if_opted_in( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "dev", - "optional": False, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": ["12345"], "bar": ["67890"]}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt", dev=True) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:67890 -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:12345 -""" - - assert content == expected - - -def test_exporter_exports_requirements_txt_without_optional_packages( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "dev", - "optional": True, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": ["12345"], "bar": ["67890"]}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt", dev=True) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:12345 -""" - - assert content == expected - - -@pytest.mark.parametrize( - ["extras", "lines"], - [ - ( - None, - [ - 'foo==1.2.3 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"' - ], - ), - ( - False, - [ - 'foo==1.2.3 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"' - ], - ), - ( - True, - [ - 'bar==4.5.6 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - 'foo==1.2.3 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - 'spam==0.1.0 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - ], - ), - ( - ["feature_bar"], - [ - 'bar==4.5.6 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - 'foo==1.2.3 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - 'spam==0.1.0 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - ], - ), - ], -) -def test_exporter_exports_requirements_txt_with_optional_packages( - tmp_dir: str, - poetry: Poetry, - extras: bool | list[str] | None, - lines: list[str], -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "main", - "optional": True, - "python-versions": "*", - "dependencies": {"spam": ">=0.1"}, - }, - { - "name": "spam", - "version": "0.1.0", - "category": "main", - "optional": True, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": ["12345"], "bar": ["67890"], "spam": ["abcde"]}, - }, - "extras": {"feature_bar": ["bar"]}, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export( - "requirements.txt", - Path(tmp_dir), - "requirements.txt", - dev=True, - with_hashes=False, - extras=extras, - ) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = "\n".join(lines) - - assert content.strip() == expected - - -def test_exporter_can_export_requirements_txt_with_git_packages( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "source": { - "type": "git", - "url": "https://github.com/foo/foo.git", - "reference": "123456", - }, - } - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -foo @ git+https://github.com/foo/foo.git@123456 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_with_nested_packages( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "source": { - "type": "git", - "url": "https://github.com/foo/foo.git", - "reference": "123456", - }, - }, - { - "name": "bar", - "version": "4.5.6", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": {"foo": "rev 123456"}, - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": [], "bar": []}, - }, - } - ) - set_package_requires(poetry, skip={"foo"}) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -foo @ git+https://github.com/foo/foo.git@123456 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_with_nested_packages_cyclic( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": {"bar": {"version": "4.5.6"}}, - }, - { - "name": "bar", - "version": "4.5.6", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": {"baz": {"version": "7.8.9"}}, - }, - { - "name": "baz", - "version": "7.8.9", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": {"foo": {"version": "1.2.3"}}, - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": [], "bar": [], "baz": []}, - }, - } - ) - set_package_requires(poetry, skip={"bar", "baz"}) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -baz==7.8.9 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_with_nested_packages_and_multiple_markers( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": { - "bar": [ - { - "version": ">=1.2.3,<7.8.10", - "markers": 'platform_system != "Windows"', - }, - { - "version": ">=4.5.6,<7.8.10", - "markers": 'platform_system == "Windows"', - }, - ] - }, - }, - { - "name": "bar", - "version": "7.8.9", - "category": "main", - "optional": True, - "python-versions": "*", - "dependencies": { - "baz": { - "version": "!=10.11.12", - "markers": 'platform_system == "Windows"', - } - }, - }, - { - "name": "baz", - "version": "10.11.13", - "category": "main", - "optional": True, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": [], "bar": [], "baz": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export( - "requirements.txt", Path(tmp_dir), "requirements.txt", with_hashes=False - ) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -bar==7.8.9 ;\ - python_version >= "2.7" and python_version < "2.8" and platform_system != "Windows" or\ - python_version >= "3.6" and python_version < "4.0" and platform_system != "Windows" or\ - python_version >= "2.7" and python_version < "2.8" and platform_system == "Windows" or\ - python_version >= "3.6" and python_version < "4.0" and platform_system == "Windows" -baz==10.11.13 ;\ - python_version >= "2.7" and python_version < "2.8" and platform_system == "Windows" or\ - python_version >= "3.6" and python_version < "4.0" and platform_system == "Windows" -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_with_git_packages_and_markers( - tmp_dir: str, poetry: Poetry -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "marker": "python_version < '3.7'", - "source": { - "type": "git", - "url": "https://github.com/foo/foo.git", - "reference": "123456", - }, - } - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -foo @ git+https://github.com/foo/foo.git@123456 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "3.7" -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_with_directory_packages( - tmp_dir: str, poetry: Poetry, working_directory: Path -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "source": { - "type": "directory", - "url": "tests/fixtures/sample_project", - "reference": "", - }, - } - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = f"""\ -foo @ {working_directory.as_uri()}/tests/fixtures/sample_project ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_with_nested_directory_packages( - tmp_dir: str, poetry: Poetry, working_directory: Path -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "source": { - "type": "directory", - "url": "tests/fixtures/sample_project", - "reference": "", - }, - }, - { - "name": "bar", - "version": "4.5.6", - "category": "main", - "optional": False, - "python-versions": "*", - "source": { - "type": "directory", - "url": "tests/fixtures/sample_project/../project_with_nested_local/bar", # noqa: E501 - "reference": "", - }, - }, - { - "name": "baz", - "version": "7.8.9", - "category": "main", - "optional": False, - "python-versions": "*", - "source": { - "type": "directory", - "url": "tests/fixtures/sample_project/../project_with_nested_local/bar/..", # noqa: E501 - "reference": "", - }, - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": [], "bar": [], "baz": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = f"""\ -bar @ {working_directory.as_uri()}/tests/fixtures/project_with_nested_local/bar ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -baz @ {working_directory.as_uri()}/tests/fixtures/project_with_nested_local ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -foo @ {working_directory.as_uri()}/tests/fixtures/sample_project ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_with_directory_packages_and_markers( - tmp_dir: str, poetry: Poetry, working_directory: Path -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "marker": "python_version < '3.7'", - "source": { - "type": "directory", - "url": "tests/fixtures/sample_project", - "reference": "", - }, - } - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = f"""\ -foo @ {working_directory.as_uri()}/tests/fixtures/sample_project ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "3.7" -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_with_file_packages( - tmp_dir: str, poetry: Poetry, working_directory: Path -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "source": { - "type": "file", - "url": "tests/fixtures/distributions/demo-0.1.0.tar.gz", - "reference": "", - }, - } - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = f"""\ -foo @ {working_directory.as_uri()}/tests/fixtures/distributions/demo-0.1.0.tar.gz ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -""" - - assert content == expected - - -def test_exporter_can_export_requirements_txt_with_file_packages_and_markers( - tmp_dir: str, poetry: Poetry, working_directory: Path -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "marker": "python_version < '3.7'", - "source": { - "type": "file", - "url": "tests/fixtures/distributions/demo-0.1.0.tar.gz", - "reference": "", - }, - } - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt") - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = f"""\ -foo @ {working_directory.as_uri()}/tests/fixtures/distributions/demo-0.1.0.tar.gz ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "3.7" -""" - - assert content == expected - - -def test_exporter_exports_requirements_txt_with_legacy_packages( - tmp_dir: str, poetry: Poetry -): - poetry.pool.add_repository( - LegacyRepository( - "custom", - "https://example.com/simple", - ) - ) - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "dev", - "optional": False, - "python-versions": "*", - "source": { - "type": "legacy", - "url": "https://example.com/simple", - "reference": "", - }, - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": ["12345"], "bar": ["67890"]}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt", dev=True) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ ---extra-index-url https://example.com/simple - -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:67890 -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:12345 -""" - - assert content == expected - - -def test_exporter_exports_requirements_txt_with_url_false(tmp_dir: str, poetry: Poetry): - poetry.pool.add_repository( - LegacyRepository( - "custom", - "https://example.com/simple", - ) - ) - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "dev", - "optional": False, - "python-versions": "*", - "source": { - "type": "legacy", - "url": "https://example.com/simple", - "reference": "", - }, - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": ["12345"], "bar": ["67890"]}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export( - "requirements.txt", Path(tmp_dir), "requirements.txt", dev=True, with_urls=False - ) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:67890 -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:12345 -""" - - assert content == expected - - -def test_exporter_exports_requirements_txt_with_legacy_packages_trusted_host( - tmp_dir: str, poetry: Poetry -): - poetry.pool.add_repository( - LegacyRepository( - "custom", - "http://example.com/simple", - ) - ) - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "bar", - "version": "4.5.6", - "category": "dev", - "optional": False, - "python-versions": "*", - "source": { - "type": "legacy", - "url": "http://example.com/simple", - "reference": "", - }, - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"bar": ["67890"]}, - }, - } - ) - set_package_requires(poetry) - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt", dev=True) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ ---trusted-host example.com ---extra-index-url http://example.com/simple - -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:67890 -""" - - assert content == expected - - -@pytest.mark.parametrize( - ["dev", "expected"], - [ - ( - True, - [ - 'bar==1.2.2 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - 'baz==1.2.3 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - 'foo==1.2.1 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - ], - ), - ( - False, - [ - 'bar==1.2.2 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - 'foo==1.2.1 ; python_version >= "2.7" and python_version < "2.8" or' - ' python_version >= "3.6" and python_version < "4.0"', - ], - ), - ], -) -def test_exporter_exports_requirements_txt_with_dev_extras( - tmp_dir: str, poetry: Poetry, dev: bool, expected: list[str] -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.1", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "1.2.2", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": { - "baz": { - "version": ">=0.1.0", - "optional": True, - "markers": "extra == 'baz'", - } - }, - "extras": {"baz": ["baz (>=0.1.0)"]}, - }, - { - "name": "baz", - "version": "1.2.3", - "category": "dev", - "optional": False, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": [], "bar": [], "baz": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt", dev=dev) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - assert content == "\n".join(expected) + "\n" - - -def test_exporter_exports_requirements_txt_with_legacy_packages_and_duplicate_sources( - tmp_dir: str, poetry: Poetry -): - poetry.pool.add_repository( - LegacyRepository( - "custom", - "https://example.com/simple", - ) - ) - poetry.pool.add_repository( - LegacyRepository( - "custom", - "https://foobaz.com/simple", - ) - ) - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - "source": { - "type": "legacy", - "url": "https://example.com/simple", - "reference": "", - }, - }, - { - "name": "bar", - "version": "4.5.6", - "category": "dev", - "optional": False, - "python-versions": "*", - "source": { - "type": "legacy", - "url": "https://example.com/simple", - "reference": "", - }, - }, - { - "name": "baz", - "version": "7.8.9", - "category": "dev", - "optional": False, - "python-versions": "*", - "source": { - "type": "legacy", - "url": "https://foobaz.com/simple", - "reference": "", - }, - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": ["12345"], "bar": ["67890"], "baz": ["24680"]}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), "requirements.txt", dev=True) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ ---extra-index-url https://example.com/simple ---extra-index-url https://foobaz.com/simple - -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:67890 -baz==7.8.9 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:24680 -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:12345 -""" - - assert content == expected - - -def test_exporter_exports_requirements_txt_with_legacy_packages_and_credentials( - tmp_dir: str, poetry: Poetry, config: Config -): - poetry.config.merge( - { - "repositories": {"custom": {"url": "https://example.com/simple"}}, - "http-basic": {"custom": {"username": "foo", "password": "bar"}}, - } - ) - poetry.pool.add_repository( - LegacyRepository("custom", "https://example.com/simple", config=poetry.config) - ) - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "dev", - "optional": False, - "python-versions": "*", - "source": { - "type": "legacy", - "url": "https://example.com/simple", - "reference": "", - }, - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": ["12345"], "bar": ["67890"]}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export( - "requirements.txt", - Path(tmp_dir), - "requirements.txt", - dev=True, - with_credentials=True, - ) - - with (Path(tmp_dir) / "requirements.txt").open(encoding="utf-8") as f: - content = f.read() - - expected = """\ ---extra-index-url https://foo:bar@example.com/simple - -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:67890 -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" \\ - --hash=sha256:12345 -""" - - assert content == expected - - -def test_exporter_exports_requirements_txt_to_standard_output( - tmp_dir: str, poetry: Poetry, capsys: CaptureFixture -): - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "foo", - "version": "1.2.3", - "category": "main", - "optional": False, - "python-versions": "*", - }, - { - "name": "bar", - "version": "4.5.6", - "category": "main", - "optional": False, - "python-versions": "*", - }, - ], - "metadata": { - "python-versions": "*", - "content-hash": "123456789", - "hashes": {"foo": [], "bar": []}, - }, - } - ) - set_package_requires(poetry) - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), sys.stdout) - - out, err = capsys.readouterr() - expected = """\ -bar==4.5.6 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -foo==1.2.3 ;\ - python_version >= "2.7" and python_version < "2.8" or\ - python_version >= "3.6" and python_version < "4.0" -""" - - assert out == expected - - -def test_exporter_doesnt_confuse_repeated_packages( - tmp_dir: str, poetry: Poetry, capsys: CaptureFixture -): - # Testcase derived from . - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "celery", - "version": "5.1.2", - "category": "main", - "optional": False, - "python-versions": "<3.7", - "dependencies": { - "click": ">=7.0,<8.0", - "click-didyoumean": ">=0.0.3", - "click-plugins": ">=1.1.1", - }, - }, - { - "name": "celery", - "version": "5.2.3", - "category": "main", - "optional": False, - "python-versions": ">=3.7", - "dependencies": { - "click": ">=8.0.3,<9.0", - "click-didyoumean": ">=0.0.3", - "click-plugins": ">=1.1.1", - }, - }, - { - "name": "click", - "version": "7.1.2", - "category": "main", - "optional": False, - "python-versions": ( - ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - ), - }, - { - "name": "click", - "version": "8.0.3", - "category": "main", - "optional": False, - "python-versions": ">=3.6", - "dependencies": {}, - }, - { - "name": "click-didyoumean", - "version": "0.0.3", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": {"click": "*"}, - }, - { - "name": "click-didyoumean", - "version": "0.3.0", - "category": "main", - "optional": False, - "python-versions": ">=3.6.2,<4.0.0", - "dependencies": {"click": ">=7"}, - }, - { - "name": "click-plugins", - "version": "1.1.1", - "category": "main", - "optional": False, - "python-versions": "*", - "dependencies": {"click": ">=4.0"}, - }, - ], - "metadata": { - "lock-version": "1.1", - "python-versions": "^3.6", - "content-hash": ( - "832b13a88e5020c27cbcd95faa577bf0dbf054a65c023b45dc9442b640d414e6" - ), - "hashes": { - "celery": [], - "click-didyoumean": [], - "click-plugins": [], - "click": [], - }, - }, - } - ) - root = poetry.package.with_dependency_groups([], only=True) - root.python_versions = "^3.6" - root.add_dependency( - Factory.create_dependency( - name="celery", constraint={"version": "5.1.2", "python": "<3.7"} - ) - ) - root.add_dependency( - Factory.create_dependency( - name="celery", constraint={"version": "5.2.3", "python": ">=3.7"} - ) - ) - poetry._package = root - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), sys.stdout) - - out, err = capsys.readouterr() - expected = """\ -celery==5.1.2 ; python_version >= "3.6" and python_version < "3.7" -celery==5.2.3 ; python_version >= "3.7" and python_version < "4.0" -click-didyoumean==0.0.3 ; python_version >= "3.6" and python_version < "3.7" -click-didyoumean==0.3.0 ; python_version >= "3.7" and python_full_version < "4.0.0" -click-plugins==1.1.1 ;\ - python_version >= "3.6" and python_version < "3.7" or\ - python_version >= "3.7" and python_version < "4.0" -click==7.1.2 ; python_version >= "3.6" and python_version < "3.7" -click==8.0.3 ;\ - python_version >= "3.7" and python_version < "4.0" or\ - python_version >= "3.7" and python_full_version < "4.0.0" -""" - - assert out == expected - - -def test_exporter_handles_extras_next_to_non_extras( - tmp_dir: str, poetry: Poetry, capsys: CaptureFixture -): - # Testcase similar to the solver testcase added at #5305. - poetry.locker.mock_lock_data( - { - "package": [ - { - "name": "localstack", - "python-versions": "*", - "version": "1.0.0", - "category": "main", - "optional": False, - "dependencies": { - "localstack-ext": [ - {"version": ">=1.0.0"}, - { - "version": ">=1.0.0", - "extras": ["bar"], - "markers": 'extra == "foo"', - }, - ] - }, - "extras": {"foo": ["localstack-ext (>=1.0.0)"]}, - }, - { - "name": "localstack-ext", - "python-versions": "*", - "version": "1.0.0", - "category": "main", - "optional": False, - "dependencies": { - "something": "*", - "something-else": { - "version": ">=1.0.0", - "markers": 'extra == "bar"', - }, - "another-thing": { - "version": ">=1.0.0", - "markers": 'extra == "baz"', - }, - }, - "extras": { - "bar": ["something-else (>=1.0.0)"], - "baz": ["another-thing (>=1.0.0)"], - }, - }, - { - "name": "something", - "python-versions": "*", - "version": "1.0.0", - "category": "main", - "optional": False, - "dependencies": {}, - }, - { - "name": "something-else", - "python-versions": "*", - "version": "1.0.0", - "category": "main", - "optional": False, - "dependencies": {}, - }, - { - "name": "another-thing", - "python-versions": "*", - "version": "1.0.0", - "category": "main", - "optional": False, - "dependencies": {}, - }, - ], - "metadata": { - "lock-version": "1.1", - "python-versions": "^3.6", - "content-hash": ( - "832b13a88e5020c27cbcd95faa577bf0dbf054a65c023b45dc9442b640d414e6" - ), - "hashes": { - "localstack": [], - "localstack-ext": [], - "something": [], - "something-else": [], - "another-thing": [], - }, - }, - } - ) - root = poetry.package.with_dependency_groups([], only=True) - root.python_versions = "^3.6" - root.add_dependency( - Factory.create_dependency( - name="localstack", constraint={"version": "^1.0.0", "extras": ["foo"]} - ) - ) - poetry._package = root - - exporter = Exporter(poetry) - - exporter.export("requirements.txt", Path(tmp_dir), sys.stdout) - - out, err = capsys.readouterr() - expected = """\ -localstack-ext==1.0.0 ; python_version >= "3.6" and python_version < "4.0" -localstack==1.0.0 ; python_version >= "3.6" and python_version < "4.0" -something-else==1.0.0 ; python_version >= "3.6" and python_version < "4.0" -something==1.0.0 ; python_version >= "3.6" and python_version < "4.0" -""" - - assert out == expected From 2216fc11751add00b50b25a95c02a4cfe633e27a Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 5 Apr 2022 17:57:50 +0200 Subject: [PATCH 5/8] locker: fix project marker type hinting --- src/poetry/packages/locker.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/poetry/packages/locker.py b/src/poetry/packages/locker.py index 46eae599982..43e8d7dee4e 100644 --- a/src/poetry/packages/locker.py +++ b/src/poetry/packages/locker.py @@ -32,7 +32,6 @@ if TYPE_CHECKING: - from poetry.core.semver.version_constraint import VersionConstraint from poetry.core.version.markers import BaseMarker from tomlkit.items import InlineTable from tomlkit.toml_document import TOMLDocument @@ -316,7 +315,7 @@ def get_project_dependencies( def get_project_dependency_packages( self, project_requires: list[Dependency], - project_python_marker: VersionConstraint | None = None, + project_python_marker: BaseMarker | None = None, dev: bool = False, extras: bool | Sequence[str] | None = None, ) -> Iterator[DependencyPackage]: From 55700880932637cb1d24e107aeb27c9f7556c532 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Mon, 11 Apr 2022 00:32:30 +0200 Subject: [PATCH 6/8] locker: remove with_dev_reqs parameter This is no longer required and is superseded by the groups feature. --- src/poetry/console/commands/show.py | 2 +- src/poetry/installation/installer.py | 6 +++--- src/poetry/packages/locker.py | 13 +++---------- tests/console/commands/test_lock.py | 6 ++---- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/poetry/console/commands/show.py b/src/poetry/console/commands/show.py index d2557af1994..22e9b7b4f2c 100644 --- a/src/poetry/console/commands/show.py +++ b/src/poetry/console/commands/show.py @@ -75,7 +75,7 @@ def handle(self) -> int | None: ) return 1 - locked_repo = self.poetry.locker.locked_repository(True) + locked_repo = self.poetry.locker.locked_repository() root = self.project_with_activated_groups_only() # Show tree view if requested diff --git a/src/poetry/installation/installer.py b/src/poetry/installation/installer.py index de5216775a9..4a63e0767aa 100644 --- a/src/poetry/installation/installer.py +++ b/src/poetry/installation/installer.py @@ -190,7 +190,7 @@ def _do_refresh(self) -> int: if extra not in self._package.extras: raise ValueError(f"Extra [{extra}] is not specified.") - locked_repository = self._locker.locked_repository(True) + locked_repository = self._locker.locked_repository() solver = Solver( self._package, self._pool, @@ -214,7 +214,7 @@ def _do_install(self, local_repo: Repository) -> int: locked_repository = Repository() if self._update: if self._locker.is_locked() and not self._lock: - locked_repository = self._locker.locked_repository(True) + locked_repository = self._locker.locked_repository() # If no packages have been whitelisted (The ones we want to update), # we whitelist every package in the lock file. @@ -240,7 +240,7 @@ def _do_install(self, local_repo: Repository) -> int: else: self._io.write_line("Installing dependencies from lock file") - locked_repository = self._locker.locked_repository(True) + locked_repository = self._locker.locked_repository() if not self._locker.is_fresh(): self._io.write_error_line( diff --git a/src/poetry/packages/locker.py b/src/poetry/packages/locker.py index 43e8d7dee4e..15f87c6505f 100644 --- a/src/poetry/packages/locker.py +++ b/src/poetry/packages/locker.py @@ -85,7 +85,7 @@ def is_fresh(self) -> bool: return False - def locked_repository(self, with_dev_reqs: bool = False) -> Repository: + def locked_repository(self) -> Repository: """ Searches and returns a repository of locked packages. """ @@ -97,13 +97,7 @@ def locked_repository(self, with_dev_reqs: bool = False) -> Repository: lock_data = self.lock_data packages = Repository() - - if with_dev_reqs: - locked_packages = lock_data["package"] - else: - locked_packages = [ - p for p in lock_data["package"] if p["category"] == "main" - ] + locked_packages = lock_data["package"] if not locked_packages: return packages @@ -316,7 +310,6 @@ def get_project_dependency_packages( self, project_requires: list[Dependency], project_python_marker: BaseMarker | None = None, - dev: bool = False, extras: bool | Sequence[str] | None = None, ) -> Iterator[DependencyPackage]: # Apply the project python marker to all requirements. @@ -328,7 +321,7 @@ def get_project_dependency_packages( marked_requires.append(require) project_requires = marked_requires - repository = self.locked_repository(with_dev_reqs=dev) + repository = self.locked_repository() # Build a set of all packages required by our selected extras extra_package_names: set[str] | None = None diff --git a/tests/console/commands/test_lock.py b/tests/console/commands/test_lock.py index f12837d6bc3..dae977a1d27 100644 --- a/tests/console/commands/test_lock.py +++ b/tests/console/commands/test_lock.py @@ -129,9 +129,7 @@ def test_lock_no_update( ) poetry_with_old_lockfile.set_locker(locker) - locked_repository = poetry_with_old_lockfile.locker.locked_repository( - with_dev_reqs=True - ) + locked_repository = poetry_with_old_lockfile.locker.locked_repository() assert ( poetry_with_old_lockfile.locker.lock_data["metadata"].get("lock-version") == "1.0" @@ -144,7 +142,7 @@ def test_lock_no_update( lock=poetry_with_old_lockfile.pyproject.file.path.parent / "poetry.lock", local_config={}, ) - packages = locker.locked_repository(True).packages + packages = locker.locked_repository().packages assert len(packages) == len(locked_repository.packages) From 2ff7813e706ca637e58cd8c7c892cf7b042b8a98 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Mon, 11 Apr 2022 19:50:38 +0200 Subject: [PATCH 7/8] update deprecation warning for export --dev flag --- src/poetry/console/commands/group_command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/poetry/console/commands/group_command.py b/src/poetry/console/commands/group_command.py index 5d330c6036a..23bf04e3a95 100644 --- a/src/poetry/console/commands/group_command.py +++ b/src/poetry/console/commands/group_command.py @@ -69,7 +69,7 @@ def activated_groups(self) -> set[str]: for opt, new, group in [ ("default", "only", "default"), ("no-dev", "only", "default"), - ("dev", "without", "default"), + ("dev", "with", "dev"), ("dev-only", "without", "default"), ]: if self.io.input.has_option(opt) and self.option(opt): From 2444521855c6f4c19a061d1f65c48fbc7d115fe5 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Mon, 11 Apr 2022 20:48:53 +0200 Subject: [PATCH 8/8] ci: ensure tests against poetry-plugin-export suite --- .github/workflows/main.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b9e76eca2d4..af810ff052e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -84,3 +84,19 @@ jobs: - name: Run pytest run: poetry run python -m pytest -p no:sugar -q tests/ + + - name: Get Plugin Version (poetry-plugin-export) + id: poetry-plugin-export-version + run: | + echo ::set-output name=version::$(\ + poetry show poetry-plugin-export | grep version | cut -d : -f 2 | xargs) + + - name: Checkout Plugin Source (poetry-plugin-export) + uses: actions/checkout@v2 + with: + path: poetry-plugin-export + repository: python-poetry/poetry-plugin-export + ref: refs/tags/${{ steps.poetry-plugin-export-version.outputs.version }} + + - name: Run pytest (poetry-plugin-export) + run: poetry run python -m pytest -p no:sugar -q poetry-plugin-export/tests/