From 41c807c5938d269703c6ff2644fb3b7dc88eda4e Mon Sep 17 00:00:00 2001 From: Karolina Surma Date: Tue, 7 Jan 2025 12:52:35 +0100 Subject: [PATCH] Show License-Expression if present in package metadata With Core Metadata 2.4 a new field, License-Expression, has been added. If it's present, favor it over the deprecated (with PEP 639) legacy unstructured License field. Closes: #13112 --- news/13112.feature.rst | 1 + src/pip/_internal/commands/show.py | 9 +++- .../license.dist-0.1-py2.py3-none-any.whl | Bin 0 -> 1789 bytes .../license.dist-0.2-py2.py3-none-any.whl | Bin 0 -> 1770 bytes tests/functional/test_show.py | 43 +++++++++++++++++- 5 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 news/13112.feature.rst create mode 100644 tests/data/packages/license.dist-0.1-py2.py3-none-any.whl create mode 100644 tests/data/packages/license.dist-0.2-py2.py3-none-any.whl diff --git a/news/13112.feature.rst b/news/13112.feature.rst new file mode 100644 index 00000000000..bf60883211f --- /dev/null +++ b/news/13112.feature.rst @@ -0,0 +1 @@ +Prefer to display ``License-Expression`` in ``pip show`` if metadata version is at least 2.4. diff --git a/src/pip/_internal/commands/show.py b/src/pip/_internal/commands/show.py index c54d548f5fb..b47500cf8b4 100644 --- a/src/pip/_internal/commands/show.py +++ b/src/pip/_internal/commands/show.py @@ -66,6 +66,7 @@ class _PackageInfo(NamedTuple): author: str author_email: str license: str + license_expression: str entry_points: List[str] files: Optional[List[str]] @@ -161,6 +162,7 @@ def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]: author=metadata.get("Author", ""), author_email=metadata.get("Author-email", ""), license=metadata.get("License", ""), + license_expression=metadata.get("License-Expression", ""), entry_points=entry_points, files=files, ) @@ -180,13 +182,18 @@ def print_results( if i > 0: write_output("---") + metadata_version_tuple = tuple(map(int, dist.metadata_version.split("."))) + write_output("Name: %s", dist.name) write_output("Version: %s", dist.version) write_output("Summary: %s", dist.summary) write_output("Home-page: %s", dist.homepage) write_output("Author: %s", dist.author) write_output("Author-email: %s", dist.author_email) - write_output("License: %s", dist.license) + if metadata_version_tuple >= (2, 4) and dist.license_expression: + write_output("License-Expression: %s", dist.license_expression) + else: + write_output("License: %s", dist.license) write_output("Location: %s", dist.location) if dist.editable_project_location is not None: write_output( diff --git a/tests/data/packages/license.dist-0.1-py2.py3-none-any.whl b/tests/data/packages/license.dist-0.1-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..b1a73258dd18d718cc0362c758f70afd0a05028e GIT binary patch literal 1789 zcmWIWW@Zs#U|`^2P%kx&DsdP5e;>&6U}RuWW{_dX$xKeoD^Ar*$t*6>HPADJFf#Mf z^7VaPLmXWkLmWdxI2o9KF78j~Skj+fTEWf0$nt`jfdNd!_B;9=HV|Q*+Kb*>|`i=%VTL2Q_n8+BF&?dei=0{$IaM<;H>qbxMAEWo%(b zcW#n6(NJz$+SzqXHPHUoCW~NxXO0u<%&Hu!g|XbqNAm1F+8%s<92=9 zduz9?l6fNV^Zz={1)uj{pRE}dzwfJ0CfD7#_Y1aMwfI`3#`A`cWnR=8|KiAu72i%+ zTni9gIxANHzS!o!bKNUDW&Xuj$i+Hqs&g%FOqQ_^`u@dg#~p|8pQg;!hpq)*d%={! zlDN0+=n=gu;sNlm(*lNF0E24LC1BX81H(=cf7k`NI{OE?fJ2S}7;0ETuJ+7Xzh(ml zh6leV?e#pJu%>~X-(Zz+_=Npe-d<8z9kk%ahsDzV`DZr0&b$9I+x6t`#`#AK)=3D@ zm5{uAg_S3CquaH|$G#P=$x45|lr`mM_YwitXuG2}nJQeJTrWP}TyyEACi}0Vi*e>} z_c!lKi&5Nl{=MkInXh-xte&l_^!30BODqgCw!Tk~*|W^gV)BnmHD3%(6<@xLe&=|&yRLET&fjVk z(yDe0r;o}b0?RZ-*)ikl?jkc_KotYADlvhjmsMU24zRX%mw>ss7)eca!fC%|2L{*r zqZ_U*Y49k#E~e3}dBT8)|FX#m2T7}jPg)6o%2&qTeWLfya;JUFykjc4QSZJ!Z1z?+ z{(ndP$}(xL+Vo8m%x~#s96Xn^=)tBh(UMcDqR>qdxWuoBLQY{U?M@*tB$J_3g%>O~3Eg zAKv3^+-_}WR}la4$g3GTv&>Ih@vQmfxA~QTt{Ch7{Yn(GT1h#4&AJU84q%@)AlxgVso-T)!&gBw~OZb$6 z%TTtWHyCAyx)x&fs{sX3WRRthCWrKwz82$$t= z`v-Cm9Ec-a1~NuJK0Y%qvm`!Vub>j_pi7^{0-P`tNPssZlN>XyqD}&AJTNgbY-t2B zi7NP5Aq5{=p@(b{X1RcDkqyv3JQl%937|EgQUZ@Pm^lhzO+#ZQ6A{)x@)yu5Q2xSd z6|QuGuxd-=KVZp^$4Agq1GEK{YVg>C8RrmNKvA=$aRv+A7R(d?G#r!yFbrp918HUj O!hgVEe+e{#fdK&Mm}u?* literal 0 HcmV?d00001 diff --git a/tests/data/packages/license.dist-0.2-py2.py3-none-any.whl b/tests/data/packages/license.dist-0.2-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..db5662bdc1e9e1dd587f77c4a7a2f49acacc369b GIT binary patch literal 1770 zcmWIWW@Zs#U|`^2U@SF_ih2EN+ZiBFjgf&tnL&mjCo?%UuQ*jNC9}9h*Fet*!pO`^ z%h&gH4RLgF3~>w%;bdUmw75TgKMii(0l-Sxar9=c~!x{j}WG0VHa&hJpms)mSPd(VF>=Y8DQyIMVCnn}g1>&w?Y zzj?>MKIr`2zY@x??_Hk6b2H^dW>d(u#iAW{_V@0(cCFZUXS#F$+0^3C zcXm9@n7!oxbFPW1f0l)}Z=WY;wxD6&?Z1JoR$CgUE?3WMy|XIk?d?}PYNT((O*&@Z zxomF9iK{YR$xb(2cpm9sYg*OMz-4PFf2h{OI(yq+FJJb>h0xKxuGBO9z4&I2=57vH8omxLR;cgjQg{++)9f{ zTlW@Emf7?rG@#>;?tCRVb0$`oUthi?O*#2x4rA%gjQrW3?mLEF%wMtV{Cm-ZD<4~~ zES~SH6cWEWIE0;dmaOFbeL9xgRJYsah$I?*XMAMuucV!`Ao|wf+3yVQ-G5wDzo(

