sci-protocol 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pysci/__init__.py +77 -0
- pysci/__main__.py +8 -0
- pysci/_cli/__init__.py +5 -0
- pysci/_cli/checks.py +173 -0
- pysci/_cli/clients/__init__.py +0 -0
- pysci/_cli/clients/archive.py +456 -0
- pysci/_cli/clients/credentials.py +54 -0
- pysci/_cli/clients/github.py +734 -0
- pysci/_cli/clients/hosting.py +238 -0
- pysci/_cli/clients/http.py +481 -0
- pysci/_cli/clients/huggingface.py +492 -0
- pysci/_cli/clients/index.py +435 -0
- pysci/_cli/clients/tsa.py +223 -0
- pysci/_cli/commands/__init__.py +44 -0
- pysci/_cli/commands/check.py +115 -0
- pysci/_cli/commands/claim.py +179 -0
- pysci/_cli/commands/common.py +91 -0
- pysci/_cli/commands/folders.py +83 -0
- pysci/_cli/commands/index.py +51 -0
- pysci/_cli/commands/keygen.py +58 -0
- pysci/_cli/commands/license.py +75 -0
- pysci/_cli/commands/protocol.py +72 -0
- pysci/_cli/commands/publish.py +282 -0
- pysci/_cli/commands/pull.py +337 -0
- pysci/_cli/commands/read.py +364 -0
- pysci/_cli/commands/revoke.py +207 -0
- pysci/_cli/commands/search.py +400 -0
- pysci/_cli/commands/sign.py +284 -0
- pysci/_cli/commands/submit.py +172 -0
- pysci/_cli/commands/timestamp.py +196 -0
- pysci/_cli/commands/verify.py +163 -0
- pysci/_cli/config.py +233 -0
- pysci/_cli/context.py +79 -0
- pysci/_cli/destinations.py +163 -0
- pysci/_cli/errors.py +168 -0
- pysci/_cli/main.py +229 -0
- pysci/_cli/output.py +80 -0
- pysci/_cli/retrieval.py +51 -0
- pysci/_cli/spec.py +113 -0
- pysci/_cli/storage/__init__.py +0 -0
- pysci/_cli/storage/catalog.py +371 -0
- pysci/_cli/storage/files.py +252 -0
- pysci/_cli/storage/inputs.py +205 -0
- pysci/_cli/storage/keys.py +132 -0
- pysci/_cli/storage/local_publish.py +88 -0
- pysci/accounts.py +135 -0
- pysci/baseline.py +142 -0
- pysci/bundle.py +878 -0
- pysci/citations.py +233 -0
- pysci/data/freetsa-cacert.pem +45 -0
- pysci/data/sci-baseline-LICENSE.txt +457 -0
- pysci/data/sci-protocol.md +613 -0
- pysci/der.py +146 -0
- pysci/discovery.py +461 -0
- pysci/draft.py +374 -0
- pysci/ed25519.py +153 -0
- pysci/errors.py +204 -0
- pysci/ids.py +61 -0
- pysci/jsonstrict.py +225 -0
- pysci/keys.py +247 -0
- pysci/manifest.py +302 -0
- pysci/metadata.py +406 -0
- pysci/paths.py +136 -0
- pysci/png.py +217 -0
- pysci/py.typed +0 -0
- pysci/records.py +384 -0
- pysci/rfc3339.py +66 -0
- pysci/schemas.py +75 -0
- pysci/source.py +166 -0
- pysci/specification.py +58 -0
- pysci/submission.py +225 -0
- pysci/templates.py +129 -0
- pysci/text.py +226 -0
- pysci/timestamps.py +867 -0
- pysci/zipformat.py +400 -0
- sci_protocol-0.2.0.dist-info/METADATA +112 -0
- sci_protocol-0.2.0.dist-info/RECORD +80 -0
- sci_protocol-0.2.0.dist-info/WHEEL +4 -0
- sci_protocol-0.2.0.dist-info/entry_points.txt +2 -0
- sci_protocol-0.2.0.dist-info/licenses/LICENSE +202 -0
pysci/baseline.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Baseline publications (protocol §2.1)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from functools import cache
|
|
9
|
+
from importlib import resources
|
|
10
|
+
from typing import Final
|
|
11
|
+
|
|
12
|
+
from .errors import Issue
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"APPROVED_LICENSE_SHA256",
|
|
16
|
+
"BASELINE_LICENSE_SHA256",
|
|
17
|
+
"BaselineLimits",
|
|
18
|
+
"BaselineReport",
|
|
19
|
+
"baseline_license_bytes",
|
|
20
|
+
"evaluate_baseline",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
BASELINE_LICENSE_SHA256: Final = "8ccfd5fdaa93e5e71dc8812bf11e0a7198bd0a534fa64b8970fd2de58e6c2478"
|
|
24
|
+
"""SHA-256 of the SCI baseline license 0.1."""
|
|
25
|
+
|
|
26
|
+
APPROVED_LICENSE_SHA256: Final = frozenset({BASELINE_LICENSE_SHA256})
|
|
27
|
+
"""Hashes of every approved baseline license."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True, slots=True)
|
|
31
|
+
class BaselineLimits:
|
|
32
|
+
"""The §2.1 maxima."""
|
|
33
|
+
|
|
34
|
+
zip_bytes: int = 64 * 1024 * 1024
|
|
35
|
+
files: int = 1000
|
|
36
|
+
display_items: int = 3
|
|
37
|
+
manifest_bytes: int = 256 * 1024
|
|
38
|
+
metadata_bytes: int = 64 * 1024
|
|
39
|
+
readme_bytes: int = 1024 * 1024
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
BASELINE_LIMITS: Final = BaselineLimits()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@cache
|
|
46
|
+
def baseline_license_bytes() -> bytes:
|
|
47
|
+
"""Exact bytes of the approved SCI baseline license 0.1 (bundled package data).
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
RuntimeError: the packaged file is damaged (hash mismatch).
|
|
51
|
+
"""
|
|
52
|
+
data = resources.files("pysci").joinpath("data/sci-baseline-LICENSE.txt").read_bytes()
|
|
53
|
+
if hashlib.sha256(data).hexdigest() != BASELINE_LICENSE_SHA256:
|
|
54
|
+
raise RuntimeError("bundled baseline license does not match its approved hash")
|
|
55
|
+
return data
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True, slots=True)
|
|
59
|
+
class BaselineReport:
|
|
60
|
+
"""Result of evaluating the baseline requirements.
|
|
61
|
+
|
|
62
|
+
``eligible`` is True only when every limit holds and the license is an
|
|
63
|
+
approved one. ``reasons`` lists each unmet requirement as an issue.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
eligible: bool
|
|
67
|
+
zip_bytes: int
|
|
68
|
+
reasons: tuple[Issue, ...] = field(default_factory=tuple)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def evaluate_baseline(
|
|
72
|
+
*,
|
|
73
|
+
zip_bytes: int,
|
|
74
|
+
file_count: int,
|
|
75
|
+
display_items: int,
|
|
76
|
+
manifest_bytes: int,
|
|
77
|
+
metadata_bytes: int,
|
|
78
|
+
readme_bytes: int,
|
|
79
|
+
license_sha256: str | None,
|
|
80
|
+
display_problems: Mapping[str, str],
|
|
81
|
+
limits: BaselineLimits = BASELINE_LIMITS,
|
|
82
|
+
) -> BaselineReport:
|
|
83
|
+
"""Evaluate §2.1 for an otherwise valid publication (or a draft plan).
|
|
84
|
+
|
|
85
|
+
``file_count`` counts the manifest ``files`` entries (metadata, README, and
|
|
86
|
+
LICENSE included; manifest and signature excluded). ``license_sha256`` is
|
|
87
|
+
the SHA-256 of the ``LICENSE`` bytes, or None if there is no LICENSE.
|
|
88
|
+
``display_problems`` maps each display file outside the §2.1 display
|
|
89
|
+
requirements to the reason (see :func:`pysci.png.display_problem`).
|
|
90
|
+
"""
|
|
91
|
+
reasons: list[Issue] = []
|
|
92
|
+
|
|
93
|
+
def check(value: int, maximum: int, code: str, what: str) -> None:
|
|
94
|
+
if value > maximum:
|
|
95
|
+
reasons.append(
|
|
96
|
+
Issue(
|
|
97
|
+
code,
|
|
98
|
+
f"baseline allows at most {maximum:,} {what}",
|
|
99
|
+
section="2.1",
|
|
100
|
+
observed=f"{value:,}",
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
check(zip_bytes, limits.zip_bytes, "baseline_zip_bytes", "bytes in the complete ZIP")
|
|
105
|
+
check(file_count, limits.files, "baseline_file_count", "entries in the manifest files object")
|
|
106
|
+
check(display_items, limits.display_items, "baseline_display_items", "display items")
|
|
107
|
+
check(
|
|
108
|
+
manifest_bytes, limits.manifest_bytes, "baseline_manifest_bytes", "bytes of manifest.json"
|
|
109
|
+
)
|
|
110
|
+
check(metadata_bytes, limits.metadata_bytes, "baseline_metadata_bytes", "bytes of sci.json")
|
|
111
|
+
check(readme_bytes, limits.readme_bytes, "baseline_readme_bytes", "bytes of README.md")
|
|
112
|
+
for path, problem in display_problems.items():
|
|
113
|
+
reasons.append(
|
|
114
|
+
Issue(
|
|
115
|
+
"baseline_display_file",
|
|
116
|
+
"display files must be static PNGs named .png: at most 2 MiB, 8,000,000 pixels, "
|
|
117
|
+
"and 8,192 pixels per side; convert the file or remove it from display",
|
|
118
|
+
section="2.1",
|
|
119
|
+
file=path,
|
|
120
|
+
observed=problem,
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
if license_sha256 is None:
|
|
124
|
+
reasons.append(
|
|
125
|
+
Issue(
|
|
126
|
+
"baseline_license_missing",
|
|
127
|
+
"baseline requires LICENSE with the exact bytes of an approved license",
|
|
128
|
+
section="2.1",
|
|
129
|
+
file="LICENSE",
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
elif license_sha256 not in APPROVED_LICENSE_SHA256:
|
|
133
|
+
reasons.append(
|
|
134
|
+
Issue(
|
|
135
|
+
"baseline_license_not_approved",
|
|
136
|
+
"LICENSE must be byte-identical to an approved baseline license",
|
|
137
|
+
section="2.1",
|
|
138
|
+
file="LICENSE",
|
|
139
|
+
observed=license_sha256,
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
return BaselineReport(eligible=not reasons, zip_bytes=zip_bytes, reasons=tuple(reasons))
|