diff --git a/docs/docs/pyproject.md b/docs/docs/pyproject.md index 679ad824a22..7a2e6698ecb 100644 --- a/docs/docs/pyproject.md +++ b/docs/docs/pyproject.md @@ -40,6 +40,15 @@ 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/factory.py b/poetry/factory.py index 93361f24dd0..c61f492edcc 100644 --- a/poetry/factory.py +++ b/poetry/factory.py @@ -81,6 +81,16 @@ def create_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 license_file in local_config["license-files"]: + package.license_files.add(Path(poetry_file.parent) / license_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/poetry/json/schemas/poetry-schema.json b/poetry/json/schemas/poetry-schema.json index 10aff39e502..3e86fe7f5eb 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 af1a05a29d4..899e367a7aa 100644 --- a/poetry/masonry/builders/builder.py +++ b/poetry/masonry/builders/builder.py @@ -154,8 +154,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 e1fadc0b7cb..b6774385d6d 100644 --- a/poetry/masonry/builders/sdist.py +++ b/poetry/masonry/builders/sdist.py @@ -136,6 +136,11 @@ def build_setup(self): # type: () -> bytes else: pass + license_files = list(map(str, self._package.license_files)) + if license_files: + before.append("license_files = \\\n{}\n".format(pformat(license_files))) + extra.append("'license_files': license_files,") + 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 058679edb4e..823d3737817 100644 --- a/poetry/masonry/builders/wheel.py +++ b/poetry/masonry/builders/wheel.py @@ -181,9 +181,11 @@ 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 relative_license_path in self._package.license_files: + license_path = self._path / relative_license_path + self._add_file( + wheel, license_path, "%s/%s" % (self.dist_info, license_path.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 0811c54bf88..4a9c0dfee00 100644 --- a/poetry/packages/package.py +++ b/poetry/packages/package.py @@ -58,6 +58,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/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 05ae2e17824..6216736538a 100644 --- a/tests/masonry/builders/test_complete.py +++ b/tests/masonry/builders/test_complete.py @@ -395,6 +395,79 @@ def test_package_src(): zip.close() +def test_multiple_licenses(): + module_path = fixtures_dir / "multiple-licenses" + builder = CompleteBuilder( + Factory().create_poetry(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( + Factory().create_poetry(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( + Factory().create_poetry(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"