Skip to content

Commit 591cb52

Browse files
authored
Expose wheel upload time via data-upload-time in HTML index (#8000)
## Summary - Adds `data-upload-time` attribute (RFC 3339, UTC with `Z` suffix) to each wheel anchor in the PEP 503 simple HTML index emitted by `s3_management/manage_v2.py`. - Timestamp is sourced from the S3 `LastModified` returned by `ListObjectsV2`, threaded through a new `S3Object.last_modified` field populated in `fetch_object_names` / `from_S3`. - Unblocks dependency tooling (e.g. supply-chain grace-period checks) that needs per-file upload times, per pytorch/pytorch#178980. ## Test plan - [ ] `python s3_management/manage_v2.py whl/nightly --do-not-upload` and spot-check a generated `index.html` — each `<a>` has `data-upload-time="YYYY-MM-DDTHH:MM:SSZ"`. - [ ] Confirm existing attributes (`data-dist-info-metadata`, `data-core-metadata`, `#sha256=` fragment) still render correctly alongside the new attribute. - [ ] Verify libtorch/source_code index generation is unaffected (those HTML methods don't consume `last_modified`).
1 parent c57427d commit 591cb52

1 file changed

Lines changed: 18 additions & 6 deletions

File tree

s3_management/manage_v2.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
from collections import defaultdict
7373
from os import makedirs, path
7474
from re import match, sub
75-
from typing import Dict, Iterable, List, Optional, Set, TypeVar
75+
from typing import Dict, Iterable, List, Optional, Set, Tuple, TypeVar
7676

7777
import boto3 # type: ignore[import]
7878
import botocore # type: ignore[import]
@@ -561,6 +561,7 @@ class S3Object:
561561
checksum: Optional[str]
562562
size: Optional[int]
563563
pep658: Optional[str]
564+
last_modified: Optional[str] = None
564565

565566
def __hash__(self):
566567
return hash(self.key)
@@ -851,6 +852,8 @@ def to_simple_package_html(
851852
pep658_sha = f"sha256={obj.pep658}"
852853
# pep714 renames the attribute to data-core-metadata
853854
attributes = f' data-dist-info-metadata="{pep658_sha}" data-core-metadata="{pep658_sha}"'
855+
if obj.last_modified:
856+
attributes += f' data-upload-time="{obj.last_modified}"'
854857

855858
out.append(
856859
f' <a href="{base_url}/{obj.key}{maybe_fragment}"{attributes}>{path.basename(obj.key).replace("%2B", "+")}</a><br/>'
@@ -1332,16 +1335,24 @@ def grant_public_read(cls, key: str) -> None:
13321335
CLIENT.put_object_acl(Bucket=BUCKET.name, Key=key, ACL="public-read")
13331336

13341337
@classmethod
1335-
def fetch_object_names(cls, prefix: str) -> List[str]:
1336-
obj_names = []
1338+
def fetch_object_names(cls, prefix: str) -> List[Tuple[str, Optional[str]]]:
1339+
obj_names: List[Tuple[str, Optional[str]]] = []
1340+
1341+
def _format_last_modified(obj) -> Optional[str]:
1342+
lm = getattr(obj, "last_modified", None)
1343+
if lm is None:
1344+
return None
1345+
# S3 returns timezone-aware UTC datetimes; emit RFC 3339 with "Z" suffix
1346+
iso = lm.isoformat()
1347+
return iso.replace("+00:00", "Z")
13371348

13381349
# Special handling for source_code prefix - flat structure with only tar.gz files
13391350
if prefix.startswith("source_code"):
13401351
for obj in BUCKET.objects.filter(Prefix=prefix):
13411352
# For source_code, we only want files directly in the prefix directory
13421353
# and they should be tar.gz files matching pytorch-*.tar.gz
13431354
if path.dirname(obj.key) == prefix and obj.key.endswith(".tar.gz"):
1344-
obj_names.append(obj.key)
1355+
obj_names.append((obj.key, _format_last_modified(obj)))
13451356
return obj_names
13461357

13471358
# Original logic for whl and libtorch prefixes
@@ -1362,7 +1373,7 @@ def fetch_object_names(cls, prefix: str) -> List[str]:
13621373

13631374
if not is_acceptable or is_not_accepted:
13641375
continue
1365-
obj_names.append(obj.key)
1376+
obj_names.append((obj.key, _format_last_modified(obj)))
13661377
return obj_names
13671378

13681379
def fetch_metadata(self) -> None:
@@ -1447,8 +1458,9 @@ def sanitize_key(key: str) -> str:
14471458
checksum=None,
14481459
size=None,
14491460
pep658=None,
1461+
last_modified=last_modified,
14501462
)
1451-
for key in obj_names
1463+
for key, last_modified in obj_names
14521464
],
14531465
prefix,
14541466
)

0 commit comments

Comments
 (0)