i3u#dtny+=fVH~>%+19JFx3gC{hA#ZTNDlijT1){3fJzAH4>9_dV6`qa94!wyr`H=X6ZHwx1ufS6aD`)M#*QULHs<-FV z+m|)0&ZpQ;jk_N_9bgX6J`Pl@MdI^W5!jkNq~(9CPs!WjUXmbr5r1yltat9$QEIi3&<8} z0`0?N5v-H|S_3L2@K}SHqY&0KG=?z|VGSgIfvf_?CswO)r4xizTN+;ji+Magf~Fdv wEud6`#}>>uhu8v&nk|j3EO1*eQvlF#Pzt~>oRtlvnH32C0fYS-& None: """ Test that all the fields are present """ + # future-compat: once pip adopts PEP 639 in pyproject.toml and + # its build backend produces metadata 2.4 or greater, + # it will display "License-Expression" rather than License + verbose = script.pip("show", "--verbose", "pip").stdout + match = re.search(r"Metadata-Version:\s(\d+\.\d+)", verbose) + if match is not None: + metadata_version = match.group(1) + metadata_version_tuple = tuple(map(int, metadata_version.split("."))) + if metadata_version_tuple >= (2, 4) and "License-Expression" in verbose: + license_str = "License-Expression" + else: + license_str = "License" + else: + license_str = "License" + result = script.pip("show", "pip") lines = result.stdout.splitlines() expected = { @@ -226,7 +241,7 @@ def test_all_fields(script: PipTestEnvironment) -> None: "Home-page", "Author", "Author-email", - "License", + f"{license_str}", "Location", "Editable project location", "Requires", @@ -410,3 +425,29 @@ def test_show_populate_homepage_from_project_urls( result = script.pip("show", "simple", cwd=pkg_path) lines = result.stdout.splitlines() assert "Home-page: https://example.com" in lines + + +def test_show_license_expression(script: PipTestEnvironment, data: TestData) -> None: + """ + Show License-Expression if present in metadata >= 2.4. + """ + wheel_file = data.packages.joinpath("license.dist-0.1-py2.py3-none-any.whl") + script.pip("install", "--no-index", wheel_file) + result = script.pip("show", "license.dist") + lines = result.stdout.splitlines() + assert "License-Expression: MIT AND MIT-0" in lines + assert "License: The legacy license declaration" not in lines + + +def test_show_license_for_metadata_24( + script: PipTestEnvironment, data: TestData +) -> None: + """ + Show License if License-Expression is not there for metadata >= 2.4. + """ + wheel_file = data.packages.joinpath("license.dist-0.2-py2.py3-none-any.whl") + script.pip("install", "--no-index", wheel_file) + result = script.pip("show", "license.dist") + lines = result.stdout.splitlines() + assert "License-Expression: " not in lines + assert "License: The legacy license declaration" in lines