From 2604a73a981accd4284261bfae12efb58e22028e Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 4 Aug 2020 16:54:49 +0200 Subject: [PATCH 1/7] layouts: improve supported layout creation - remove unnecessary template duplication - stop generating default test case (this is out of scope for poetry) - stop generating content for package `__init__.py` - simplify inheritance to only require base directory overrides - remove "StandardLayout" (replaced with `Layout`) - support handling namespace package names given in the form "a.b.c" - specify include packages by default (be explicit) --- poetry/layouts/__init__.py | 3 +- poetry/layouts/layout.py | 102 +++++++++++++++++----------- poetry/layouts/src.py | 21 ++---- poetry/layouts/standard.py | 21 ------ tests/console/commands/test_init.py | 11 ++- 5 files changed, 77 insertions(+), 81 deletions(-) diff --git a/poetry/layouts/__init__.py b/poetry/layouts/__init__.py index 291319e82c1..f7ddf8c4def 100644 --- a/poetry/layouts/__init__.py +++ b/poetry/layouts/__init__.py @@ -2,10 +2,9 @@ from .layout import Layout from .src import SrcLayout -from .standard import StandardLayout -_LAYOUTS = {"src": SrcLayout, "standard": StandardLayout} +_LAYOUTS = {"src": SrcLayout, "standard": Layout} def layout(name: str) -> Type[Layout]: diff --git a/poetry/layouts/layout.py b/poetry/layouts/layout.py index dfc2e609c95..0f0ef4bef4b 100644 --- a/poetry/layouts/layout.py +++ b/poetry/layouts/layout.py @@ -1,26 +1,22 @@ +from pathlib import Path from typing import TYPE_CHECKING from typing import Dict from typing import Optional from tomlkit import dumps +from tomlkit import inline_table from tomlkit import loads from tomlkit import table +from poetry.utils.helpers import canonicalize_name from poetry.utils.helpers import module_name if TYPE_CHECKING: - from pathlib import Path + from tomlkit.items import InlineTable from poetry.core.pyproject.toml import PyProjectTOML -TESTS_DEFAULT = """from {package_name} import __version__ - - -def test_version(): - assert __version__ == '{version}' -""" - POETRY_DEFAULT = """\ [tool.poetry] @@ -28,26 +24,15 @@ def test_version(): version = "" description = "" authors = [] - -[tool.poetry.dependencies] - -[tool.poetry.dev-dependencies] -""" - -POETRY_WITH_LICENSE = """\ -[tool.poetry] -name = "" -version = "" -description = "" -authors = [] license = "" +packages = [] [tool.poetry.dependencies] [tool.poetry.dev-dependencies] """ -BUILD_SYSTEM_MIN_VERSION = "1.0.0" +BUILD_SYSTEM_MIN_VERSION: Optional[str] = None BUILD_SYSTEM_MAX_VERSION: Optional[str] = None @@ -59,13 +44,16 @@ def __init__( description: str = "", readme_format: str = "md", author: Optional[str] = None, - license: Optional[str] = None, + license: Optional[str] = None, # noqa python: str = "*", dependencies: Optional[Dict[str, str]] = None, dev_dependencies: Optional[Dict[str, str]] = None, ): - self._project = project - self._package_name = module_name(project) + self._project = canonicalize_name(project).replace(".", "-") + self._package_path_relative = Path( + *(module_name(part) for part in canonicalize_name(project).split(".")) + ) + self._package_name = ".".join(self._package_path_relative.parts) self._version = version self._description = description self._readme_format = readme_format @@ -79,6 +67,30 @@ def __init__( self._author = author + @property + def basedir(self) -> Path: + return Path() + + @property + def package_path(self) -> Path: + return self.basedir / self._package_path_relative + + def get_package_include(self) -> Optional["InlineTable"]: + package = inline_table() + + include = self._package_path_relative.parts[0] + package.append("include", include) + + if self.basedir != Path(): + package.append("from", self.basedir.as_posix()) + else: + if include == self._project: + # package include and package name are the same, + # packages table is redundant here. + return None + + return package + def create(self, path: "Path", with_tests: bool = True) -> None: path.mkdir(parents=True, exist_ok=True) @@ -94,17 +106,25 @@ def generate_poetry_content( self, original: Optional["PyProjectTOML"] = None ) -> str: template = POETRY_DEFAULT - if self._license: - template = POETRY_WITH_LICENSE content = loads(template) + poetry_content = content["tool"]["poetry"] poetry_content["name"] = self._project poetry_content["version"] = self._version poetry_content["description"] = self._description poetry_content["authors"].append(self._author) + if self._license: poetry_content["license"] = self._license + else: + poetry_content.remove("license") + + packages = self.get_package_include() + if packages: + poetry_content["packages"].append(packages) + else: + poetry_content.remove("packages") poetry_content["dependencies"]["python"] = self._python @@ -116,9 +136,14 @@ def generate_poetry_content( # Add build system build_system = table() - build_system_version = ">=" + BUILD_SYSTEM_MIN_VERSION + build_system_version = "" + + if BUILD_SYSTEM_MIN_VERSION is not None: + build_system_version = ">=" + BUILD_SYSTEM_MIN_VERSION if BUILD_SYSTEM_MAX_VERSION is not None: - build_system_version += ",<" + BUILD_SYSTEM_MAX_VERSION + if build_system_version: + build_system_version += "," + build_system_version += "<" + BUILD_SYSTEM_MAX_VERSION build_system.add("requires", ["poetry-core" + build_system_version]) build_system.add("build-backend", "poetry.core.masonry.api") @@ -133,7 +158,11 @@ def generate_poetry_content( return content def _create_default(self, path: "Path", src: bool = True) -> None: - raise NotImplementedError() + package_path = path / self.package_path + package_path.mkdir(parents=True) + + package_init = package_path / "__init__.py" + package_init.touch() def _create_readme(self, path: "Path") -> None: if self._readme_format == "rst": @@ -143,20 +172,13 @@ def _create_readme(self, path: "Path") -> None: readme_file.touch() - def _create_tests(self, path: "Path") -> None: + @staticmethod + def _create_tests(path: "Path") -> None: tests = path / "tests" - tests_init = tests / "__init__.py" - tests_default = tests / f"test_{self._package_name}.py" - tests.mkdir() - tests_init.touch(exist_ok=False) - with tests_default.open("w", encoding="utf-8") as f: - f.write( - TESTS_DEFAULT.format( - package_name=self._package_name, version=self._version - ) - ) + tests_init = tests / "__init__.py" + tests_init.touch(exist_ok=False) def _write_poetry(self, path: "Path") -> None: content = self.generate_poetry_content() diff --git a/poetry/layouts/src.py b/poetry/layouts/src.py index 4f43263ef93..6d10e63296b 100644 --- a/poetry/layouts/src.py +++ b/poetry/layouts/src.py @@ -1,22 +1,9 @@ -from typing import TYPE_CHECKING +from pathlib import Path from .layout import Layout -if TYPE_CHECKING: - from pathlib import Path - -DEFAULT = """__version__ = '{version}' -""" - - class SrcLayout(Layout): - def _create_default(self, path: "Path") -> None: - package_path = path / "src" / self._package_name - - package_init = package_path / "__init__.py" - - package_path.mkdir(parents=True) - - with package_init.open("w", encoding="utf-8") as f: - f.write(DEFAULT.format(version=self._version)) + @property + def basedir(self) -> "Path": + return Path("src") diff --git a/poetry/layouts/standard.py b/poetry/layouts/standard.py index 9971d4aed8d..e69de29bb2d 100644 --- a/poetry/layouts/standard.py +++ b/poetry/layouts/standard.py @@ -1,21 +0,0 @@ -from typing import TYPE_CHECKING - -from .layout import Layout - - -if TYPE_CHECKING: - from pathlib import Path -DEFAULT = """__version__ = '{version}' -""" - - -class StandardLayout(Layout): - def _create_default(self, path: "Path") -> None: - package_path = path / self._package_name - - package_init = package_path / "__init__.py" - - package_path.mkdir() - - with package_init.open("w", encoding="utf-8") as f: - f.write(DEFAULT.format(version=self._version)) diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index 087c3f2088b..e7cab36d4b7 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -66,6 +66,7 @@ def init_basic_toml(): description = "This is a description" authors = ["Your Name "] license = "MIT" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" @@ -112,6 +113,7 @@ def test_interactive_with_dependencies(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" @@ -144,6 +146,7 @@ def test_empty_license(tester): version = "1.2.3" description = "" authors = ["Your Name "] +packages = [{{include = "my_package"}}] [tool.poetry.dependencies] python = "^{python}" @@ -152,7 +155,6 @@ def test_empty_license(tester): """.format( python=".".join(str(c) for c in sys.version_info[:2]) ) - assert expected in tester.io.fetch_output() @@ -186,6 +188,7 @@ def test_interactive_with_git_dependencies(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" @@ -228,6 +231,7 @@ def test_interactive_with_git_dependencies_with_reference(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" @@ -270,6 +274,7 @@ def test_interactive_with_git_dependencies_and_other_name(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" @@ -315,6 +320,7 @@ def test_interactive_with_directory_dependency(tester, repo, source_dir, fixture description = "This is a description" authors = ["Your Name "] license = "MIT" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" @@ -361,6 +367,7 @@ def test_interactive_with_directory_dependency_and_other_name( description = "This is a description" authors = ["Your Name "] license = "MIT" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" @@ -406,6 +413,7 @@ def test_interactive_with_file_dependency(tester, repo, source_dir, fixture_dir) description = "This is a description" authors = ["Your Name "] license = "MIT" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" @@ -471,6 +479,7 @@ def test_predefined_dependency(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" From 5a60e1083c0be985274f4bc7c5e931dbee5fbc08 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 4 Aug 2020 17:00:18 +0200 Subject: [PATCH 2/7] command/new: support namespace package creation With this change, names containing "." are treated as namespace packages. The following behaviour is now expected. - "a.b.c" creates package name "a-b-c" with directory structure "a/b/c" - "a-b-c" creates package name "a-b-c" with directory structure "a/b/c" - "a.b_c" creates package name "a-b-c" with directory structure "a/b_c" - "a_b_c" creates package name "a-b-c" with directory structure "a_b_c" --- docs/docs/cli.md | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/docs/docs/cli.md b/docs/docs/cli.md index 64cf17941e5..fa945f413b0 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -35,8 +35,7 @@ my-package ├── my_package │ └── __init__.py └── tests - ├── __init__.py - └── test_my_package.py + └── __init__.py ``` If you want to name your project differently than the folder, you can pass @@ -62,8 +61,27 @@ my-package │ └── my_package │ └── __init__.py └── tests - ├── __init__.py - └── test_my_package.py + └── __init__.py +``` + +The `--name` option is smart enough to detect namespace packages and create +the required structure for you. + +```bash +poetry new --src --name my.package my-package +``` + +will create the following structure: + +```text +my-package +├── pyproject.toml +├── src +│ └── my +│ └── package +│ └── __init__.py +└── tests + └── __init__.py ``` ## init From 90818317d67db4cb8307fea22e9747276f9139ff Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 4 Aug 2020 17:00:57 +0200 Subject: [PATCH 3/7] command/new: do not add pytest dependency to project --- poetry/console/commands/new.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/poetry/console/commands/new.py b/poetry/console/commands/new.py index 04c3a8ba653..786e0be8185 100644 --- a/poetry/console/commands/new.py +++ b/poetry/console/commands/new.py @@ -22,7 +22,6 @@ class NewCommand(Command): def handle(self) -> None: from pathlib import Path - from poetry.core.semver.helpers import parse_constraint from poetry.core.vcs.git import GitConfig from poetry.layouts import layout from poetry.utils.env import SystemEnv @@ -60,20 +59,12 @@ def handle(self) -> None: ".".join(str(v) for v in current_env.version_info[:2]) ) - dev_dependencies = {} - python_constraint = parse_constraint(default_python) - if parse_constraint("<3.5").allows_any(python_constraint): - dev_dependencies["pytest"] = "^4.6" - if parse_constraint(">=3.5").allows_all(python_constraint): - dev_dependencies["pytest"] = "^5.2" - layout_ = layout_( name, "0.1.0", author=author, readme_format=readme_format, python=default_python, - dev_dependencies=dev_dependencies, ) layout_.create(path) From 69245d744d2736f90d44c8ffe05052587f216614 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 4 Aug 2020 19:10:38 +0200 Subject: [PATCH 4/7] command/new: handle relative paths properly --- poetry/console/commands/new.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/poetry/console/commands/new.py b/poetry/console/commands/new.py index 786e0be8185..7e40b48414c 100644 --- a/poetry/console/commands/new.py +++ b/poetry/console/commands/new.py @@ -3,8 +3,6 @@ from cleo.helpers import argument from cleo.helpers import option -from poetry.utils.helpers import module_name - from .command import Command @@ -31,7 +29,11 @@ def handle(self) -> None: else: layout_ = layout("standard") - path = Path.cwd() / Path(self.argument("path")) + path = Path(self.argument("path")) + if not path.is_absolute(): + # we do not use resolve here due to compatibility issues for path.resolve(strict=False) + path = Path.cwd().joinpath(path) + name = self.option("name") if not name: name = path.name @@ -68,8 +70,15 @@ def handle(self) -> None: ) layout_.create(path) + path = path.resolve() + + try: + path = path.relative_to(Path.cwd()) + except ValueError: + pass + self.line( "Created package {} in {}".format( - module_name(name), path.relative_to(Path.cwd()) + layout_._package_name, path.as_posix() # noqa ) ) From add61e9531966821729266b06acb42b291110785 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 4 Aug 2020 19:11:11 +0200 Subject: [PATCH 5/7] command/new: add test coverage --- tests/console/commands/test_new.py | 142 +++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 tests/console/commands/test_new.py diff --git a/tests/console/commands/test_new.py b/tests/console/commands/test_new.py new file mode 100644 index 00000000000..1ac42facd04 --- /dev/null +++ b/tests/console/commands/test_new.py @@ -0,0 +1,142 @@ +import pytest + +from cleo.testers import CommandTester + +from poetry.console import Application +from poetry.factory import Factory +from poetry.poetry import Poetry +from poetry.utils._compat import Path # noqa + + +@pytest.fixture +def command(app, poetry): # type: (Application, Poetry) -> CommandTester + command = app.find("new") + command._pool = poetry.pool + return CommandTester(command) + + +def verify_project_directory(path, package_name, package_path, include_from=None): + package_path = Path(package_path) + assert path.is_dir() + + pyproject = path / "pyproject.toml" + assert pyproject.is_file() + + init_file = path / package_path / "__init__.py" + assert init_file.is_file() + + tests_init_file = path / "tests" / "__init__.py" + assert tests_init_file.is_file() + + poetry = Factory().create_poetry(cwd=path) + assert poetry.package.name == package_name + + if include_from: + package_include = { + "include": package_path.relative_to(include_from).parts[0], + "from": include_from, + } + else: + package_include = {"include": package_path.parts[0]} + + packages = poetry.local_config.get("packages") + + if not packages: + assert poetry.local_config.get("name") == package_include.get("include") + else: + assert len(packages) == 1 + assert packages[0] == package_include + + +@pytest.mark.parametrize( + "options,directory,package_name,package_path,include_from", + [ + ([], "package", "package", "package", None), + (["--src"], "package", "package", "src/package", "src"), + ( + ["--name namespace.package"], + "namespace-package", + "namespace-package", + "namespace/package", + None, + ), + ( + ["--src", "--name namespace.package"], + "namespace-package", + "namespace-package", + "src/namespace/package", + "src", + ), + ( + ["--name namespace.package_a"], + "namespace-package_a", + "namespace-package-a", + "namespace/package_a", + None, + ), + ( + ["--src", "--name namespace.package_a"], + "namespace-package_a", + "namespace-package-a", + "src/namespace/package_a", + "src", + ), + ( + ["--name namespace_package"], + "namespace-package", + "namespace-package", + "namespace_package", + None, + ), + ( + ["--name namespace_package", "--src"], + "namespace-package", + "namespace-package", + "src/namespace_package", + "src", + ), + ( + ["--name namespace.package"], + "package", + "namespace-package", + "namespace/package", + None, + ), + ( + ["--name namespace.package", "--src"], + "package", + "namespace-package", + "src/namespace/package", + "src", + ), + ( + ["--name namespace.package"], + "package", + "namespace-package", + "namespace/package", + None, + ), + ( + ["--name namespace.package", "--src"], + "package", + "namespace-package", + "src/namespace/package", + "src", + ), + ([], "namespace_package", "namespace-package", "namespace_package", None), + ( + ["--src", "--name namespace_package"], + "namespace_package", + "namespace-package", + "src/namespace_package", + "src", + ), + ], +) +def test_command_new( + options, directory, package_name, package_path, include_from, command, tmp_dir +): + path = Path(tmp_dir) / directory + options.append(path.as_posix()) + command.execute(" ".join(options)) + verify_project_directory(path, package_name, package_path, include_from) From 7ce029e0f70e4acad7d54457fd38114b2b606f95 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Thu, 6 Aug 2020 23:57:04 +0200 Subject: [PATCH 6/7] layouts: add readme to pyproject.toml on creation Relates-to: #280 Co-authored-by: Arun Babu Neelicattu --- poetry/layouts/layout.py | 23 ++++++++++++++++------- tests/console/commands/test_init.py | 22 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/poetry/layouts/layout.py b/poetry/layouts/layout.py index 0f0ef4bef4b..f4610564582 100644 --- a/poetry/layouts/layout.py +++ b/poetry/layouts/layout.py @@ -25,6 +25,7 @@ description = "" authors = [] license = "" +readme = "" packages = [] [tool.poetry.dependencies] @@ -37,6 +38,8 @@ class Layout: + ACCEPTED_README_FORMATS = {"md", "rst"} + def __init__( self, project: str, @@ -56,7 +59,15 @@ def __init__( self._package_name = ".".join(self._package_path_relative.parts) self._version = version self._description = description - self._readme_format = readme_format + + self._readme_format = readme_format.lower() + if self._readme_format not in self.ACCEPTED_README_FORMATS: + raise ValueError( + "Invalid readme format '{}', use one of {}.".format( + readme_format, ", ".join(self.ACCEPTED_README_FORMATS) + ) + ) + self._license = license self._python = python self._dependencies = dependencies or {} @@ -120,6 +131,7 @@ def generate_poetry_content( else: poetry_content.remove("license") + poetry_content["readme"] = "README.{}".format(self._readme_format) packages = self.get_package_include() if packages: poetry_content["packages"].append(packages) @@ -164,13 +176,10 @@ def _create_default(self, path: "Path", src: bool = True) -> None: package_init = package_path / "__init__.py" package_init.touch() - def _create_readme(self, path: "Path") -> None: - if self._readme_format == "rst": - readme_file = path / "README.rst" - else: - readme_file = path / "README.md" - + def _create_readme(self, path: "Path") -> "Path": + readme_file = path.joinpath("README.{}".format(self._readme_format)) readme_file.touch() + return readme_file @staticmethod def _create_tests(path: "Path") -> None: diff --git a/tests/console/commands/test_init.py b/tests/console/commands/test_init.py index e7cab36d4b7..4ae13465399 100644 --- a/tests/console/commands/test_init.py +++ b/tests/console/commands/test_init.py @@ -66,6 +66,7 @@ def init_basic_toml(): description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" packages = [{include = "my_package"}] [tool.poetry.dependencies] @@ -113,6 +114,7 @@ def test_interactive_with_dependencies(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" packages = [{include = "my_package"}] [tool.poetry.dependencies] @@ -146,6 +148,7 @@ def test_empty_license(tester): version = "1.2.3" description = "" authors = ["Your Name "] +readme = "README.md" packages = [{{include = "my_package"}}] [tool.poetry.dependencies] @@ -188,6 +191,7 @@ def test_interactive_with_git_dependencies(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" packages = [{include = "my_package"}] [tool.poetry.dependencies] @@ -231,6 +235,7 @@ def test_interactive_with_git_dependencies_with_reference(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" packages = [{include = "my_package"}] [tool.poetry.dependencies] @@ -274,6 +279,7 @@ def test_interactive_with_git_dependencies_and_other_name(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" packages = [{include = "my_package"}] [tool.poetry.dependencies] @@ -320,6 +326,7 @@ def test_interactive_with_directory_dependency(tester, repo, source_dir, fixture description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" packages = [{include = "my_package"}] [tool.poetry.dependencies] @@ -367,6 +374,7 @@ def test_interactive_with_directory_dependency_and_other_name( description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" packages = [{include = "my_package"}] [tool.poetry.dependencies] @@ -413,6 +421,7 @@ def test_interactive_with_file_dependency(tester, repo, source_dir, fixture_dir) description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" packages = [{include = "my_package"}] [tool.poetry.dependencies] @@ -446,6 +455,8 @@ def test_python_option(tester): description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" @@ -479,6 +490,7 @@ def test_predefined_dependency(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" packages = [{include = "my_package"}] [tool.poetry.dependencies] @@ -520,6 +532,8 @@ def test_predefined_and_interactive_dependencies(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" @@ -554,6 +568,8 @@ def test_predefined_dev_dependency(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" @@ -594,11 +610,15 @@ def test_predefined_and_interactive_dev_dependencies(tester, repo): description = "This is a description" authors = ["Your Name "] license = "MIT" +readme = "README.md" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "~2.7 || ^3.6" [tool.poetry.dev-dependencies] +pytest = "^3.6.0" +pytest-requests = "^0.2.0" """ output = tester.io.fetch_output() @@ -657,6 +677,8 @@ def test_init_non_interactive_existing_pyproject_add_dependency( version = "0.1.0" description = "" authors = ["Your Name "] +readme = "README.md" +packages = [{include = "my_package"}] [tool.poetry.dependencies] python = "^3.6" From 152d720e45b4d281ab5f719825f9746a8a86a8d4 Mon Sep 17 00:00:00 2001 From: finswimmer Date: Fri, 7 Aug 2020 00:00:41 +0200 Subject: [PATCH 7/7] command/new: make readme format configurable This change makes readme formant configurable, defaulting to markdown when using the new command. Resolves: #280 Closes: #1515 Co-authored-by: Arun Babu Neelicattu --- docs/docs/cli.md | 5 +++-- poetry/console/commands/new.py | 8 ++++++- tests/console/commands/test_new.py | 35 ++++++++++++++++++++---------- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/docs/docs/cli.md b/docs/docs/cli.md index fa945f413b0..af7c8b1a81a 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -31,7 +31,7 @@ will create a folder as follows: ```text my-package ├── pyproject.toml -├── README.rst +├── README.md ├── my_package │ └── __init__.py └── tests @@ -56,7 +56,7 @@ That will create a folder structure as follows: ```text my-package ├── pyproject.toml -├── README.rst +├── README.md ├── src │ └── my_package │ └── __init__.py @@ -76,6 +76,7 @@ will create the following structure: ```text my-package ├── pyproject.toml +├── README.md ├── src │ └── my │ └── package diff --git a/poetry/console/commands/new.py b/poetry/console/commands/new.py index 7e40b48414c..8158442f741 100644 --- a/poetry/console/commands/new.py +++ b/poetry/console/commands/new.py @@ -15,6 +15,12 @@ class NewCommand(Command): options = [ option("name", None, "Set the resulting package name.", flag=False), option("src", None, "Use the src layout for the project."), + option( + "readme", + None, + "Specify the readme file format. One of md (default) or rst", + flag=False, + ), ] def handle(self) -> None: @@ -46,7 +52,7 @@ def handle(self) -> None: "exists and is not empty".format(path) ) - readme_format = "rst" + readme_format = self.option("readme") or "md" config = GitConfig() author = None diff --git a/tests/console/commands/test_new.py b/tests/console/commands/test_new.py index 1ac42facd04..0df5149ae0c 100644 --- a/tests/console/commands/test_new.py +++ b/tests/console/commands/test_new.py @@ -1,21 +1,20 @@ -import pytest +from pathlib import Path +from typing import Optional -from cleo.testers import CommandTester +import pytest -from poetry.console import Application from poetry.factory import Factory from poetry.poetry import Poetry -from poetry.utils._compat import Path # noqa @pytest.fixture -def command(app, poetry): # type: (Application, Poetry) -> CommandTester - command = app.find("new") - command._pool = poetry.pool - return CommandTester(command) +def tester(command_tester_factory): + return command_tester_factory("new") -def verify_project_directory(path, package_name, package_path, include_from=None): +def verify_project_directory( + path: Path, package_name: str, package_path: str, include_from: Optional[str] = None +) -> Poetry: package_path = Path(package_path) assert path.is_dir() @@ -47,6 +46,8 @@ def verify_project_directory(path, package_name, package_path, include_from=None assert len(packages) == 1 assert packages[0] == package_include + return poetry + @pytest.mark.parametrize( "options,directory,package_name,package_path,include_from", @@ -134,9 +135,21 @@ def verify_project_directory(path, package_name, package_path, include_from=None ], ) def test_command_new( - options, directory, package_name, package_path, include_from, command, tmp_dir + options, directory, package_name, package_path, include_from, tester, tmp_dir ): path = Path(tmp_dir) / directory options.append(path.as_posix()) - command.execute(" ".join(options)) + tester.execute(" ".join(options)) verify_project_directory(path, package_name, package_path, include_from) + + +@pytest.mark.parametrize("fmt", [(None,), ("md",), ("rst",)]) +def test_command_new_with_readme(fmt, tester, tmp_dir): + fmt = "md" + package = "package" + path = Path(tmp_dir) / package + options = ["--readme {}".format(fmt) if fmt else "md", path.as_posix()] + tester.execute(" ".join(options)) + + poetry = verify_project_directory(path, package, package, None) + assert poetry.local_config.get("readme") == "README.{}".format(fmt or "md")