Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

poetry init uses existing pyproject.toml if possible #2448

Merged
merged 8 commits into from
Sep 29, 2020
21 changes: 16 additions & 5 deletions poetry/console/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from cleo import option
from tomlkit import inline_table

from poetry.core.pyproject.toml import PyProjectTOML
from poetry.utils._compat import OrderedDict
from poetry.utils._compat import Path
from poetry.utils._compat import urlparse
Expand All @@ -22,7 +23,6 @@


class InitCommand(Command):

name = "init"
description = (
"Creates a basic <comment>pyproject.toml</> file in the current directory."
Expand Down Expand Up @@ -67,9 +67,20 @@ def handle(self):
from poetry.utils._compat import Path
from poetry.utils.env import SystemEnv

if (Path.cwd() / "pyproject.toml").exists():
self.line("<error>A pyproject.toml file already exists.</error>")
return 1
pyproject = PyProjectTOML(Path.cwd() / "pyproject.toml")

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()

Expand Down Expand Up @@ -198,7 +209,7 @@ def handle(self):
dev_dependencies=dev_requirements,
)

content = layout_.generate_poetry_content()
content = layout_.generate_poetry_content(pyproject)
if self.io.is_interactive():
self.line("<info>Generated file</info>")
self.line("")
Expand Down
14 changes: 12 additions & 2 deletions poetry/layouts/layout.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from typing import TYPE_CHECKING

from tomlkit import dumps
from tomlkit import loads
from tomlkit import table

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__


Expand Down Expand Up @@ -81,7 +86,7 @@ def create(self, path, with_tests=True):

self._write_poetry(path)

def generate_poetry_content(self):
def generate_poetry_content(self, original_toml): # type: ("PyProjectTOML") -> str
template = POETRY_DEFAULT
if self._license:
template = POETRY_WITH_LICENSE
Expand Down Expand Up @@ -114,7 +119,12 @@ def generate_poetry_content(self):

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):
raise NotImplementedError()
Expand Down
82 changes: 64 additions & 18 deletions tests/console/commands/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,46 @@
import pytest

from poetry.utils._compat import Path
from poetry.utils._compat import decode
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)
def patches(mocker, mocked_open_files):
mocked_open_files.append("pyproject.toml")
def patches(mocker, source_dir):
patch = mocker.patch("poetry.utils._compat.Path.cwd")
patch.return_value = Path(__file__).parent
patch.return_value = source_dir


@pytest.fixture
def tester(command_tester_factory):
return command_tester_factory("init")


def test_basic_interactive(tester):
inputs = [
"my-package", # Package name
"1.2.3", # Version
"This is a description", # Description
"n", # Author
"MIT", # License
"~2.7 || ^3.6", # Python
"n", # Interactive packages
"n", # Interactive dev packages
"\n", # Generate
]
tester.execute(inputs="\n".join(inputs))
@pytest.fixture
def init_basic_inputs():
return "\n".join(
[
"my-package", # Package name
"1.2.3", # Version
"This is a description", # Description
"n", # Author
"MIT", # License
"~2.7 || ^3.6", # Python
"n", # Interactive packages
"n", # Interactive dev packages
"\n", # Generate
]
)

expected = """\

@pytest.fixture()
def init_basic_toml():
return """\
[tool.poetry]
name = "my-package"
version = "1.2.3"
Expand All @@ -46,7 +56,10 @@ def test_basic_interactive(tester):
[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):
Expand Down Expand Up @@ -565,3 +578,36 @@ def test_add_package_with_extras_and_whitespace(tester):
assert len(result[0]["extras"]) == 2
assert "postgresql" 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()