Commit 30a9b13b by finswimmer Committed by GitHub

init: reuse existing pyproject.toml when safe (#2448)

At the moment a `poetry init` will fail, if a pyproject.toml already
exists. This change ensures pyproject.toml is reused if no build-system
has already been defined or poetry is not already configured.

Resolves: #1639
parent e49f0934
...@@ -13,6 +13,7 @@ from typing import Union ...@@ -13,6 +13,7 @@ from typing import Union
from cleo import option from cleo import option
from tomlkit import inline_table from tomlkit import inline_table
from poetry.core.pyproject.toml import PyProjectTOML
from poetry.utils._compat import OrderedDict from poetry.utils._compat import OrderedDict
from poetry.utils._compat import Path from poetry.utils._compat import Path
from poetry.utils._compat import urlparse from poetry.utils._compat import urlparse
...@@ -22,7 +23,6 @@ from .env_command import EnvCommand ...@@ -22,7 +23,6 @@ from .env_command import EnvCommand
class InitCommand(Command): class InitCommand(Command):
name = "init" name = "init"
description = ( description = (
"Creates a basic <comment>pyproject.toml</> file in the current directory." "Creates a basic <comment>pyproject.toml</> file in the current directory."
...@@ -67,9 +67,20 @@ The <c1>init</c1> command creates a basic <comment>pyproject.toml</> file in the ...@@ -67,9 +67,20 @@ The <c1>init</c1> command creates a basic <comment>pyproject.toml</> file in the
from poetry.utils._compat import Path from poetry.utils._compat import Path
from poetry.utils.env import SystemEnv from poetry.utils.env import SystemEnv
if (Path.cwd() / "pyproject.toml").exists(): pyproject = PyProjectTOML(Path.cwd() / "pyproject.toml")
self.line("<error>A pyproject.toml file already exists.</error>")
return 1 if pyproject.file.exists():
if pyproject.is_poetry_project():
self.line(
"<error>A pyproject.toml file with a poetry section already exists.</error>"
)
return 1
if pyproject.data.get("build-system"):
self.line(
"<error>A pyproject.toml file with a defined build-system already exists.</error>"
)
return 1
vcs_config = GitConfig() vcs_config = GitConfig()
...@@ -198,7 +209,7 @@ The <c1>init</c1> command creates a basic <comment>pyproject.toml</> file in the ...@@ -198,7 +209,7 @@ The <c1>init</c1> command creates a basic <comment>pyproject.toml</> file in the
dev_dependencies=dev_requirements, dev_dependencies=dev_requirements,
) )
content = layout_.generate_poetry_content() content = layout_.generate_poetry_content(pyproject)
if self.io.is_interactive(): if self.io.is_interactive():
self.line("<info>Generated file</info>") self.line("<info>Generated file</info>")
self.line("") self.line("")
......
from typing import TYPE_CHECKING
from tomlkit import dumps from tomlkit import dumps
from tomlkit import loads from tomlkit import loads
from tomlkit import table from tomlkit import table
...@@ -5,6 +7,9 @@ from tomlkit import table ...@@ -5,6 +7,9 @@ from tomlkit import table
from poetry.utils.helpers import module_name from poetry.utils.helpers import module_name
if TYPE_CHECKING:
from poetry.core.pyproject.toml import PyProjectTOML
TESTS_DEFAULT = u"""from {package_name} import __version__ TESTS_DEFAULT = u"""from {package_name} import __version__
...@@ -81,7 +86,7 @@ class Layout(object): ...@@ -81,7 +86,7 @@ class Layout(object):
self._write_poetry(path) self._write_poetry(path)
def generate_poetry_content(self): def generate_poetry_content(self, original_toml): # type: ("PyProjectTOML") -> str
template = POETRY_DEFAULT template = POETRY_DEFAULT
if self._license: if self._license:
template = POETRY_WITH_LICENSE template = POETRY_WITH_LICENSE
...@@ -114,7 +119,12 @@ class Layout(object): ...@@ -114,7 +119,12 @@ class Layout(object):
content.add("build-system", build_system) content.add("build-system", build_system)
return dumps(content) content = dumps(content)
if original_toml.file.exists():
content = dumps(original_toml.data) + "\n" + content
return content
def _create_default(self, path, src=True): def _create_default(self, path, src=True):
raise NotImplementedError() raise NotImplementedError()
......
...@@ -3,14 +3,19 @@ import sys ...@@ -3,14 +3,19 @@ import sys
import pytest import pytest
from poetry.utils._compat import Path from poetry.utils._compat import Path
from poetry.utils._compat import decode
from tests.helpers import get_package from tests.helpers import get_package
@pytest.fixture
def source_dir(tmp_path): # type: (Path) -> Path
yield Path(tmp_path.as_posix())
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def patches(mocker, mocked_open_files): def patches(mocker, source_dir):
mocked_open_files.append("pyproject.toml")
patch = mocker.patch("poetry.utils._compat.Path.cwd") patch = mocker.patch("poetry.utils._compat.Path.cwd")
patch.return_value = Path(__file__).parent patch.return_value = source_dir
@pytest.fixture @pytest.fixture
...@@ -18,21 +23,26 @@ def tester(command_tester_factory): ...@@ -18,21 +23,26 @@ def tester(command_tester_factory):
return command_tester_factory("init") return command_tester_factory("init")
def test_basic_interactive(tester): @pytest.fixture
inputs = [ def init_basic_inputs():
"my-package", # Package name return "\n".join(
"1.2.3", # Version [
"This is a description", # Description "my-package", # Package name
"n", # Author "1.2.3", # Version
"MIT", # License "This is a description", # Description
"~2.7 || ^3.6", # Python "n", # Author
"n", # Interactive packages "MIT", # License
"n", # Interactive dev packages "~2.7 || ^3.6", # Python
"\n", # Generate "n", # Interactive packages
] "n", # Interactive dev packages
tester.execute(inputs="\n".join(inputs)) "\n", # Generate
]
)
expected = """\
@pytest.fixture()
def init_basic_toml():
return """\
[tool.poetry] [tool.poetry]
name = "my-package" name = "my-package"
version = "1.2.3" version = "1.2.3"
...@@ -46,7 +56,10 @@ python = "~2.7 || ^3.6" ...@@ -46,7 +56,10 @@ python = "~2.7 || ^3.6"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
""" """
assert expected in tester.io.fetch_output()
def test_basic_interactive(tester, init_basic_inputs, init_basic_toml):
tester.execute(inputs=init_basic_inputs)
assert init_basic_toml in tester.io.fetch_output()
def test_interactive_with_dependencies(tester, repo): def test_interactive_with_dependencies(tester, repo):
...@@ -565,3 +578,36 @@ def test_add_package_with_extras_and_whitespace(tester): ...@@ -565,3 +578,36 @@ def test_add_package_with_extras_and_whitespace(tester):
assert len(result[0]["extras"]) == 2 assert len(result[0]["extras"]) == 2
assert "postgresql" in result[0]["extras"] assert "postgresql" in result[0]["extras"]
assert "sqlite" in result[0]["extras"] assert "sqlite" in result[0]["extras"]
def test_init_existing_pyproject_simple(
tester, source_dir, init_basic_inputs, init_basic_toml
):
pyproject_file = source_dir / "pyproject.toml"
existing_section = """
[tool.black]
line-length = 88
"""
pyproject_file.write_text(decode(existing_section))
tester.execute(inputs=init_basic_inputs)
assert (
"{}\n{}".format(existing_section, init_basic_toml) in pyproject_file.read_text()
)
def test_init_existing_pyproject_with_build_system_fails(
tester, source_dir, init_basic_inputs
):
pyproject_file = source_dir / "pyproject.toml"
existing_section = """
[build-system]
requires = ["setuptools >= 40.6.0", "wheel"]
build-backend = "setuptools.build_meta"
"""
pyproject_file.write_text(decode(existing_section))
tester.execute(inputs=init_basic_inputs)
assert (
tester.io.fetch_output().strip()
== "A pyproject.toml file with a defined build-system already exists."
)
assert "{}".format(existing_section) in pyproject_file.read_text()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment