Skip to content

Commit 8074917

Browse files
authored
Upload: All metadata incl. PEP 639 (#9442)
We were previously not uploading all metadata in the formdata of an upload request in the legacy api. Notably, we were missing the PEP 639 license-files field. I had to switch to pdm due to pypa/hatch#1828
1 parent ba94d85 commit 8074917

File tree

2 files changed

+236
-164
lines changed

2 files changed

+236
-164
lines changed

crates/uv-publish/src/lib.rs

+190-156
Original file line numberDiff line numberDiff line change
@@ -613,20 +613,49 @@ async fn form_metadata(
613613
) -> Result<Vec<(&'static str, String)>, PublishPrepareError> {
614614
let hash_hex = hash_file(file, Hasher::from(HashAlgorithm::Sha256)).await?;
615615

616-
let metadata = metadata(file, filename).await?;
616+
let Metadata23 {
617+
metadata_version,
618+
name,
619+
version,
620+
platforms,
621+
// Not used by PyPI legacy upload
622+
supported_platforms: _,
623+
summary,
624+
description,
625+
description_content_type,
626+
keywords,
627+
home_page,
628+
download_url,
629+
author,
630+
author_email,
631+
maintainer,
632+
maintainer_email,
633+
license,
634+
license_expression,
635+
license_files,
636+
classifiers,
637+
requires_dist,
638+
provides_dist,
639+
obsoletes_dist,
640+
requires_python,
641+
requires_external,
642+
project_urls,
643+
provides_extras,
644+
dynamic,
645+
} = metadata(file, filename).await?;
617646

618647
let mut form_metadata = vec![
619648
(":action", "file_upload".to_string()),
620649
("sha256_digest", hash_hex.digest.to_string()),
621650
("protocol_version", "1".to_string()),
622-
("metadata_version", metadata.metadata_version.clone()),
651+
("metadata_version", metadata_version.clone()),
623652
// Twine transforms the name with `re.sub("[^A-Za-z0-9.]+", "-", name)`
624653
// * <https://github.com/pypa/twine/issues/743>
625654
// * <https://github.com/pypa/twine/blob/5bf3f38ff3d8b2de47b7baa7b652c697d7a64776/twine/package.py#L57-L65>
626655
// warehouse seems to call `packaging.utils.canonicalize_name` nowadays and has a separate
627656
// `normalized_name`, so we'll start with this and we'll readjust if there are user reports.
628-
("name", metadata.name.clone()),
629-
("version", metadata.version.clone()),
657+
("name", name.clone()),
658+
("version", version.clone()),
630659
("filetype", filename.filetype().to_string()),
631660
];
632661

@@ -642,41 +671,39 @@ async fn form_metadata(
642671
}
643672
};
644673

645-
add_option("summary", metadata.summary);
646-
add_option("description", metadata.description);
647-
add_option(
648-
"description_content_type",
649-
metadata.description_content_type,
650-
);
651-
add_option("author", metadata.author);
652-
add_option("author_email", metadata.author_email);
653-
add_option("maintainer", metadata.maintainer);
654-
add_option("maintainer_email", metadata.maintainer_email);
655-
add_option("license", metadata.license);
656-
add_option("keywords", metadata.keywords);
657-
add_option("home_page", metadata.home_page);
658-
add_option("download_url", metadata.download_url);
674+
add_option("author", author);
675+
add_option("author_email", author_email);
676+
add_option("description", description);
677+
add_option("description_content_type", description_content_type);
678+
add_option("download_url", download_url);
679+
add_option("home_page", home_page);
680+
add_option("keywords", keywords);
681+
add_option("license", license);
682+
add_option("license_expression", license_expression);
683+
add_option("maintainer", maintainer);
684+
add_option("maintainer_email", maintainer_email);
685+
add_option("summary", summary);
659686

660687
// The GitLab PyPI repository API implementation requires this metadata field and twine always
661688
// includes it in the request, even when it's empty.
662-
form_metadata.push((
663-
"requires_python",
664-
metadata.requires_python.unwrap_or(String::new()),
665-
));
689+
form_metadata.push(("requires_python", requires_python.unwrap_or(String::new())));
666690

667691
let mut add_vec = |name, values: Vec<String>| {
668692
for i in values {
669693
form_metadata.push((name, i.clone()));
670694
}
671695
};
672696

673-
add_vec("classifiers", metadata.classifiers);
674-
add_vec("platform", metadata.platforms);
675-
add_vec("requires_dist", metadata.requires_dist);
676-
add_vec("provides_dist", metadata.provides_dist);
677-
add_vec("obsoletes_dist", metadata.obsoletes_dist);
678-
add_vec("requires_external", metadata.requires_external);
679-
add_vec("project_urls", metadata.project_urls);
697+
add_vec("classifiers", classifiers);
698+
add_vec("dynamic", dynamic);
699+
add_vec("license_file", license_files);
700+
add_vec("obsoletes_dist", obsoletes_dist);
701+
add_vec("platform", platforms);
702+
add_vec("project_urls", project_urls);
703+
add_vec("provides_dist", provides_dist);
704+
add_vec("provides_extra", provides_extras);
705+
add_vec("requires_dist", requires_dist);
706+
add_vec("requires_external", requires_external);
680707

681708
Ok(form_metadata)
682709
}
@@ -848,52 +875,54 @@ mod tests {
848875
.map(|(k, v)| format!("{k}: {v}"))
849876
.join("\n");
850877
assert_snapshot!(&formatted_metadata, @r###"
851-
:action: file_upload
852-
sha256_digest: 89fa05cffa7f457658373b85de302d24d0c205ceda2819a8739e324b75e9430b
853-
protocol_version: 1
854-
metadata_version: 2.3
855-
name: tqdm
856-
version: 999.0.0
857-
filetype: sdist
858-
pyversion: source
859-
description: # tqdm
860-
861-
[![PyPI - Version](https://img.shields.io/pypi/v/tqdm.svg)](https://pypi.org/project/tqdm)
862-
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/tqdm.svg)](https://pypi.org/project/tqdm)
863-
864-
-----
865-
866-
**Table of Contents**
867-
868-
- [Installation](#installation)
869-
- [License](#license)
870-
871-
## Installation
872-
873-
```console
874-
pip install tqdm
875-
```
876-
877-
## License
878-
879-
`tqdm` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
880-
881-
description_content_type: text/markdown
882-
author_email: Charlie Marsh <[email protected]>
883-
requires_python: >=3.8
884-
classifiers: Development Status :: 4 - Beta
885-
classifiers: Programming Language :: Python
886-
classifiers: Programming Language :: Python :: 3.8
887-
classifiers: Programming Language :: Python :: 3.9
888-
classifiers: Programming Language :: Python :: 3.10
889-
classifiers: Programming Language :: Python :: 3.11
890-
classifiers: Programming Language :: Python :: 3.12
891-
classifiers: Programming Language :: Python :: Implementation :: CPython
892-
classifiers: Programming Language :: Python :: Implementation :: PyPy
893-
project_urls: Documentation, https://github.com/unknown/tqdm#readme
894-
project_urls: Issues, https://github.com/unknown/tqdm/issues
895-
project_urls: Source, https://github.com/unknown/tqdm
896-
"###);
878+
:action: file_upload
879+
sha256_digest: 89fa05cffa7f457658373b85de302d24d0c205ceda2819a8739e324b75e9430b
880+
protocol_version: 1
881+
metadata_version: 2.3
882+
name: tqdm
883+
version: 999.0.0
884+
filetype: sdist
885+
pyversion: source
886+
author_email: Charlie Marsh <[email protected]>
887+
description: # tqdm
888+
889+
[![PyPI - Version](https://img.shields.io/pypi/v/tqdm.svg)](https://pypi.org/project/tqdm)
890+
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/tqdm.svg)](https://pypi.org/project/tqdm)
891+
892+
-----
893+
894+
**Table of Contents**
895+
896+
- [Installation](#installation)
897+
- [License](#license)
898+
899+
## Installation
900+
901+
```console
902+
pip install tqdm
903+
```
904+
905+
## License
906+
907+
`tqdm` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
908+
909+
description_content_type: text/markdown
910+
license_expression: MIT
911+
requires_python: >=3.8
912+
classifiers: Development Status :: 4 - Beta
913+
classifiers: Programming Language :: Python
914+
classifiers: Programming Language :: Python :: 3.8
915+
classifiers: Programming Language :: Python :: 3.9
916+
classifiers: Programming Language :: Python :: 3.10
917+
classifiers: Programming Language :: Python :: 3.11
918+
classifiers: Programming Language :: Python :: 3.12
919+
classifiers: Programming Language :: Python :: Implementation :: CPython
920+
classifiers: Programming Language :: Python :: Implementation :: PyPy
921+
license_file: LICENSE.txt
922+
project_urls: Documentation, https://github.com/unknown/tqdm#readme
923+
project_urls: Issues, https://github.com/unknown/tqdm/issues
924+
project_urls: Source, https://github.com/unknown/tqdm
925+
"###);
897926

898927
let (request, _) = build_request(
899928
&file,
@@ -958,87 +987,92 @@ mod tests {
958987
.map(|(k, v)| format!("{k}: {v}"))
959988
.join("\n");
960989
assert_snapshot!(&formatted_metadata, @r###"
961-
:action: file_upload
962-
sha256_digest: 0d88ca657bc6b64995ca416e0c59c71af85cc10015d940fa446c42a8b485ee1c
963-
protocol_version: 1
964-
metadata_version: 2.1
965-
name: tqdm
966-
version: 4.66.1
967-
filetype: bdist_wheel
968-
pyversion: py3
969-
summary: Fast, Extensible Progress Meter
970-
description_content_type: text/x-rst
971-
maintainer_email: tqdm developers <[email protected]>
972-
license: MPL-2.0 AND MIT
973-
keywords: progressbar,progressmeter,progress,bar,meter,rate,eta,console,terminal,time
974-
requires_python: >=3.7
975-
classifiers: Development Status :: 5 - Production/Stable
976-
classifiers: Environment :: Console
977-
classifiers: Environment :: MacOS X
978-
classifiers: Environment :: Other Environment
979-
classifiers: Environment :: Win32 (MS Windows)
980-
classifiers: Environment :: X11 Applications
981-
classifiers: Framework :: IPython
982-
classifiers: Framework :: Jupyter
983-
classifiers: Intended Audience :: Developers
984-
classifiers: Intended Audience :: Education
985-
classifiers: Intended Audience :: End Users/Desktop
986-
classifiers: Intended Audience :: Other Audience
987-
classifiers: Intended Audience :: System Administrators
988-
classifiers: License :: OSI Approved :: MIT License
989-
classifiers: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
990-
classifiers: Operating System :: MacOS
991-
classifiers: Operating System :: MacOS :: MacOS X
992-
classifiers: Operating System :: Microsoft
993-
classifiers: Operating System :: Microsoft :: MS-DOS
994-
classifiers: Operating System :: Microsoft :: Windows
995-
classifiers: Operating System :: POSIX
996-
classifiers: Operating System :: POSIX :: BSD
997-
classifiers: Operating System :: POSIX :: BSD :: FreeBSD
998-
classifiers: Operating System :: POSIX :: Linux
999-
classifiers: Operating System :: POSIX :: SunOS/Solaris
1000-
classifiers: Operating System :: Unix
1001-
classifiers: Programming Language :: Python
1002-
classifiers: Programming Language :: Python :: 3
1003-
classifiers: Programming Language :: Python :: 3.7
1004-
classifiers: Programming Language :: Python :: 3.8
1005-
classifiers: Programming Language :: Python :: 3.9
1006-
classifiers: Programming Language :: Python :: 3.10
1007-
classifiers: Programming Language :: Python :: 3.11
1008-
classifiers: Programming Language :: Python :: 3 :: Only
1009-
classifiers: Programming Language :: Python :: Implementation
1010-
classifiers: Programming Language :: Python :: Implementation :: IronPython
1011-
classifiers: Programming Language :: Python :: Implementation :: PyPy
1012-
classifiers: Programming Language :: Unix Shell
1013-
classifiers: Topic :: Desktop Environment
1014-
classifiers: Topic :: Education :: Computer Aided Instruction (CAI)
1015-
classifiers: Topic :: Education :: Testing
1016-
classifiers: Topic :: Office/Business
1017-
classifiers: Topic :: Other/Nonlisted Topic
1018-
classifiers: Topic :: Software Development :: Build Tools
1019-
classifiers: Topic :: Software Development :: Libraries
1020-
classifiers: Topic :: Software Development :: Libraries :: Python Modules
1021-
classifiers: Topic :: Software Development :: Pre-processors
1022-
classifiers: Topic :: Software Development :: User Interfaces
1023-
classifiers: Topic :: System :: Installation/Setup
1024-
classifiers: Topic :: System :: Logging
1025-
classifiers: Topic :: System :: Monitoring
1026-
classifiers: Topic :: System :: Shells
1027-
classifiers: Topic :: Terminals
1028-
classifiers: Topic :: Utilities
1029-
requires_dist: colorama ; platform_system == "Windows"
1030-
requires_dist: pytest >=6 ; extra == 'dev'
1031-
requires_dist: pytest-cov ; extra == 'dev'
1032-
requires_dist: pytest-timeout ; extra == 'dev'
1033-
requires_dist: pytest-xdist ; extra == 'dev'
1034-
requires_dist: ipywidgets >=6 ; extra == 'notebook'
1035-
requires_dist: slack-sdk ; extra == 'slack'
1036-
requires_dist: requests ; extra == 'telegram'
1037-
project_urls: homepage, https://tqdm.github.io
1038-
project_urls: repository, https://github.com/tqdm/tqdm
1039-
project_urls: changelog, https://tqdm.github.io/releases
1040-
project_urls: wiki, https://github.com/tqdm/tqdm/wiki
1041-
"###);
990+
:action: file_upload
991+
sha256_digest: 0d88ca657bc6b64995ca416e0c59c71af85cc10015d940fa446c42a8b485ee1c
992+
protocol_version: 1
993+
metadata_version: 2.1
994+
name: tqdm
995+
version: 4.66.1
996+
filetype: bdist_wheel
997+
pyversion: py3
998+
description_content_type: text/x-rst
999+
keywords: progressbar,progressmeter,progress,bar,meter,rate,eta,console,terminal,time
1000+
license: MPL-2.0 AND MIT
1001+
maintainer_email: tqdm developers <[email protected]>
1002+
summary: Fast, Extensible Progress Meter
1003+
requires_python: >=3.7
1004+
classifiers: Development Status :: 5 - Production/Stable
1005+
classifiers: Environment :: Console
1006+
classifiers: Environment :: MacOS X
1007+
classifiers: Environment :: Other Environment
1008+
classifiers: Environment :: Win32 (MS Windows)
1009+
classifiers: Environment :: X11 Applications
1010+
classifiers: Framework :: IPython
1011+
classifiers: Framework :: Jupyter
1012+
classifiers: Intended Audience :: Developers
1013+
classifiers: Intended Audience :: Education
1014+
classifiers: Intended Audience :: End Users/Desktop
1015+
classifiers: Intended Audience :: Other Audience
1016+
classifiers: Intended Audience :: System Administrators
1017+
classifiers: License :: OSI Approved :: MIT License
1018+
classifiers: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
1019+
classifiers: Operating System :: MacOS
1020+
classifiers: Operating System :: MacOS :: MacOS X
1021+
classifiers: Operating System :: Microsoft
1022+
classifiers: Operating System :: Microsoft :: MS-DOS
1023+
classifiers: Operating System :: Microsoft :: Windows
1024+
classifiers: Operating System :: POSIX
1025+
classifiers: Operating System :: POSIX :: BSD
1026+
classifiers: Operating System :: POSIX :: BSD :: FreeBSD
1027+
classifiers: Operating System :: POSIX :: Linux
1028+
classifiers: Operating System :: POSIX :: SunOS/Solaris
1029+
classifiers: Operating System :: Unix
1030+
classifiers: Programming Language :: Python
1031+
classifiers: Programming Language :: Python :: 3
1032+
classifiers: Programming Language :: Python :: 3.7
1033+
classifiers: Programming Language :: Python :: 3.8
1034+
classifiers: Programming Language :: Python :: 3.9
1035+
classifiers: Programming Language :: Python :: 3.10
1036+
classifiers: Programming Language :: Python :: 3.11
1037+
classifiers: Programming Language :: Python :: 3 :: Only
1038+
classifiers: Programming Language :: Python :: Implementation
1039+
classifiers: Programming Language :: Python :: Implementation :: IronPython
1040+
classifiers: Programming Language :: Python :: Implementation :: PyPy
1041+
classifiers: Programming Language :: Unix Shell
1042+
classifiers: Topic :: Desktop Environment
1043+
classifiers: Topic :: Education :: Computer Aided Instruction (CAI)
1044+
classifiers: Topic :: Education :: Testing
1045+
classifiers: Topic :: Office/Business
1046+
classifiers: Topic :: Other/Nonlisted Topic
1047+
classifiers: Topic :: Software Development :: Build Tools
1048+
classifiers: Topic :: Software Development :: Libraries
1049+
classifiers: Topic :: Software Development :: Libraries :: Python Modules
1050+
classifiers: Topic :: Software Development :: Pre-processors
1051+
classifiers: Topic :: Software Development :: User Interfaces
1052+
classifiers: Topic :: System :: Installation/Setup
1053+
classifiers: Topic :: System :: Logging
1054+
classifiers: Topic :: System :: Monitoring
1055+
classifiers: Topic :: System :: Shells
1056+
classifiers: Topic :: Terminals
1057+
classifiers: Topic :: Utilities
1058+
license_file: LICENCE
1059+
project_urls: homepage, https://tqdm.github.io
1060+
project_urls: repository, https://github.com/tqdm/tqdm
1061+
project_urls: changelog, https://tqdm.github.io/releases
1062+
project_urls: wiki, https://github.com/tqdm/tqdm/wiki
1063+
provides_extra: dev
1064+
provides_extra: notebook
1065+
provides_extra: slack
1066+
provides_extra: telegram
1067+
requires_dist: colorama ; platform_system == "Windows"
1068+
requires_dist: pytest >=6 ; extra == 'dev'
1069+
requires_dist: pytest-cov ; extra == 'dev'
1070+
requires_dist: pytest-timeout ; extra == 'dev'
1071+
requires_dist: pytest-xdist ; extra == 'dev'
1072+
requires_dist: ipywidgets >=6 ; extra == 'notebook'
1073+
requires_dist: slack-sdk ; extra == 'slack'
1074+
requires_dist: requests ; extra == 'telegram'
1075+
"###);
10421076

10431077
let (request, _) = build_request(
10441078
&file,

0 commit comments

Comments
 (0)