Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/poetry/core/packages/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,21 @@
T = TypeVar("T", bound="Package")


class PackageFile(TypedDict):
# TODO: Make use of https://peps.python.org/pep-0655/ when we drop support for Python < 3.11.
# TODO: Make use of https://peps.python.org/pep-0705/ when we drop support for Python < 3.10.
# We could also use the backport in typing-extensions, but it is not worth to vendor
# another dependency just for this.
class _PackageFile(TypedDict):
file: str
hash: str


class PackageFile(_PackageFile, total=False):
url: str # not required for file dependencies
size: int
upload_time: str


class Package(PackageSpecification):
AVAILABLE_PYTHONS: ClassVar[set[str]] = {
"2",
Expand Down
30 changes: 30 additions & 0 deletions src/poetry/core/packages/utils/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import posixpath
import re
import sys
import urllib.parse as urlparse

from datetime import datetime
from functools import cached_property
from typing import TYPE_CHECKING

Expand All @@ -24,6 +26,8 @@ def __init__(
hashes: Mapping[str, str] | None = None,
metadata: str | bool | dict[str, str] | None = None,
yanked: str | bool = False,
size: int | None = None,
upload_time: str | None = None,
) -> None:
"""
Object representing a parsed link from https://pypi.python.org/simple/*
Expand All @@ -49,6 +53,10 @@ def __init__(
A string, if the data-yanked attribute has a string value.
True, if the data-yanked attribute is present but has no value.
According to PEP 592.
size:
The size of the files in bytes.
upload_time:
The upload time of the file as an ISO 8601 formatted string.
"""

# url can be a UNC windows share
Expand All @@ -66,6 +74,8 @@ def __init__(

self._metadata = metadata
self._yanked = yanked
self._size = size
self._upload_time = upload_time

def __str__(self) -> str:
if self.requires_python:
Expand Down Expand Up @@ -224,3 +234,23 @@ def yanked_reason(self) -> str:
if isinstance(self._yanked, str):
return self._yanked
return ""

@cached_property
def size(self) -> int | None:
return self._size

@cached_property
def upload_time_isoformat(self) -> str | None:
return self._upload_time

@cached_property
def upload_time(self) -> datetime | None:
if self._upload_time is None:
return None
try:
# Python < 3.11 does not support 'Z' suffix for UTC, replace it with '+00:00'
if sys.version_info < (3, 11) and self._upload_time.endswith("Z"):
return datetime.fromisoformat(self._upload_time[:-1] + "+00:00")
return datetime.fromisoformat(self._upload_time)
except ValueError:
return None
45 changes: 45 additions & 0 deletions tests/packages/utils/test_utils_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import uuid

from datetime import datetime
from datetime import timezone
from hashlib import sha256

import pytest
Expand Down Expand Up @@ -157,3 +159,46 @@ def test_package_link_pep592_yanked(

assert link.yanked == expected_yanked
assert link.yanked_reason == expected_yanked_reason


def test_package_link_size_default_is_none() -> None:
link = Link("https://example.org")

assert link.size is None


def test_package_link_size() -> None:
link = Link("https://example.org", size=1234)

assert link.size == 1234


def test_package_link_upload_time_default_is_none() -> None:
link = Link("https://example.org")

assert link.upload_time is None


@pytest.mark.parametrize(
("upload_time", "expected"),
[
("2023-06-15T08:30:45Z", datetime(2023, 6, 15, 8, 30, 45, tzinfo=timezone.utc)),
(
"2022-12-31T23:59:59+00:00",
datetime(2022, 12, 31, 23, 59, 59, tzinfo=timezone.utc),
),
(
"2023-06-15T08:30:45.123456Z",
datetime(2023, 6, 15, 8, 30, 45, 123456, tzinfo=timezone.utc),
),
(
"2023-06-15T10:30:45+02:00",
datetime(2023, 6, 15, 8, 30, 45, tzinfo=timezone.utc),
),
("not-a-timestamp", None),
],
)
def test_package_link_upload_time(upload_time: str, expected: datetime) -> None:
link = Link("https://example.org", upload_time=upload_time)

assert link.upload_time == expected