diff --git a/poetry/core/json/schemas/poetry-schema.json b/poetry/core/json/schemas/poetry-schema.json index 10aff39e5..8397ba8ab 100644 --- a/poetry/core/json/schemas/poetry-schema.json +++ b/poetry/core/json/schemas/poetry-schema.json @@ -326,6 +326,10 @@ "items": { "type": "string" } + }, + "develop": { + "type": "boolean", + "description": "Whether to install the dependency in development mode." } } }, diff --git a/poetry/core/packages/package.py b/poetry/core/packages/package.py index d5c822d25..28488ff69 100644 --- a/poetry/core/packages/package.py +++ b/poetry/core/packages/package.py @@ -299,6 +299,7 @@ def add_dependency( rev=constraint.get("rev", None), category=category, optional=optional, + develop=constraint.get("develop", True), ) elif "file" in constraint: file_path = Path(constraint["file"]) diff --git a/poetry/core/packages/vcs_dependency.py b/poetry/core/packages/vcs_dependency.py index 119afb64a..fd1ea14a8 100644 --- a/poetry/core/packages/vcs_dependency.py +++ b/poetry/core/packages/vcs_dependency.py @@ -18,6 +18,7 @@ def __init__( rev=None, category="main", optional=False, + develop=False, ): self._vcs = vcs self._source = source @@ -29,6 +30,7 @@ def __init__( self._branch = branch self._tag = tag self._rev = rev + self._develop = develop super(VCSDependency, self).__init__( name, "*", category=category, optional=optional, allows_prereleases=True @@ -54,6 +56,10 @@ def tag(self): def rev(self): return self._rev + @property + def develop(self): # type: () -> bool + return self._develop + @property def reference(self): # type: () -> str return self._branch or self._tag or self._rev diff --git a/tests/fixtures/complete.toml b/tests/fixtures/complete.toml new file mode 100644 index 000000000..a894a89c6 --- /dev/null +++ b/tests/fixtures/complete.toml @@ -0,0 +1,45 @@ +[tool.poetry] +name = "poetry" +version = "0.5.0" +description = "Python dependency management and packaging made easy." +authors = [ + "Sébastien Eustace " +] +license = "MIT" + +readme = "README.rst" + +homepage = "https://python-poetry.org/" +repository = "https://github.com/python-poetry/poetry" +documentation = "https://python-poetry.org/docs" + +keywords = ["packaging", "dependency", "poetry"] + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.2" # Compatible python versions must be declared here +toml = "^0.9" +# Dependencies with extras +requests = { version = "^2.13", extras = [ "security" ] } +# Python specific dependencies with prereleases allowed +pathlib2 = { version = "^2.2", python = "~2.7", allows-prereleases = true } +# Git dependencies +cleo = { git = "https://github.com/sdispater/cleo.git", branch = "master" } + +# Optional dependencies (extras) +pendulum = { version = "^1.4", optional = true } + +[tool.poetry.extras] +time = [ "pendulum" ] + +[tool.poetry.dev-dependencies] +pytest = "^3.0" +pytest-cov = "^2.4" + +[tool.poetry.scripts] +my-script = 'my_package:main' + + +[[tool.poetry.source]] +name = "foo" +url = "https://bar.com" diff --git a/tests/fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl b/tests/fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl new file mode 100644 index 000000000..9e0805593 Binary files /dev/null and b/tests/fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl differ diff --git a/tests/fixtures/distributions/demo-0.1.0.tar.gz b/tests/fixtures/distributions/demo-0.1.0.tar.gz new file mode 100644 index 000000000..133b64421 Binary files /dev/null and b/tests/fixtures/distributions/demo-0.1.0.tar.gz differ diff --git a/tests/fixtures/invalid_pyproject/pyproject.toml b/tests/fixtures/invalid_pyproject/pyproject.toml new file mode 100644 index 000000000..06a8e27da --- /dev/null +++ b/tests/fixtures/invalid_pyproject/pyproject.toml @@ -0,0 +1,11 @@ +[tool.poetry] +name = "invalid" +version = "1.0.0" +authors = [ + "Foo " +] +license = "INVALID" + +[tool.poetry.dependencies] +python = "*" +pendulum = {"version" = "^2.0.5", allows-prereleases = true} diff --git a/tests/fixtures/project_with_setup/my_package/__init__.py b/tests/fixtures/project_with_setup/my_package/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/fixtures/project_with_setup/setup.py b/tests/fixtures/project_with_setup/setup.py new file mode 100644 index 000000000..71b3074d8 --- /dev/null +++ b/tests/fixtures/project_with_setup/setup.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from setuptools import setup + + +kwargs = dict( + name="my-package", + license="MIT", + version="0.1.2", + description="Demo project.", + author="Sébastien Eustace", + author_email="sebastien@eustace.io", + url="https://github.com/demo/demo", + packages=["my_package"], + install_requires=["pendulum>=1.4.4", "cachy[msgpack]>=0.2.0"], +) + + +setup(**kwargs) diff --git a/tests/fixtures/sample_project/README.rst b/tests/fixtures/sample_project/README.rst new file mode 100644 index 000000000..f7fe15470 --- /dev/null +++ b/tests/fixtures/sample_project/README.rst @@ -0,0 +1,2 @@ +My Package +========== diff --git a/tests/fixtures/sample_project/pyproject.toml b/tests/fixtures/sample_project/pyproject.toml new file mode 100644 index 000000000..a26d33087 --- /dev/null +++ b/tests/fixtures/sample_project/pyproject.toml @@ -0,0 +1,59 @@ +[tool.poetry] +name = "my-package" +version = "1.2.3" +description = "Some description." +authors = [ + "Sébastien Eustace " +] +license = "MIT" + +readme = "README.rst" + +homepage = "https://python-poetry.org" +repository = "https://github.com/python-poetry/poetry" +documentation = "https://python-poetry.org/docs" + +keywords = ["packaging", "dependency", "poetry"] + +classifiers = [ + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules" +] + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.6" +cleo = "^0.6" +pendulum = { git = "https://github.com/sdispater/pendulum.git", branch = "2.0" } +tomlkit = { git = "https://github.com/sdispater/tomlkit.git", rev = "3bff550", develop = false } +requests = { version = "^2.18", optional = true, extras=[ "security" ] } +pathlib2 = { version = "^2.2", python = "~2.7" } + +orator = { version = "^0.9", optional = true } + +# File dependency +demo = { path = "../distributions/demo-0.1.0-py2.py3-none-any.whl" } + +# Dir dependency with setup.py +my-package = { path = "../project_with_setup/" } + +# Dir dependency with pyproject.toml +simple-project = { path = "../simple_project/" } + +# Dependency with markers +functools32 = { version = "^3.2.3", markers = "python_version ~= '2.7' and sys_platform == 'win32' or python_version in '3.4 3.5'" } + + +[tool.poetry.extras] +db = [ "orator" ] + +[tool.poetry.dev-dependencies] +pytest = "~3.4" + + +[tool.poetry.scripts] +my-script = "my_package:main" + + +[tool.poetry.plugins."blogtool.parsers"] +".rst" = "some_module::SomeClass" diff --git a/tests/fixtures/simple_project/README.rst b/tests/fixtures/simple_project/README.rst new file mode 100644 index 000000000..f7fe15470 --- /dev/null +++ b/tests/fixtures/simple_project/README.rst @@ -0,0 +1,2 @@ +My Package +========== diff --git a/tests/fixtures/simple_project/dist/simple-project-1.2.3.tar.gz b/tests/fixtures/simple_project/dist/simple-project-1.2.3.tar.gz new file mode 100644 index 000000000..149aa9527 Binary files /dev/null and b/tests/fixtures/simple_project/dist/simple-project-1.2.3.tar.gz differ diff --git a/tests/fixtures/simple_project/dist/simple_project-1.2.3-py2.py3-none-any.whl b/tests/fixtures/simple_project/dist/simple_project-1.2.3-py2.py3-none-any.whl new file mode 100644 index 000000000..fcdeda313 Binary files /dev/null and b/tests/fixtures/simple_project/dist/simple_project-1.2.3-py2.py3-none-any.whl differ diff --git a/tests/fixtures/simple_project/pyproject.toml b/tests/fixtures/simple_project/pyproject.toml new file mode 100644 index 000000000..72d0c0bee --- /dev/null +++ b/tests/fixtures/simple_project/pyproject.toml @@ -0,0 +1,25 @@ +[tool.poetry] +name = "simple-project" +version = "1.2.3" +description = "Some description." +authors = [ + "Sébastien Eustace " +] +license = "MIT" + +readme = "README.rst" + +homepage = "https://python-poetry.org" +repository = "https://github.com/python-poetry/poetry" +documentation = "https://python-poetry.org/docs" + +keywords = ["packaging", "dependency", "poetry"] + +classifiers = [ + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules" +] + +# Requirements +[tool.poetry.dependencies] +python = "~2.7 || ^3.4" diff --git a/tests/fixtures/simple_project/simple_project/__init__.py b/tests/fixtures/simple_project/simple_project/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_factory.py b/tests/test_factory.py new file mode 100644 index 000000000..af15b627f --- /dev/null +++ b/tests/test_factory.py @@ -0,0 +1,197 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import +from __future__ import unicode_literals + +import pytest + +from poetry.core.factory import Factory +from poetry.core.utils._compat import PY2 +from poetry.core.utils._compat import Path +from poetry.core.utils.toml_file import TomlFile + + +fixtures_dir = Path(__file__).parent / "fixtures" + + +def test_create_poetry(): + poetry = Factory().create_poetry(fixtures_dir / "sample_project") + + package = poetry.package + + assert package.name == "my-package" + assert package.version.text == "1.2.3" + assert package.description == "Some description." + assert package.authors == ["Sébastien Eustace "] + assert package.license.id == "MIT" + assert ( + package.readme.relative_to(fixtures_dir).as_posix() + == "sample_project/README.rst" + ) + assert package.homepage == "https://python-poetry.org" + assert package.repository_url == "https://github.com/python-poetry/poetry" + assert package.keywords == ["packaging", "dependency", "poetry"] + + assert package.python_versions == "~2.7 || ^3.6" + assert str(package.python_constraint) == ">=2.7,<2.8 || >=3.6,<4.0" + + dependencies = {} + for dep in package.requires: + dependencies[dep.name] = dep + + cleo = dependencies["cleo"] + assert cleo.pretty_constraint == "^0.6" + assert not cleo.is_optional() + + pendulum = dependencies["pendulum"] + assert pendulum.pretty_constraint == "branch 2.0" + assert pendulum.is_vcs() + assert pendulum.vcs == "git" + assert pendulum.branch == "2.0" + assert pendulum.source == "https://github.com/sdispater/pendulum.git" + assert pendulum.allows_prereleases() + assert pendulum.develop + + tomlkit = dependencies["tomlkit"] + assert tomlkit.pretty_constraint == "rev 3bff550" + assert tomlkit.is_vcs() + assert tomlkit.vcs == "git" + assert tomlkit.rev == "3bff550" + assert tomlkit.source == "https://github.com/sdispater/tomlkit.git" + assert tomlkit.allows_prereleases() + assert not tomlkit.develop + + requests = dependencies["requests"] + assert requests.pretty_constraint == "^2.18" + assert not requests.is_vcs() + assert not requests.allows_prereleases() + assert requests.is_optional() + assert requests.extras == ["security"] + + pathlib2 = dependencies["pathlib2"] + assert pathlib2.pretty_constraint == "^2.2" + assert pathlib2.python_versions == "~2.7" + assert not pathlib2.is_optional() + + demo = dependencies["demo"] + assert demo.is_file() + assert not demo.is_vcs() + assert demo.name == "demo" + assert demo.pretty_constraint == "*" + + demo = dependencies["my-package"] + assert not demo.is_file() + assert demo.is_directory() + assert not demo.is_vcs() + assert demo.name == "my-package" + assert demo.pretty_constraint == "*" + + simple_project = dependencies["simple-project"] + assert not simple_project.is_file() + assert simple_project.is_directory() + assert not simple_project.is_vcs() + assert simple_project.name == "simple-project" + assert simple_project.pretty_constraint == "*" + + functools32 = dependencies["functools32"] + assert functools32.name == "functools32" + assert functools32.pretty_constraint == "^3.2.3" + assert ( + str(functools32.marker) + == 'python_version ~= "2.7" and sys_platform == "win32" or python_version in "3.4 3.5"' + ) + + assert "db" in package.extras + + classifiers = package.classifiers + + assert classifiers == [ + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules", + ] + + assert package.all_classifiers == [ + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Topic :: Software Development :: Build Tools", + "Topic :: Software Development :: Libraries :: Python Modules", + ] + + +def test_create_poetry_with_packages_and_includes(): + poetry = Factory().create_poetry( + fixtures_dir.parent / "masonry" / "builders" / "fixtures" / "with-include" + ) + + package = poetry.package + + assert package.packages == [ + {"include": "extra_dir/**/*.py"}, + {"include": "extra_dir/**/*.py"}, + {"include": "my_module.py"}, + {"include": "package_with_include"}, + {"include": "tests", "format": "sdist"}, + {"include": "for_wheel_only", "format": ["wheel"]}, + {"include": "src_package", "from": "src"}, + ] + + assert package.include == ["extra_dir/vcs_excluded.txt", "notes.txt"] + + +def test_create_poetry_with_multi_constraints_dependency(): + poetry = Factory().create_poetry( + fixtures_dir / "project_with_multi_constraints_dependency" + ) + + package = poetry.package + + assert len(package.requires) == 2 + + +def test_validate(): + complete = TomlFile(fixtures_dir / "complete.toml") + content = complete.read()["tool"]["poetry"] + + assert Factory.validate(content) == {"errors": [], "warnings": []} + + +def test_validate_fails(): + complete = TomlFile(fixtures_dir / "complete.toml") + content = complete.read()["tool"]["poetry"] + content["this key is not in the schema"] = "" + + if PY2: + expected = ( + "Additional properties are not allowed " + "(u'this key is not in the schema' was unexpected)" + ) + else: + expected = ( + "Additional properties are not allowed " + "('this key is not in the schema' was unexpected)" + ) + + assert Factory.validate(content) == {"errors": [expected], "warnings": []} + + +def test_create_poetry_fails_on_invalid_configuration(): + with pytest.raises(RuntimeError) as e: + Factory().create_poetry( + Path(__file__).parent / "fixtures" / "invalid_pyproject" / "pyproject.toml" + ) + + if PY2: + expected = """\ +The Poetry configuration is invalid: + - u'description' is a required property +""" + else: + expected = """\ +The Poetry configuration is invalid: + - 'description' is a required property +""" + assert expected == str(e.value)