diff --git a/docs/libraries.md b/docs/libraries.md index ee6d5807c6c..02a0242878d 100644 --- a/docs/libraries.md +++ b/docs/libraries.md @@ -62,16 +62,19 @@ poetry build This command will package your library in two different formats: `sdist` which is the source format, and `wheel` which is a `compiled` package. -Poetry will automatically include some metadata files when building a package. When building -a `wheel`, the following files are included in the `.dist-info` directory: -- `LICENSE` -- `LICENSE.*` -- `COPYING` -- `COPYING.*` -- `LICENSES/**` - -When building an `sdist`, the following files will be included in the root folder: - - `LICENSE*` +Poetry will automatically include some license-related files when building a package - +in the `.dist-info/licenses` directory when building a `wheel`, +and in the root folder when building an `sdist`: +- `LICENSE*` +- `LICENCE*` +- `COPYING*` +- `AUTHORS*` +- `NOTICE*` +- `LICENSES/**/*` + +You can override this behavior by specifying +[`license-files`]({{< relref "pyproject/#license-files" >}}) +in the `pyproject.toml` file. ### Alternative build backends diff --git a/docs/pyproject.md b/docs/pyproject.md index ca309bc13f3..b8cbf350ce7 100644 --- a/docs/pyproject.md +++ b/docs/pyproject.md @@ -71,7 +71,8 @@ description = "A short description of the package." ### license -The license of the package. +An [SPDX expression](https://packaging.python.org/en/latest/glossary/#term-License-Expression) +representing the license of the package. The recommended notation for the most common licenses is (alphabetical): @@ -93,20 +94,43 @@ Optional, but it is highly recommended to supply this. More identifiers are listed at the [SPDX Open Source License Registry](https://spdx.org/licenses/). ```toml -license = { text = "MIT" } +license = "MIT" ``` -{{% note %}} -If your project is proprietary and does not use a specific license, you can set this value as `Proprietary`. -{{% /note %}} -You can also specify a license file. However, when doing this, the complete license text -will be added to the metadata and the License classifier cannot be determined -automatically so that you have to add it manually. +{{% warning %}} +Specifying license as a table, e.g. `{ text = "MIT" }` is deprecated. +If you used to specify a license file, e.g. `{ file = "LICENSE" }`, +use `license-files` instead. +{{% /warning %}} + +### license-files + +A list of glob patterns that match the license files of the package +relative to the root of the project source tree. ```toml -license = { file = "LICENSE" } +[project] +# ... +license-files = [ + "*-LICENSE", + "CONTRIBUTORS", + "MY-SPECIAL-LICENSE-DIR/**/*" +] ``` +By default, Poetry will include the following files: +- `LICENSE*` +- `LICENCE*` +- `COPYING*` +- `AUTHORS*` +- `NOTICE*` +- `LICENSES/**/*` + +{{% note %}} +The default applies only if the `license-files` field is not specified. +Specifying an empty list results in no license files being included. +{{% /note %}} + ### readme A path to the README file or the content. @@ -198,7 +222,7 @@ classifiers = [ ``` {{% warning %}} -Note that suitable classifiers based on your `python` requirement and `license` +Note that suitable classifiers based on your `python` requirement are **not** automatically added for you if you define classifiers statically in the `project` section. @@ -419,9 +443,6 @@ More identifiers are listed at the [SPDX Open Source License Registry](https://s ```toml license = "MIT" ``` -{{% note %}} -If your project is proprietary and does not use a specific licence, you can set this value as `Proprietary`. -{{% /note %}} ### authors @@ -545,11 +566,8 @@ classifiers = [ Note that Python classifiers are automatically added for you and are determined by your `python` requirement. -The `license` property will also set the License classifier automatically. - If you do not want Poetry to automatically add suitable classifiers -based on the `python` requirement and `license` property, -use `project.classifiers` instead of this setting. +based on the `python` requirement, use `project.classifiers` instead of this setting. {{% /note %}} ### packages diff --git a/poetry.lock b/poetry.lock index b108fa52c14..7d93e434468 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1154,7 +1154,7 @@ develop = false type = "git" url = "https://github.com/python-poetry/poetry-core.git" reference = "HEAD" -resolved_reference = "002aa3e16f98d21645bb9a45f698b55adc40f317" +resolved_reference = "e0bd00670fd883238838d0ced7d6bfe24c733dfd" [[package]] name = "pre-commit" @@ -1979,4 +1979,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.1" python-versions = ">=3.9,<4.0" -content-hash = "207d41932ff1199c1e7014253f2c30ba87a11325f4d2b3e0d1ce3fff6ccabc71" +content-hash = "3c42c2d19ba942ff693da2dd016a4e5d43509d69342e762e9a528ca981f52a3b" diff --git a/pyproject.toml b/pyproject.toml index 1f135913510..707fd576e1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ dependencies = [ "installer (>=0.7.0,<0.8.0)", "keyring (>=25.1.0,<26.0.0)", # packaging uses calver, so version is unclamped - "packaging (>=24.0)", + "packaging (>=24.2)", # PEP 639 support was added in 24.2 "pkginfo (>=1.12,<2.0)", "platformdirs (>=3.0.0,<5)", "pyproject-hooks (>=1.0.0,<2.0.0)", @@ -44,7 +44,7 @@ maintainers = [ { name = "finswimmer", email = "finswimmer77@gmail.com" }, { name = "Bartosz Sokorski", email = "b.sokorski@gmail.com" }, ] -license = { text = "MIT" } +license = "MIT" readme = "README.md" keywords = ["packaging", "dependency", "poetry"] # classifieres is dynamic because we want to create Python classifiers automatically diff --git a/src/poetry/masonry/builders/editable.py b/src/poetry/masonry/builders/editable.py index 6a077af7d77..47904bbdab9 100644 --- a/src/poetry/masonry/builders/editable.py +++ b/src/poetry/masonry/builders/editable.py @@ -216,8 +216,6 @@ def _add_scripts(self) -> list[Path]: def _add_dist_info(self, added_files: list[Path]) -> None: from poetry.core.masonry.builders.wheel import WheelBuilder - added_files = added_files[:] - builder = WheelBuilder(self._poetry) dist_info = self._env.site_packages.mkdir(Path(builder.dist_info)) @@ -226,24 +224,15 @@ def _add_dist_info(self, added_files: list[Path]) -> None: f" {dist_info.parent}" ) - with dist_info.joinpath("METADATA").open("w", encoding="utf-8") as f: - builder._write_metadata_file(f) - - added_files.append(dist_info.joinpath("METADATA")) + builder.prepare_metadata(dist_info.parent) + for path in sorted(f for f in dist_info.rglob("*") if f.is_file()): + added_files.append(path) with dist_info.joinpath("INSTALLER").open("w", encoding="utf-8") as f: f.write("poetry") added_files.append(dist_info.joinpath("INSTALLER")) - if self.convert_entry_points(): - with dist_info.joinpath("entry_points.txt").open( - "w", encoding="utf-8" - ) as f: - builder._write_entry_points(f) - - added_files.append(dist_info.joinpath("entry_points.txt")) - # write PEP 610 metadata direct_url_json = dist_info.joinpath("direct_url.json") direct_url_json.write_text( diff --git a/tests/console/commands/test_check.py b/tests/console/commands/test_check.py index 6a23a2c093f..15c5a828b35 100644 --- a/tests/console/commands/test_check.py +++ b/tests/console/commands/test_check.py @@ -173,6 +173,8 @@ def test_check_invalid( Error: Invalid source "not-exists" referenced in dependencies. Error: Invalid source "not-exists2" referenced in dependencies. Error: poetry.lock was not found. +Warning: [project.license] is not a valid SPDX identifier.\ + This is deprecated and will raise an error in the future. Warning: A wildcard Python dependency is ambiguous.\ Consider specifying a more explicit one. Warning: The "pendulum" dependency specifies the "allows-prereleases" property,\ diff --git a/tests/fixtures/invalid_pyproject/pyproject.toml b/tests/fixtures/invalid_pyproject/pyproject.toml index 036c5a4aba0..fcb22c59ece 100644 --- a/tests/fixtures/invalid_pyproject/pyproject.toml +++ b/tests/fixtures/invalid_pyproject/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "invalid" version = "1.0.0" -license = { text = "INVALID" } +license = "INVALID" classifiers = [ "Environment :: Console", "Intended Audience :: Clowns", diff --git a/tests/fixtures/simple_project/LICENSE b/tests/fixtures/simple_project/LICENSE new file mode 100644 index 00000000000..8fb264a2bf2 --- /dev/null +++ b/tests/fixtures/simple_project/LICENSE @@ -0,0 +1 @@ +license ... diff --git a/tests/fixtures/simple_project/pyproject.toml b/tests/fixtures/simple_project/pyproject.toml index e85c7a74a20..e8fbcacb591 100644 --- a/tests/fixtures/simple_project/pyproject.toml +++ b/tests/fixtures/simple_project/pyproject.toml @@ -5,7 +5,7 @@ description = "Some description." authors = [ { name = "Sébastien Eustace", email = "sebastien@eustace.io" } ] -license = { text = "MIT" } +license = "MIT" readme = "README.rst" keywords = ["packaging", "dependency", "poetry"] dynamic = [ "classifiers", "dependencies", "requires-python" ] diff --git a/tests/fixtures/simple_project_legacy/LICENSE b/tests/fixtures/simple_project_legacy/LICENSE new file mode 100644 index 00000000000..8fb264a2bf2 --- /dev/null +++ b/tests/fixtures/simple_project_legacy/LICENSE @@ -0,0 +1 @@ +license ... diff --git a/tests/masonry/builders/test_editable_builder.py b/tests/masonry/builders/test_editable_builder.py index 181eaa907a8..8dcf7185851 100644 --- a/tests/masonry/builders/test_editable_builder.py +++ b/tests/masonry/builders/test_editable_builder.py @@ -141,10 +141,12 @@ def test_builder_installs_proper_files_for_standard_packages( dist_info = tmp_venv.site_packages.find(dist_info)[0] - assert dist_info.joinpath("INSTALLER").exists() + assert dist_info.joinpath("entry_points.txt").exists() + assert dist_info.joinpath("WHEEL").exists() assert dist_info.joinpath("METADATA").exists() + assert dist_info.joinpath("licenses/LICENSE").exists() + assert dist_info.joinpath("INSTALLER").exists() assert dist_info.joinpath("RECORD").exists() - assert dist_info.joinpath("entry_points.txt").exists() assert dist_info.joinpath("direct_url.json").exists() assert not DeepDiff( @@ -175,6 +177,7 @@ def test_builder_installs_proper_files_for_standard_packages( Version: 1.2.3 Summary: Some description. License: MIT +License-File: LICENSE Keywords: packaging,dependency,poetry Author: Sébastien Eustace Author-email: sebastien@eustace.io @@ -192,7 +195,11 @@ def test_builder_installs_proper_files_for_standard_packages( ========== """ - assert metadata == dist_info.joinpath("METADATA").read_text(encoding="utf-8") + if project == "simple_project": + metadata = metadata.replace("License:", "License-Expression:").replace( + "Classifier: License :: OSI Approved :: MIT License\n", "" + ) + assert dist_info.joinpath("METADATA").read_text(encoding="utf-8") == metadata with open(dist_info.joinpath("RECORD"), encoding="utf-8", newline="") as f: reader = csv.reader(f) @@ -205,9 +212,12 @@ def test_builder_installs_proper_files_for_standard_packages( assert str(tmp_venv.site_packages.find(pth_file)[0]) in record_entries assert str(tmp_venv._bin_dir.joinpath("foo")) in record_entries assert str(tmp_venv._bin_dir.joinpath("baz")) in record_entries + assert str(dist_info.joinpath("entry_points.txt")) in record_entries + assert str(dist_info.joinpath("WHEEL")) in record_entries assert str(dist_info.joinpath("METADATA")) in record_entries + assert str(dist_info.joinpath("licenses/LICENSE")) in record_entries + assert str(dist_info.joinpath("INSTALLER")) in record_entries - assert str(dist_info.joinpath("entry_points.txt")) in record_entries assert str(dist_info.joinpath("RECORD")) in record_entries assert str(dist_info.joinpath("direct_url.json")) in record_entries @@ -220,7 +230,7 @@ def test_builder_installs_proper_files_for_standard_packages( sys.exit(baz.boom.bim()) """ - assert baz_script == tmp_venv._bin_dir.joinpath("baz").read_text(encoding="utf-8") + assert tmp_venv._bin_dir.joinpath("baz").read_text(encoding="utf-8") == baz_script foo_script = f"""\ #!{tmp_venv.python} @@ -231,7 +241,7 @@ def test_builder_installs_proper_files_for_standard_packages( sys.exit(bar()) """ - assert foo_script == tmp_venv._bin_dir.joinpath("foo").read_text(encoding="utf-8") + assert tmp_venv._bin_dir.joinpath("foo").read_text(encoding="utf-8") == foo_script fox_script = f"""\ #!{tmp_venv.python} @@ -242,7 +252,7 @@ def test_builder_installs_proper_files_for_standard_packages( sys.exit(bar.baz()) """ - assert fox_script == tmp_venv._bin_dir.joinpath("fox").read_text(encoding="utf-8") + assert tmp_venv._bin_dir.joinpath("fox").read_text(encoding="utf-8") == fox_script def test_builder_falls_back_on_setup_and_pip_for_packages_with_build_scripts(