diff --git a/poetry/console/commands/init.py b/poetry/console/commands/init.py index 606dad2870a..8f96b12ca18 100644 --- a/poetry/console/commands/init.py +++ b/poetry/console/commands/init.py @@ -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 @@ -22,7 +23,6 @@ class InitCommand(Command): - name = "init" description = ( "Creates a basic pyproject.toml file in the current directory." @@ -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("A pyproject.toml file already exists.") - return 1 + pyproject = PyProjectTOML(Path.cwd() / "pyproject.toml") + + if pyproject.file.exists(): + if pyproject.is_poetry_project(): + self.line( + "A pyproject.toml file with a poetry section already exists." + ) + return 1 + + if pyproject.data.get("build-system"): + self.line( + "A pyproject.toml file with a defined build-system already exists." + ) + return 1 vcs_config = GitConfig() @@ -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("Generated file") self.line("") diff --git a/poetry/layouts/layout.py b/poetry/layouts/layout.py index a7378c67c4c..1d0feccc224 100644 --- a/poetry/layouts/layout.py +++ b/poetry/layouts/layout.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING + from tomlkit import dumps from tomlkit import loads from tomlkit import table @@ -5,6 +7,9 @@ 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__ @@ -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 @@ -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() diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index d367fbc8b8e..c64bc23316a 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -3,14 +3,19 @@ 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 @@ -18,21 +23,26 @@ 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" @@ -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): @@ -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()