Skip to content

Commit 45ea863

Browse files
committed
use depgather.models for DefaultOnNoneModel, pypi models; add last_updated field
1 parent d825a58 commit 45ea863

20 files changed

Lines changed: 147 additions & 265 deletions

README.md

Lines changed: 58 additions & 85 deletions
Large diffs are not rendered by default.

attestationcheck/io/fmt.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import json
3131
import re
3232
from collections import OrderedDict
33+
from datetime import datetime
3334
from importlib.metadata import PackageNotFoundError, version
3435
from io import StringIO
3536
from pathlib import Path
@@ -94,14 +95,17 @@ def ansi(
9495
if len(errors) > 0:
9596
table = Table(title="\nList Of Errors")
9697
table.add_column("Package", style="magenta")
97-
_ = [table.add_row(x.get("name", "?")) for x in errors]
98+
table.add_column("Error Code", style="magenta")
99+
_ = [table.add_row(x.get("name", "?"), str(x.get("httpErrorCode", -1))) for x in errors]
98100
console.print(table)
99101

100102
table = Table(title="\nList Of Packages")
101103
if name_bool := "name" in packages[0]:
102104
table.add_column("Package", header_style="magenta")
103105
if attestation_info := "attestation_info" in packages[0]:
104106
table.add_column("Attestation Info", header_style="magenta")
107+
if last_updated := "last_updated" in packages[0]:
108+
table.add_column("Last Updated", header_style="magenta")
105109

106110
attestation_info_lookup = {
107111
AttestationInfo.NONE: "[red]Unsupported[/]",
@@ -119,6 +123,7 @@ def ansi(
119123
if attestation_info
120124
else []
121125
)
126+
+ ([str(x.get("last_updated"))] if last_updated else [])
122127
)
123128
)
124129
for x in packages
@@ -207,7 +212,7 @@ def raw(packages: list[dict[str, Any]]) -> str:
207212
"""
208213
Format to json.
209214
210-
:param list[dict[str, Any]] packages: list of PackageInfo, representes as a dict to format.
215+
:param list[dict[str, Any]] packages: list of PackageInfo, represents as a dict to format.
211216
:return str: string to send to specified output in json format
212217
"""
213218
return json.dumps(
@@ -216,6 +221,7 @@ def raw(packages: list[dict[str, Any]]) -> str:
216221
"packages": packages,
217222
},
218223
indent="\t",
224+
default=lambda obj: obj.isoformat() if isinstance(obj, datetime) else obj,
219225
)
220226

221227

attestationcheck/models/attestation.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1+
from depgather.models.defaultonnone import DefaultOnNoneModel
12
from pydantic import Field
23

3-
from attestationcheck.models.defaultonnone import DefaultOnNoneModel
4-
54

65
class Envelope(DefaultOnNoneModel):
76
signature: str = ""

attestationcheck/models/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from dataclasses import field
44

5-
from attestationcheck.models.defaultonnone import DefaultOnNoneModel
5+
from depgather.models.defaultonnone import DefaultOnNoneModel
66

77

88
class LC_Config(DefaultOnNoneModel):

attestationcheck/models/defaultonnone.py

Lines changed: 0 additions & 22 deletions
This file was deleted.

attestationcheck/models/packageinfo.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import datetime
34
from collections.abc import Generator
45
from dataclasses import dataclass, field, fields
56
from enum import StrEnum
@@ -49,10 +50,12 @@ class PackageInfo:
4950
is_attestation_valid: bool = False
5051
is_attestation_verified: bool = False
5152

53+
last_updated: datetime.datetime = datetime.datetime(1970, 1, 1, tzinfo=datetime.UTC)
54+
5255
httpErrorCode: int = 0
5356

5457
def __post_init__(self) -> None:
55-
"""Set the namever once the object is initialised."""
58+
"""Set the namever once the object is initialized."""
5659
self.namever = f"{self.name}-{self.version or UNKNOWN}"
5760

5861
@property

attestationcheck/models/pypijson.py

Lines changed: 0 additions & 93 deletions
This file was deleted.

attestationcheck/packageinforesolver.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22

33
from __future__ import annotations
44

5+
import datetime
56
from concurrent.futures import ThreadPoolExecutor
67
from pathlib import Path
78
from typing import Any
89

910
import requests
11+
from depgather.models.pypijson import Info, ProjectResponse
1012
from depgather.parse import gather
1113
from packaging.utils import canonicalize_name
1214
from pypi_attestations._impl import AttestationBundle, Provenance
1315

1416
from attestationcheck.models.packageinfo import PackageInfo, PackageLike
15-
from attestationcheck.models.pypijson import Info, ProjectResponse
1617
from attestationcheck.session import session
1718
from attestationcheck.verify_attestation import validate_attestation, verify_attestation
1819

@@ -108,6 +109,7 @@ def _get_package_info(self, package: PackageLike) -> PackageInfo:
108109
is_supported_publisher=rpi.is_supported_publisher(),
109110
is_attestation_present=isinstance(attestation_bundle, list),
110111
httpErrorCode=rpi.http_code if rpi.http_code != HTTP_OK else 0,
112+
last_updated=rpi.get_lastUpdated(),
111113
)
112114

113115
if isinstance(attestation_bundle, list):
@@ -168,8 +170,7 @@ def is_supported_publisher(self) -> bool:
168170
return any((url.host or "").startswith(PUBLISHER_HOSTS) for url in urls)
169171

170172
def get_fileinfo(self) -> tuple[str, str] | tuple[None, None]:
171-
files = [x or "" for x in self.resp.urls]
172-
wheel_files = [x for x in files if x.filename.endswith(".whl")]
173+
wheel_files = [x for x in self.resp.urls if x.filename.endswith(".whl")]
173174
if len(wheel_files) > 0:
174175
f = wheel_files[-1]
175176
return f.filename, f.digests.sha256
@@ -188,3 +189,10 @@ def get_attestation_bundle(self) -> list[AttestationBundle] | int:
188189
return Provenance.model_validate(attestation_bundle).attestation_bundles
189190
return rc
190191
return -3
192+
193+
def get_lastUpdated(self) -> datetime.datetime:
194+
files = self.resp.urls
195+
if len(files) > 0:
196+
f = files[-1]
197+
return f.upload_time_iso_8601
198+
return datetime.datetime(1970, 1, 1, tzinfo=datetime.UTC)

attestationcheck/session.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import appdirs
21
import requests_cache
2+
from platformdirs import PlatformDirs
33

4-
session = requests_cache.CachedSession(appdirs.user_cache_dir("attestationcheck", "fredhappyface"))
4+
dirs = PlatformDirs("attestationcheck", "fredhappyface")
5+
6+
7+
session = requests_cache.CachedSession(dirs.user_cache_dir)

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "attestationcheck"
3-
version = "0.1.1"
3+
version = "0.2.0"
44
description = "Output the attestation status used by dependencies. e.g. Verified, Valid, Supported by package host etc."
55
authors = [{ name = "FredHappyface" }]
66
requires-python = ">=3.12"
@@ -21,10 +21,10 @@ classifiers = [
2121
"Topic :: Utilities",
2222
]
2323
dependencies = [
24-
"appdirs>=1.4.4",
2524
"configurator>=3.2.0",
26-
"depgather>=0.4.0",
25+
"depgather>=0.5.0",
2726
"markdown>=3.10.2",
27+
"platformdirs>=4.10.0",
2828
"pydantic>=2.13.4",
2929
"pypi-attestations>=0.0.29",
3030
"requests>=2.32.5",

0 commit comments

Comments
 (0)