diff --git a/docs/docs/cli.md b/docs/docs/cli.md
index bf207e8dc42..32e4c51a8ec 100644
--- a/docs/docs/cli.md
+++ b/docs/docs/cli.md
@@ -320,6 +320,7 @@ Note that, at the moment, only pure python wheels are supported.
### Options
* `--format (-f)`: Limit the format to either `wheel` or `sdist`.
+* `--lock (-l)`: Add all pinned dependencies from `poetry.lock` to `pyproject.toml` of source and wheels archive
## publish
diff --git a/poetry/console/commands/build.py b/poetry/console/commands/build.py
index 72b7319fcd5..2da32607a1f 100644
--- a/poetry/console/commands/build.py
+++ b/poetry/console/commands/build.py
@@ -9,7 +9,13 @@ class BuildCommand(EnvCommand):
description = "Builds a package, as a tarball and a wheel by default."
options = [
- option("format", "f", "Limit the format to either sdist or wheel.", flag=False)
+ option("format", "f", "Limit the format to either sdist or wheel.", flag=False),
+ option(
+ "lock",
+ "l",
+ "Add all dependencies as locked dependencies in the distributed pyproject.toml",
+ flag=True,
+ ),
]
loggers = [
@@ -19,12 +25,14 @@ class BuildCommand(EnvCommand):
]
def handle(self):
- from poetry.core.masonry import Builder
+ from poetry.core.masonry import builder
fmt = "all"
if self.option("format"):
fmt = self.option("format")
+ lock = self.option("lock")
+
package = self.poetry.package
self.line(
"Building {} ({})".format(
@@ -32,5 +40,27 @@ def handle(self):
)
)
- builder = Builder(self.poetry)
+ original_content = self.poetry.file.read()
+ if lock:
+ section = "dependencies"
+
+ content = self.poetry.file.read()
+ poetry_content = content["tool"]["poetry"]
+ if section not in poetry_content:
+ poetry_content[section] = {}
+
+ for dependency_package in self.poetry.locker.lock_data["package"]:
+ name = dependency_package["name"]
+ version = dependency_package["version"]
+ poetry_content[section][name] = version
+
+ self._write_pyproject_toml(content)
+
+ builder = builder.Builder(self.poetry)
builder.build(fmt, executable=self.env.python)
+
+ if lock:
+ self._write_pyproject_toml(original_content)
+
+ def _write_pyproject_toml(self, content):
+ self.poetry.file.write(content)
diff --git a/tests/console/commands/test_build.py b/tests/console/commands/test_build.py
new file mode 100644
index 00000000000..13ef85faf45
--- /dev/null
+++ b/tests/console/commands/test_build.py
@@ -0,0 +1,83 @@
+import pytest
+
+from poetry.console.commands.build import BuildCommand
+from poetry.core.masonry import builder
+from poetry.utils._compat import Path
+
+
+@pytest.fixture
+def source_dir(tmp_path): # type: (Path) -> Path
+ yield Path(tmp_path.as_posix())
+
+
+@pytest.fixture
+def tester(command_tester_factory):
+ return command_tester_factory("build")
+
+
+class MockedBuilder:
+ def __init__(self, *args, **kw):
+ pass
+
+ def build(self, *args, **kw):
+ pass
+
+
+@pytest.fixture
+def poetry_with_lock_file(project_factory, fixture_dir):
+ source = fixture_dir("lock")
+ pyproject_content = (source / "pyproject.toml").read_text(encoding="utf-8")
+ poetry_lock_content = (source / "poetry.lock").read_text(encoding="utf-8")
+ return project_factory(
+ pyproject_content=pyproject_content, poetry_lock_content=poetry_lock_content,
+ )
+
+
+def test_build_without_lock(command_tester_factory, poetry_with_lock_file, monkeypatch):
+ content_of_written_pyproject_toml = []
+ monkeypatch.setattr(
+ BuildCommand,
+ "_write_pyproject_toml",
+ lambda _, content: content_of_written_pyproject_toml.append(content),
+ )
+ monkeypatch.setattr(builder, "Builder", MockedBuilder)
+
+ tester = command_tester_factory("build", poetry=poetry_with_lock_file)
+ tester.execute()
+
+ # pyproject.toml is not changed
+ assert len(content_of_written_pyproject_toml) == 0
+
+
+def test_build_with_lock(command_tester_factory, poetry_with_lock_file, monkeypatch):
+ content_of_written_pyproject_toml = []
+ monkeypatch.setattr(
+ BuildCommand,
+ "_write_pyproject_toml",
+ lambda _, content: content_of_written_pyproject_toml.append(content),
+ )
+ monkeypatch.setattr(builder, "Builder", MockedBuilder)
+
+ tester = command_tester_factory("build", poetry=poetry_with_lock_file)
+ tester.execute("--lock")
+
+ assert len(
+ content_of_written_pyproject_toml[0]["tool"]["poetry"]["dependencies"]
+ ) > len(content_of_written_pyproject_toml[1]["tool"]["poetry"]["dependencies"])
+ assert content_of_written_pyproject_toml[0]["tool"]["poetry"]["dependencies"] == {
+ "python": "^3.8",
+ "sampleproject": ">=1.3.1",
+ "certifi": "2020.6.20",
+ "chardet": "3.0.4",
+ "docker": "4.3.1",
+ "idna": "2.10",
+ "pywin32": "227",
+ "requests": "2.24.0",
+ "six": "1.15.0",
+ "urllib3": "1.25.10",
+ "websocket-client": "0.57.0",
+ }
+ assert content_of_written_pyproject_toml[1]["tool"]["poetry"]["dependencies"] == {
+ "python": "^3.8",
+ "sampleproject": ">=1.3.1",
+ }
diff --git a/tests/fixtures/lock/poetry.lock b/tests/fixtures/lock/poetry.lock
new file mode 100644
index 00000000000..22cd049e68f
--- /dev/null
+++ b/tests/fixtures/lock/poetry.lock
@@ -0,0 +1,152 @@
+[[package]]
+name = "certifi"
+version = "2020.6.20"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "chardet"
+version = "3.0.4"
+description = "Universal encoding detector for Python 2 and 3"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "docker"
+version = "4.3.1"
+description = "A Python library for the Docker Engine API."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.dependencies]
+pywin32 = {version = "227", markers = "sys_platform == \"win32\""}
+requests = ">=2.14.2,<2.18.0 || >2.18.0"
+six = ">=1.4.0"
+websocket-client = ">=0.32.0"
+
+[package.extras]
+ssh = ["paramiko (>=2.4.2)"]
+tls = ["pyOpenSSL (>=17.5.0)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"]
+
+[[package]]
+name = "idna"
+version = "2.10"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "pywin32"
+version = "227"
+description = "Python for Window Extensions"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "requests"
+version = "2.24.0"
+description = "Python HTTP for Humans."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+chardet = ">=3.0.2,<4"
+idna = ">=2.5,<3"
+urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26"
+
+[package.extras]
+security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
+socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"]
+
+[[package]]
+name = "six"
+version = "1.15.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "urllib3"
+version = "1.25.10"
+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)"]
+secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"]
+socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"]
+
+[[package]]
+name = "websocket-client"
+version = "0.57.0"
+description = "WebSocket client for Python. hybi13 is supported."
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.dependencies]
+six = "*"
+
+[metadata]
+lock-version = "1.1"
+python-versions = "^3.8"
+content-hash = "bb4c2f3c089b802c1930b6acbeed04711d93e9cdfd9a003eb17518a6d9f350c6"
+
+[metadata.files]
+certifi = [
+ {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"},
+ {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"},
+]
+chardet = [
+ {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
+ {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"},
+]
+docker = [
+ {file = "docker-4.3.1-py2.py3-none-any.whl", hash = "sha256:13966471e8bc23b36bfb3a6fb4ab75043a5ef1dac86516274777576bed3b9828"},
+ {file = "docker-4.3.1.tar.gz", hash = "sha256:bad94b8dd001a8a4af19ce4becc17f41b09f228173ffe6a4e0355389eef142f2"},
+]
+idna = [
+ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
+ {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
+]
+pywin32 = [
+ {file = "pywin32-227-cp27-cp27m-win32.whl", hash = "sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0"},
+ {file = "pywin32-227-cp27-cp27m-win_amd64.whl", hash = "sha256:4cdad3e84191194ea6d0dd1b1b9bdda574ff563177d2adf2b4efec2a244fa116"},
+ {file = "pywin32-227-cp35-cp35m-win32.whl", hash = "sha256:f4c5be1a293bae0076d93c88f37ee8da68136744588bc5e2be2f299a34ceb7aa"},
+ {file = "pywin32-227-cp35-cp35m-win_amd64.whl", hash = "sha256:a929a4af626e530383a579431b70e512e736e9588106715215bf685a3ea508d4"},
+ {file = "pywin32-227-cp36-cp36m-win32.whl", hash = "sha256:300a2db938e98c3e7e2093e4491439e62287d0d493fe07cce110db070b54c0be"},
+ {file = "pywin32-227-cp36-cp36m-win_amd64.whl", hash = "sha256:9b31e009564fb95db160f154e2aa195ed66bcc4c058ed72850d047141b36f3a2"},
+ {file = "pywin32-227-cp37-cp37m-win32.whl", hash = "sha256:47a3c7551376a865dd8d095a98deba954a98f326c6fe3c72d8726ca6e6b15507"},
+ {file = "pywin32-227-cp37-cp37m-win_amd64.whl", hash = "sha256:31f88a89139cb2adc40f8f0e65ee56a8c585f629974f9e07622ba80199057511"},
+ {file = "pywin32-227-cp38-cp38-win32.whl", hash = "sha256:7f18199fbf29ca99dff10e1f09451582ae9e372a892ff03a28528a24d55875bc"},
+ {file = "pywin32-227-cp38-cp38-win_amd64.whl", hash = "sha256:7c1ae32c489dc012930787f06244426f8356e129184a02c25aef163917ce158e"},
+ {file = "pywin32-227-cp39-cp39-win32.whl", hash = "sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295"},
+ {file = "pywin32-227-cp39-cp39-win_amd64.whl", hash = "sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c"},
+]
+requests = [
+ {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
+ {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
+]
+six = [
+ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
+ {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
+]
+urllib3 = [
+ {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"},
+ {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"},
+]
+websocket-client = [
+ {file = "websocket_client-0.57.0-py2.py3-none-any.whl", hash = "sha256:0fc45c961324d79c781bab301359d5a1b00b13ad1b10415a4780229ef71a5549"},
+ {file = "websocket_client-0.57.0.tar.gz", hash = "sha256:d735b91d6d1692a6a181f2a8c9e0238e5f6373356f561bb9dc4c7af36f452010"},
+]
diff --git a/tests/fixtures/lock/pyproject.toml b/tests/fixtures/lock/pyproject.toml
new file mode 100644
index 00000000000..377aa676be9
--- /dev/null
+++ b/tests/fixtures/lock/pyproject.toml
@@ -0,0 +1,15 @@
+[tool.poetry]
+name = "foobar"
+version = "0.1.0"
+description = ""
+authors = ["Poetry Developer "]
+
+[tool.poetry.dependencies]
+python = "^3.8"
+sampleproject = ">=1.3.1"
+
+[tool.poetry.dev-dependencies]
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"