diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b9a75affdb4..3e1ba6cda2a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ repos: -- repo: https://github.com/ambv/black +- repo: https://github.com/psf/black rev: stable hooks: - id: black diff --git a/docs/docs/pyproject.md b/docs/docs/pyproject.md index 679ad824a22..27926bea6fd 100644 --- a/docs/docs/pyproject.md +++ b/docs/docs/pyproject.md @@ -40,6 +40,12 @@ The recommended notation for the most common licenses is (alphabetical): Optional, but it is highly recommended to supply this. More identifiers are listed at the [SPDX Open Source License Registry](https://www.spdx.org/licenses/). +## license-files + +The license file(s) belonging to this package. **optional** + +Poetry will automatically include files matching `LICENSE*` and `COPYING*` but if you have licenses files that do not fit these patterns, you can configure `poetry` to include one or more files as the license files. These paths should be relative to the project root directory, where the `pyproject.toml` lives. + ## authors The authors of the package. **Required** diff --git a/poetry/json/schemas/poetry-schema.json b/poetry/json/schemas/poetry-schema.json index c02c93aff79..00d76ed6d8e 100644 --- a/poetry/json/schemas/poetry-schema.json +++ b/poetry/json/schemas/poetry-schema.json @@ -47,6 +47,9 @@ "type": "string", "description": "License name." }, + "license-files": { + "$ref": "#/definitions/license-files" + }, "authors": { "$ref": "#/definitions/authors" }, @@ -183,6 +186,13 @@ "type": "string" } }, + "license-files": { + "type": "array", + "description": "A list of license files applicable to this packages. This accepts multiple files for situations involving more complicated licensing situations.", + "items": { + "type": "string" + } + }, "maintainers": { "type": "array", "description": "List of maintainers, other than the original author(s), that upkeep the package.", diff --git a/poetry/masonry/builders/builder.py b/poetry/masonry/builders/builder.py index ef38540743f..79a67c489cc 100644 --- a/poetry/masonry/builders/builder.py +++ b/poetry/masonry/builders/builder.py @@ -146,8 +146,9 @@ def find_files_to_add(self, exclude_build=True): # type: (bool) -> list ) to_add.append(Path("pyproject.toml")) - # If a license file exists, add it - for license_file in self._path.glob("LICENSE*"): + # If any license files exist, add them. + for license_filename in self._package.license_files: + license_file = self._path / license_filename self._io.write_line( " - Adding: {}".format( license_file.relative_to(self._path) diff --git a/poetry/masonry/builders/sdist.py b/poetry/masonry/builders/sdist.py index 268f8f5c6f8..ae8381ba0a2 100644 --- a/poetry/masonry/builders/sdist.py +++ b/poetry/masonry/builders/sdist.py @@ -22,7 +22,7 @@ SETUP = """\ # -*- coding: utf-8 -*- -from distutils.core import setup +from setuptools import setup {before} setup_kwargs = {{ @@ -137,6 +137,12 @@ def build_setup(self): # type: () -> bytes else: pass + license_file_list = list(self._package.license_files) + if len(license_file_list) == 1: + extra.append("'license_file': \"{}\",".format(license_file_list[0])) + elif len(license_file_list) > 1: + extra.append("'license_file': \"{}\",".format(list(map(str, license_file_list)))) + if package_dir: before.append("package_dir = \\\n{}\n".format(pformat(package_dir))) extra.append("'package_dir': package_dir,") diff --git a/poetry/masonry/builders/wheel.py b/poetry/masonry/builders/wheel.py index 71423fefeb6..e281e1f5161 100644 --- a/poetry/masonry/builders/wheel.py +++ b/poetry/masonry/builders/wheel.py @@ -181,9 +181,9 @@ def _write_metadata(self, wheel): with self._write_to_zip(wheel, self.dist_info + "/entry_points.txt") as f: self._write_entry_points(f) - for base in ("COPYING", "LICENSE"): - for path in sorted(self._path.glob(base + "*")): - self._add_file(wheel, path, "%s/%s" % (self.dist_info, path.name)) + for license_file_name in self._package.license_files: + license_file = self._path / license_file_name + self._add_file(wheel, license_file, "%s/%s" % (self.dist_info, license_file.name)) with self._write_to_zip(wheel, self.dist_info + "/WHEEL") as f: self._write_wheel_file(f) diff --git a/poetry/packages/package.py b/poetry/packages/package.py index 648a476cfd4..d9f3ef513a8 100644 --- a/poetry/packages/package.py +++ b/poetry/packages/package.py @@ -53,6 +53,7 @@ def __init__(self, name, version, pretty_version=None): self.documentation_url = None self.keywords = [] self._license = None + self.license_files = set() self.readme = None self.source_name = "" diff --git a/poetry/poetry.py b/poetry/poetry.py index 3c53244830b..31249ae21b4 100644 --- a/poetry/poetry.py +++ b/poetry/poetry.py @@ -129,6 +129,16 @@ def create(cls, cwd): # type: (Path) -> Poetry if "readme" in local_config: package.readme = Path(poetry_file.parent) / local_config["readme"] + # Add any manually defined or automatically discovered license files. + if "license-files" in local_config: + for file in local_config["license-files"]: + package.license_files.add(Path(poetry_file.parent) / file) + else: + for license_file in poetry_file.parent.glob("LICENSE*"): + package.license_files.add(license_file.relative_to(poetry_file.parent)) + for license_file in poetry_file.parent.glob("COPYING*"): + package.license_files.add(license_file.relative_to(poetry_file.parent)) + if "platform" in local_config: package.platform = local_config["platform"] diff --git a/tests/masonry/builders/fixtures/__init__.py b/tests/masonry/builders/fixtures/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/README.rst b/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/README.rst new file mode 100644 index 00000000000..a7508bd515e --- /dev/null +++ b/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/README.rst @@ -0,0 +1,2 @@ +Module 1 +======== diff --git a/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/pyproject.toml b/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/pyproject.toml new file mode 100644 index 00000000000..1bcf1ccac41 --- /dev/null +++ b/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/pyproject.toml @@ -0,0 +1,18 @@ +[tool.poetry] +name = "the-package" +version = "4.5.6" +description = "Some description." +authors = [ + "Sébastien Eustace " +] +license-files = [ + "the-first-license.txt", + "the-second-license.txt" +] + + +readme = "README.rst" + + +[tool.poetry.dependencies] +python = "3.6" diff --git a/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/the-first-license.txt b/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/the-first-license.txt new file mode 100644 index 00000000000..44cf2b30e68 --- /dev/null +++ b/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/the-first-license.txt @@ -0,0 +1,20 @@ +Copyright (c) 2018 Sébastien Eustace + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/the-second-license.txt b/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/the-second-license.txt new file mode 100644 index 00000000000..44cf2b30e68 --- /dev/null +++ b/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/the-second-license.txt @@ -0,0 +1,20 @@ +Copyright (c) 2018 Sébastien Eustace + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/the_package/__init__.py b/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/the_package/__init__.py new file mode 100644 index 00000000000..7ef41c5d4ea --- /dev/null +++ b/tests/masonry/builders/fixtures/explicit-license-config-multiple-licenses/the_package/__init__.py @@ -0,0 +1,3 @@ +"""Example module""" + +__version__ = "0.1" diff --git a/tests/masonry/builders/fixtures/explicit-license-config-single-license/README.rst b/tests/masonry/builders/fixtures/explicit-license-config-single-license/README.rst new file mode 100644 index 00000000000..a7508bd515e --- /dev/null +++ b/tests/masonry/builders/fixtures/explicit-license-config-single-license/README.rst @@ -0,0 +1,2 @@ +Module 1 +======== diff --git a/tests/masonry/builders/fixtures/explicit-license-config-single-license/pyproject.toml b/tests/masonry/builders/fixtures/explicit-license-config-single-license/pyproject.toml new file mode 100644 index 00000000000..d6ff5c859ff --- /dev/null +++ b/tests/masonry/builders/fixtures/explicit-license-config-single-license/pyproject.toml @@ -0,0 +1,16 @@ +[tool.poetry] +name = "the-package" +version = "4.5.6" +description = "Some description." +authors = [ + "Sébastien Eustace " +] +license-files = [ + "the-license.txt" +] + +readme = "README.rst" + + +[tool.poetry.dependencies] +python = "3.6" diff --git a/tests/masonry/builders/fixtures/explicit-license-config-single-license/the-license.txt b/tests/masonry/builders/fixtures/explicit-license-config-single-license/the-license.txt new file mode 100644 index 00000000000..44cf2b30e68 --- /dev/null +++ b/tests/masonry/builders/fixtures/explicit-license-config-single-license/the-license.txt @@ -0,0 +1,20 @@ +Copyright (c) 2018 Sébastien Eustace + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/masonry/builders/fixtures/explicit-license-config-single-license/the_package/__init__.py b/tests/masonry/builders/fixtures/explicit-license-config-single-license/the_package/__init__.py new file mode 100644 index 00000000000..7ef41c5d4ea --- /dev/null +++ b/tests/masonry/builders/fixtures/explicit-license-config-single-license/the_package/__init__.py @@ -0,0 +1,3 @@ +"""Example module""" + +__version__ = "0.1" diff --git a/tests/masonry/builders/fixtures/multiple-licenses/COPYING.txt b/tests/masonry/builders/fixtures/multiple-licenses/COPYING.txt new file mode 100644 index 00000000000..44cf2b30e68 --- /dev/null +++ b/tests/masonry/builders/fixtures/multiple-licenses/COPYING.txt @@ -0,0 +1,20 @@ +Copyright (c) 2018 Sébastien Eustace + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/masonry/builders/fixtures/multiple-licenses/LICENSE.txt b/tests/masonry/builders/fixtures/multiple-licenses/LICENSE.txt new file mode 100644 index 00000000000..44cf2b30e68 --- /dev/null +++ b/tests/masonry/builders/fixtures/multiple-licenses/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2018 Sébastien Eustace + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/masonry/builders/fixtures/multiple-licenses/README.rst b/tests/masonry/builders/fixtures/multiple-licenses/README.rst new file mode 100644 index 00000000000..a7508bd515e --- /dev/null +++ b/tests/masonry/builders/fixtures/multiple-licenses/README.rst @@ -0,0 +1,2 @@ +Module 1 +======== diff --git a/tests/masonry/builders/fixtures/multiple-licenses/pyproject.toml b/tests/masonry/builders/fixtures/multiple-licenses/pyproject.toml new file mode 100644 index 00000000000..26a226218e0 --- /dev/null +++ b/tests/masonry/builders/fixtures/multiple-licenses/pyproject.toml @@ -0,0 +1,13 @@ +[tool.poetry] +name = "the-package" +version = "4.5.6" +description = "Some description." +authors = [ + "Sébastien Eustace " +] + +readme = "README.rst" + + +[tool.poetry.dependencies] +python = "3.6" diff --git a/tests/masonry/builders/fixtures/multiple-licenses/the_package/__init__.py b/tests/masonry/builders/fixtures/multiple-licenses/the_package/__init__.py new file mode 100644 index 00000000000..fdac64b9433 --- /dev/null +++ b/tests/masonry/builders/fixtures/multiple-licenses/the_package/__init__.py @@ -0,0 +1,3 @@ +"""Example module""" + +__version__ = "4.5.6" diff --git a/tests/masonry/builders/test_complete.py b/tests/masonry/builders/test_complete.py index d12aec2add3..c0316cf5c78 100644 --- a/tests/masonry/builders/test_complete.py +++ b/tests/masonry/builders/test_complete.py @@ -391,6 +391,79 @@ def test_package_src(): zip.close() +def test_multiple_licenses(): + module_path = fixtures_dir / "multiple-licenses" + builder = CompleteBuilder( + Poetry.create(module_path), NullEnv(execute=True), NullIO() + ) + builder.build() + + sdist = module_path / "dist" / "the-package-4.5.6.tar.gz" + wheel = module_path / "dist" / "the_package-4.5.6-py3-none-any.whl" + + assert sdist.exists() + with tarfile.open(str(sdist), "r") as tar: + names = tar.getnames() + assert len(names) == len(set(names)) + assert "the-package-4.5.6/LICENSE.txt" in names + assert "the-package-4.5.6/COPYING.txt" in names + + assert wheel.exists() + with zipfile.ZipFile(str(wheel)) as z: + names = z.namelist() + assert len(names) == len(set(names)) + assert "the_package-4.5.6.dist-info/LICENSE.txt" in names + assert "the_package-4.5.6.dist-info/COPYING.txt" in names + + +def test_explicit_license_config_single_license(): + module_path = fixtures_dir / "explicit-license-config-single-license" + builder = CompleteBuilder( + Poetry.create(module_path), NullEnv(execute=True), NullIO() + ) + builder.build() + + sdist = module_path / "dist" / "the-package-4.5.6.tar.gz" + wheel = module_path / "dist" / "the_package-4.5.6-py3-none-any.whl" + + assert sdist.exists() + with tarfile.open(str(sdist), "r") as tar: + names = tar.getnames() + assert len(names) == len(set(names)) + assert "the-package-4.5.6/the-license.txt" in names + + assert wheel.exists() + with zipfile.ZipFile(str(wheel)) as z: + names = z.namelist() + assert len(names) == len(set(names)) + assert "the_package-4.5.6.dist-info/the-license.txt" in names + + +def test_explicit_license_config_multiple_licenses(): + module_path = fixtures_dir / "explicit-license-config-multiple-licenses" + builder = CompleteBuilder( + Poetry.create(module_path), NullEnv(execute=True), NullIO() + ) + builder.build() + + sdist = module_path / "dist" / "the-package-4.5.6.tar.gz" + wheel = module_path / "dist" / "the_package-4.5.6-py3-none-any.whl" + + assert sdist.exists() + with tarfile.open(str(sdist), "r") as tar: + names = tar.getnames() + assert len(names) == len(set(names)) + assert "the-package-4.5.6/the-first-license.txt" in names + assert "the-package-4.5.6/the-second-license.txt" in names + + assert wheel.exists() + with zipfile.ZipFile(str(wheel)) as z: + names = z.namelist() + assert len(names) == len(set(names)) + assert "the_package-4.5.6.dist-info/the-first-license.txt" in names + assert "the_package-4.5.6.dist-info/the-second-license.txt" in names + + def test_package_with_include(mocker): module_path = fixtures_dir / "with-include"