typedstandards 0.1.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.
- typedstandards/__init__.py +67 -0
- typedstandards/_badge.py +167 -0
- typedstandards/_cli.py +74 -0
- typedstandards/_commands.py +156 -0
- typedstandards/_comparison.py +164 -0
- typedstandards/_node.py +68 -0
- typedstandards/_notebook.py +200 -0
- typedstandards/_show.py +381 -0
- typedstandards/_sidecar.py +102 -0
- typedstandards/_vendor/node_modules/.package-lock.json +89 -0
- typedstandards/_vendor/node_modules/@noble/curves/LICENSE +21 -0
- typedstandards/_vendor/node_modules/@noble/curves/README.md +844 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/bls.d.ts +429 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/bls.js +568 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/curve.d.ts +497 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/curve.js +752 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/der.d.ts +97 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/der.js +168 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/edwards.d.ts +329 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/edwards.js +712 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/fft.d.ts +444 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/fft.js +644 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/frost.d.ts +471 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/frost.js +870 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/hash-to-curve.d.ts +291 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/hash-to-curve.js +568 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/modular.d.ts +581 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/modular.js +972 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/montgomery.d.ts +185 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/montgomery.js +311 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/oprf.d.ts +354 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/oprf.js +370 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/poseidon.d.ts +198 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/poseidon.js +482 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/tower.d.ts +211 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/tower.js +949 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/weierstrass.d.ts +455 -0
- typedstandards/_vendor/node_modules/@noble/curves/abstract/weierstrass.js +1165 -0
- typedstandards/_vendor/node_modules/@noble/curves/bls12-381.d.ts +23 -0
- typedstandards/_vendor/node_modules/@noble/curves/bls12-381.js +753 -0
- typedstandards/_vendor/node_modules/@noble/curves/bn254.d.ts +74 -0
- typedstandards/_vendor/node_modules/@noble/curves/bn254.js +275 -0
- typedstandards/_vendor/node_modules/@noble/curves/ed25519.d.ts +212 -0
- typedstandards/_vendor/node_modules/@noble/curves/ed25519.js +649 -0
- typedstandards/_vendor/node_modules/@noble/curves/ed448.d.ts +186 -0
- typedstandards/_vendor/node_modules/@noble/curves/ed448.js +623 -0
- typedstandards/_vendor/node_modules/@noble/curves/index.d.ts +1 -0
- typedstandards/_vendor/node_modules/@noble/curves/index.js +38 -0
- typedstandards/_vendor/node_modules/@noble/curves/misc.d.ts +113 -0
- typedstandards/_vendor/node_modules/@noble/curves/misc.js +211 -0
- typedstandards/_vendor/node_modules/@noble/curves/nist.d.ts +138 -0
- typedstandards/_vendor/node_modules/@noble/curves/nist.js +270 -0
- typedstandards/_vendor/node_modules/@noble/curves/package.json +98 -0
- typedstandards/_vendor/node_modules/@noble/curves/secp256k1.d.ts +156 -0
- typedstandards/_vendor/node_modules/@noble/curves/secp256k1.js +474 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/bls.ts +1063 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/curve.ts +1080 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/der.ts +223 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/edwards.ts +1045 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/fft.ts +926 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/frost.ts +1477 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/hash-to-curve.ts +717 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/modular.ts +1208 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/montgomery.ts +420 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/oprf.ts +805 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/poseidon.ts +556 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/tower.ts +1150 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/abstract/weierstrass.ts +1666 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/bls12-381.ts +862 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/bn254.ts +310 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/ed25519.ts +736 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/ed448.ts +743 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/index.ts +37 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/misc.ts +260 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/nist.ts +329 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/secp256k1.ts +591 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/utils.ts +930 -0
- typedstandards/_vendor/node_modules/@noble/curves/src/webcrypto.ts +649 -0
- typedstandards/_vendor/node_modules/@noble/curves/utils.d.ts +610 -0
- typedstandards/_vendor/node_modules/@noble/curves/utils.js +710 -0
- typedstandards/_vendor/node_modules/@noble/curves/webcrypto.d.ts +240 -0
- typedstandards/_vendor/node_modules/@noble/curves/webcrypto.js +431 -0
- typedstandards/_vendor/node_modules/@noble/hashes/LICENSE +21 -0
- typedstandards/_vendor/node_modules/@noble/hashes/README.md +478 -0
- typedstandards/_vendor/node_modules/@noble/hashes/_blake.d.ts +20 -0
- typedstandards/_vendor/node_modules/@noble/hashes/_blake.js +59 -0
- typedstandards/_vendor/node_modules/@noble/hashes/_md.d.ts +95 -0
- typedstandards/_vendor/node_modules/@noble/hashes/_md.js +209 -0
- typedstandards/_vendor/node_modules/@noble/hashes/_u64.d.ts +36 -0
- typedstandards/_vendor/node_modules/@noble/hashes/_u64.js +77 -0
- typedstandards/_vendor/node_modules/@noble/hashes/argon2.d.ts +148 -0
- typedstandards/_vendor/node_modules/@noble/hashes/argon2.js +577 -0
- typedstandards/_vendor/node_modules/@noble/hashes/blake1.d.ts +162 -0
- typedstandards/_vendor/node_modules/@noble/hashes/blake1.js +630 -0
- typedstandards/_vendor/node_modules/@noble/hashes/blake2.d.ts +203 -0
- typedstandards/_vendor/node_modules/@noble/hashes/blake2.js +573 -0
- typedstandards/_vendor/node_modules/@noble/hashes/blake3.d.ts +68 -0
- typedstandards/_vendor/node_modules/@noble/hashes/blake3.js +310 -0
- typedstandards/_vendor/node_modules/@noble/hashes/eskdf.d.ts +91 -0
- typedstandards/_vendor/node_modules/@noble/hashes/eskdf.js +225 -0
- typedstandards/_vendor/node_modules/@noble/hashes/hkdf.d.ts +70 -0
- typedstandards/_vendor/node_modules/@noble/hashes/hkdf.js +152 -0
- typedstandards/_vendor/node_modules/@noble/hashes/hmac.d.ts +50 -0
- typedstandards/_vendor/node_modules/@noble/hashes/hmac.js +93 -0
- typedstandards/_vendor/node_modules/@noble/hashes/index.d.ts +1 -0
- typedstandards/_vendor/node_modules/@noble/hashes/index.js +34 -0
- typedstandards/_vendor/node_modules/@noble/hashes/legacy.d.ts +107 -0
- typedstandards/_vendor/node_modules/@noble/hashes/legacy.js +348 -0
- typedstandards/_vendor/node_modules/@noble/hashes/package.json +91 -0
- typedstandards/_vendor/node_modules/@noble/hashes/pbkdf2.d.ts +66 -0
- typedstandards/_vendor/node_modules/@noble/hashes/pbkdf2.js +176 -0
- typedstandards/_vendor/node_modules/@noble/hashes/scrypt.d.ts +98 -0
- typedstandards/_vendor/node_modules/@noble/hashes/scrypt.js +304 -0
- typedstandards/_vendor/node_modules/@noble/hashes/sha2.d.ts +176 -0
- typedstandards/_vendor/node_modules/@noble/hashes/sha2.js +446 -0
- typedstandards/_vendor/node_modules/@noble/hashes/sha3-addons.d.ts +533 -0
- typedstandards/_vendor/node_modules/@noble/hashes/sha3-addons.js +973 -0
- typedstandards/_vendor/node_modules/@noble/hashes/sha3.d.ts +208 -0
- typedstandards/_vendor/node_modules/@noble/hashes/sha3.js +475 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/_blake.ts +67 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/_md.ts +227 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/_u64.ts +111 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/argon2.ts +693 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/blake1.ts +677 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/blake2.ts +648 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/blake3.ts +344 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/eskdf.ts +265 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/hkdf.ts +161 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/hmac.ts +130 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/index.ts +33 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/legacy.ts +361 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/pbkdf2.ts +219 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/scrypt.ts +375 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/sha2.ts +486 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/sha3-addons.ts +1157 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/sha3.ts +525 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/utils.ts +978 -0
- typedstandards/_vendor/node_modules/@noble/hashes/src/webcrypto.ts +283 -0
- typedstandards/_vendor/node_modules/@noble/hashes/utils.d.ts +560 -0
- typedstandards/_vendor/node_modules/@noble/hashes/utils.js +687 -0
- typedstandards/_vendor/node_modules/@noble/hashes/webcrypto.d.ts +121 -0
- typedstandards/_vendor/node_modules/@noble/hashes/webcrypto.js +228 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/CHANGELOG.md +88 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/LICENSE +21 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/README.md +280 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/attest.d.ts +11 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/attest.js +73 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/bin/main.js +22 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/errors.d.ts +18 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/errors.js +23 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/index.d.ts +4 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/index.js +5 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/input.d.ts +50 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/input.js +273 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/io.d.ts +30 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/io.js +4 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/offline.d.ts +7 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/offline.js +24 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/readings.d.ts +73 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/readings.js +212 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/run.d.ts +5 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/run.js +83 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/seed.d.ts +3 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/seed.js +34 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/sign.d.ts +23 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/sign.js +120 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/verify.d.ts +65 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/verify.js +202 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/view.d.ts +37 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/view.js +101 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/withdraw.d.ts +11 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/dist/withdraw.js +59 -0
- typedstandards/_vendor/node_modules/@typedstandards/cli/package.json +67 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/CHANGELOG.md +227 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/LICENSE +21 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/README.md +209 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/attestation.d.ts +131 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/attestation.js +107 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/commitment.d.ts +103 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/commitment.js +141 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/envelope.d.ts +239 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/envelope.js +193 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/index.d.ts +9 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/index.js +24 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/provenance.d.ts +62 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/provenance.js +88 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/rekor-proposal.d.ts +44 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/rekor-proposal.js +72 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/rfc3161-request.d.ts +7 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/rfc3161-request.js +67 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/signing.d.ts +77 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/signing.js +171 -0
- typedstandards/_vendor/node_modules/@typedstandards/produce-core/package.json +71 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/CHANGELOG.md +371 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/LICENSE +21 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/README.md +111 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/asn1.d.ts +36 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/asn1.js +154 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/attestation.d.ts +23 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/attestation.js +31 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/blob-ref.d.ts +67 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/blob-ref.js +133 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/canonicalization.d.ts +54 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/canonicalization.js +115 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/checks.d.ts +235 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/checks.js +496 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/did-key.d.ts +28 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/did-key.js +74 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/index.d.ts +17 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/index.js +31 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/lifecycle.d.ts +270 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/lifecycle.js +390 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/primitives.d.ts +14 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/primitives.js +37 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/profiles.d.ts +20 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/profiles.js +44 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rekor-inclusion.d.ts +109 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rekor-inclusion.js +291 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rekor.d.ts +29 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rekor.js +61 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rfc3161.d.ts +70 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rfc3161.js +309 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/self-certifying.d.ts +46 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/self-certifying.js +89 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/signature.d.ts +48 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/signature.js +86 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/trust-registry.d.ts +81 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/trust-registry.js +141 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/types.d.ts +48 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/types.js +6 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/verify.d.ts +188 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/verify.js +274 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/x509.d.ts +70 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/x509.js +228 -0
- typedstandards/_vendor/node_modules/@typedstandards/verify-core/package.json +67 -0
- typedstandards/_vendor/node_modules/canonicalize/LICENSE +201 -0
- typedstandards/_vendor/node_modules/canonicalize/README.md +65 -0
- typedstandards/_vendor/node_modules/canonicalize/bin/canonicalize.js +17 -0
- typedstandards/_vendor/node_modules/canonicalize/lib/canonicalize.d.ts +2 -0
- typedstandards/_vendor/node_modules/canonicalize/lib/canonicalize.js +49 -0
- typedstandards/_vendor/node_modules/canonicalize/package.json +64 -0
- typedstandards/_vendor/package-lock.json +96 -0
- typedstandards/_vendor/package.json +9 -0
- typedstandards/errors.py +58 -0
- typedstandards/pin.py +156 -0
- typedstandards-0.1.0.dist-info/METADATA +231 -0
- typedstandards-0.1.0.dist-info/RECORD +249 -0
- typedstandards-0.1.0.dist-info/WHEEL +4 -0
- typedstandards-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Typed Standards records from Python, through @typedstandards/cli as a child process.
|
|
2
|
+
|
|
3
|
+
The package vendors @typedstandards/cli and runs it with a located Node binary. It holds no
|
|
4
|
+
key, reads no signing seed and computes none of the format's hashes: the CLI reads the seed
|
|
5
|
+
from the environment it inherits, and does all of the format's work.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from ._badge import badge_cell
|
|
11
|
+
from ._cli import cli_entry
|
|
12
|
+
from ._cli import run as _run
|
|
13
|
+
from ._commands import attest, sign, verify, view, withdraw
|
|
14
|
+
from ._comparison import comparison_cell
|
|
15
|
+
from ._node import NODE_FLOOR, NODE_OVERRIDE, locate_node
|
|
16
|
+
from ._show import Shown, show
|
|
17
|
+
from ._sidecar import sidecar
|
|
18
|
+
from .errors import (
|
|
19
|
+
CliError,
|
|
20
|
+
CliNotVendoredError,
|
|
21
|
+
InternalError,
|
|
22
|
+
NodeLocatorError,
|
|
23
|
+
SeedError,
|
|
24
|
+
UsageError,
|
|
25
|
+
VerificationError,
|
|
26
|
+
)
|
|
27
|
+
from .pin import Pinned, pin
|
|
28
|
+
|
|
29
|
+
__version__ = "0.1.0"
|
|
30
|
+
|
|
31
|
+
#: The version of @typedstandards/cli this release vendors (package.json pins it exactly).
|
|
32
|
+
CLI_VERSION = "0.2.0"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def cli_version() -> str:
|
|
36
|
+
"""The ``version`` the vendored CLI prints for ``--version``."""
|
|
37
|
+
return _run("--version")["version"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"CLI_VERSION",
|
|
42
|
+
"NODE_FLOOR",
|
|
43
|
+
"NODE_OVERRIDE",
|
|
44
|
+
"CliError",
|
|
45
|
+
"CliNotVendoredError",
|
|
46
|
+
"InternalError",
|
|
47
|
+
"NodeLocatorError",
|
|
48
|
+
"Pinned",
|
|
49
|
+
"SeedError",
|
|
50
|
+
"Shown",
|
|
51
|
+
"UsageError",
|
|
52
|
+
"VerificationError",
|
|
53
|
+
"__version__",
|
|
54
|
+
"attest",
|
|
55
|
+
"badge_cell",
|
|
56
|
+
"cli_entry",
|
|
57
|
+
"cli_version",
|
|
58
|
+
"comparison_cell",
|
|
59
|
+
"locate_node",
|
|
60
|
+
"pin",
|
|
61
|
+
"show",
|
|
62
|
+
"sidecar",
|
|
63
|
+
"sign",
|
|
64
|
+
"verify",
|
|
65
|
+
"view",
|
|
66
|
+
"withdraw",
|
|
67
|
+
]
|
typedstandards/_badge.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""``badge_cell``: the verifier badge as a notebook's first cell (Jupyter) or a ``mo.md`` cell (Marimo).
|
|
2
|
+
|
|
3
|
+
The link and the badge follow ``@typedstandards/host-core``'s ``links.ts`` (typedstandards
|
|
4
|
+
``116882a``, ``packages/host-core/src/links.ts``): ``buildVerifyHref`` (:42-48) puts a hosted
|
|
5
|
+
bundle URL through ``?url=`` percent-encoded with ``encodeURIComponent``, and
|
|
6
|
+
``buildEmbedMarkdown`` (:70-79) writes a linked image whose destination is in angle brackets.
|
|
7
|
+
The constants are copied from :15-28. ``tests/test_badge.py`` compares this module's output with
|
|
8
|
+
strings captured from host-core's own functions.
|
|
9
|
+
|
|
10
|
+
The cell is written before the notebook is signed, and under ``raw-bytes/v1`` the notebook's
|
|
11
|
+
bytes are the record's output, so the cell can carry only facts known by name before signing:
|
|
12
|
+
the badge linked to the bundle URL, the host and the capture method. It never carries a hash or
|
|
13
|
+
a time; the verifier shows those from the signed record. Spec §8.8.4 calls such a cell "purely a
|
|
14
|
+
reader affordance".
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
from urllib.parse import quote, unquote, urlsplit
|
|
22
|
+
|
|
23
|
+
from ._notebook import insert_cell, source_lines
|
|
24
|
+
|
|
25
|
+
#: host-core links.ts:16, the verifier's canonical origin.
|
|
26
|
+
CANONICAL_ORIGIN = "https://typedstandards.org"
|
|
27
|
+
#: links.ts:19.
|
|
28
|
+
BADGE_ASSET_PATH = "/badge/typed-standards-verify.svg"
|
|
29
|
+
#: links.ts:22-23.
|
|
30
|
+
BADGE_WIDTH = 248
|
|
31
|
+
BADGE_HEIGHT = 30
|
|
32
|
+
#: links.ts:28: it describes the action, not a verdict.
|
|
33
|
+
BADGE_ALT = "Verify this record with Typed Standards"
|
|
34
|
+
|
|
35
|
+
#: The characters ``encodeURIComponent`` leaves unescaped besides ASCII letters and digits.
|
|
36
|
+
_URI_COMPONENT_SAFE = "-_.!~*'()"
|
|
37
|
+
|
|
38
|
+
#: The badge cell's id (nbformat 4.5).
|
|
39
|
+
BADGE_CELL_ID = "typedstandards-badge"
|
|
40
|
+
|
|
41
|
+
_HEX64 = re.compile(r"[0-9a-fA-F]{64}")
|
|
42
|
+
_DATE_OR_TIME = re.compile(r"\d{4}-\d{2}-\d{2}|\d{2}:\d{2}(?::\d{2})?")
|
|
43
|
+
_UNSAFE_IN_TABLE = re.compile(r"[|`\\\"\x00-\x1f\x7f]")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def encode_uri_component(value: str) -> str:
|
|
47
|
+
"""JavaScript's ``encodeURIComponent``: UTF-8, every byte escaped but ``A-Z a-z 0-9 - _ . ! ~ * ' ( )``."""
|
|
48
|
+
return quote(value, safe=_URI_COMPONENT_SAFE)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def verify_href(bundle_url: str) -> str:
|
|
52
|
+
"""The verifier deep link for a hosted bundle: ``https://typedstandards.org/verify?url=<encoded>``."""
|
|
53
|
+
url = bundle_url.strip()
|
|
54
|
+
if not re.match(r"https?://", url, re.IGNORECASE):
|
|
55
|
+
raise ValueError(f"bundle_url must be an http(s) URL of a served bundle, not {bundle_url!r}")
|
|
56
|
+
return f"{CANONICAL_ORIGIN}/verify?url={encode_uri_component(url)}"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def badge_markdown(bundle_url: str) -> str:
|
|
60
|
+
"""host-core's ``buildEmbedMarkdown`` for a hosted bundle (light theme)."""
|
|
61
|
+
return f"[](<{verify_href(bundle_url)}>)"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _check_fact(name: str, value: str) -> str:
|
|
65
|
+
if not isinstance(value, str) or not value.strip():
|
|
66
|
+
raise ValueError(f"{name} must be a non-empty string")
|
|
67
|
+
if _UNSAFE_IN_TABLE.search(value):
|
|
68
|
+
raise ValueError(
|
|
69
|
+
f"{name} {value!r} holds a character the cell's table cannot carry (|, `, \\, \" or a control)"
|
|
70
|
+
)
|
|
71
|
+
return value.strip()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def refuse_hash_or_time(text: str, *, authorities: tuple[str, ...] = ()) -> str:
|
|
75
|
+
"""Raise ``ValueError`` when ``text`` holds a 64-hex string, a date or a time of day.
|
|
76
|
+
|
|
77
|
+
The badge cell is part of the signed bytes, written before signing: a hash in it cannot be
|
|
78
|
+
the record's own, and a time in it cannot be the signing time, so either would mislead.
|
|
79
|
+
Each of ``authorities`` (a URL's host and port, as the text carries them) is left out of the
|
|
80
|
+
date and time check, so ``192.168.1.10:8080`` is not read as the time ``10:80``.
|
|
81
|
+
"""
|
|
82
|
+
if _HEX64.search(text):
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"the badge cell would carry a 64-hex string; it is written before signing, so it names no hash"
|
|
85
|
+
)
|
|
86
|
+
scan = text
|
|
87
|
+
for authority in authorities:
|
|
88
|
+
if authority:
|
|
89
|
+
scan = scan.replace(authority, " ")
|
|
90
|
+
if _DATE_OR_TIME.search(scan):
|
|
91
|
+
raise ValueError("the badge cell would carry a date or time; it is written before signing, so it names no time")
|
|
92
|
+
return text
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def badge_text(bundle_url: str, *, capture_method: str, host: str | None = None) -> str:
|
|
96
|
+
"""The badge cell's Markdown: the badge, a two-row table (host, capture method), one sentence."""
|
|
97
|
+
url = bundle_url.strip()
|
|
98
|
+
parts = urlsplit(url)
|
|
99
|
+
if host is None:
|
|
100
|
+
host = parts.netloc
|
|
101
|
+
host = _check_fact("host", host)
|
|
102
|
+
capture_method = _check_fact("capture_method", capture_method)
|
|
103
|
+
text = (
|
|
104
|
+
f"{badge_markdown(bundle_url)}\n"
|
|
105
|
+
"\n"
|
|
106
|
+
"| Typed Standards record | |\n"
|
|
107
|
+
"|---|---|\n"
|
|
108
|
+
f"| Host | `{host}` |\n"
|
|
109
|
+
f"| Capture method | `{capture_method}` |\n"
|
|
110
|
+
"\n"
|
|
111
|
+
"This cell is a reader affordance and is not authoritative: verification reads the signed record, "
|
|
112
|
+
"not this cell, and the verifier shows the record's signer, hash and time.\n"
|
|
113
|
+
)
|
|
114
|
+
# The cell carries the URL percent-encoded, which hides a time's ":"; so the URL past its
|
|
115
|
+
# authority (path, query, fragment) is checked as written and decoded too.
|
|
116
|
+
rest = url[len(parts.scheme) + 3 + len(parts.netloc) :]
|
|
117
|
+
refuse_hash_or_time(f"{rest}\n{unquote(rest)}")
|
|
118
|
+
return refuse_hash_or_time(text, authorities=(f"`{host}`", encode_uri_component(parts.netloc)))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _marimo_source(markdown: str) -> str:
|
|
122
|
+
"""A Marimo cell's source that renders ``markdown`` with ``mo.md``."""
|
|
123
|
+
if '"""' not in markdown and "\\" not in markdown and not markdown.endswith('"'):
|
|
124
|
+
return f'mo.md(\n """{markdown}"""\n)\n'
|
|
125
|
+
return f"mo.md({markdown!r})\n"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def badge_cell(
|
|
129
|
+
bundle_url: str,
|
|
130
|
+
*,
|
|
131
|
+
capture_method: str,
|
|
132
|
+
notebook: str | os.PathLike[str] | None = None,
|
|
133
|
+
host: str | None = None,
|
|
134
|
+
marimo: bool = False,
|
|
135
|
+
cell_id: str = BADGE_CELL_ID,
|
|
136
|
+
) -> str:
|
|
137
|
+
"""The verifier badge as a notebook cell, written before signing.
|
|
138
|
+
|
|
139
|
+
``bundle_url`` is where the record's bundle will be served (for example
|
|
140
|
+
``https://<host>/bundles/<name>.bundle.json``). ``capture_method`` is the record's
|
|
141
|
+
``captureMethod`` (for example ``script-run``). ``host`` defaults to the bundle URL's host.
|
|
142
|
+
|
|
143
|
+
Jupyter (the default): with ``notebook``, the cell is inserted as the notebook's first cell,
|
|
144
|
+
a markdown cell with id ``cell_id`` on nbformat 4.5 or later, and every other byte of the
|
|
145
|
+
file is kept. Returns the cell's Markdown. An existing cell with the same id is refused, so
|
|
146
|
+
a second call does not add a second badge.
|
|
147
|
+
|
|
148
|
+
Marimo (``marimo=True``): returns the source of a cell that renders the same Markdown with
|
|
149
|
+
``mo.md``, to paste into the app. ``notebook`` is refused, since a Marimo app is Python
|
|
150
|
+
source, not a notebook file.
|
|
151
|
+
|
|
152
|
+
The cell names no hash and no time: a URL or fact holding a 64-hex string, a date or a time
|
|
153
|
+
is refused with ``ValueError``.
|
|
154
|
+
"""
|
|
155
|
+
text = badge_text(bundle_url, capture_method=capture_method, host=host)
|
|
156
|
+
if marimo:
|
|
157
|
+
if notebook is not None:
|
|
158
|
+
raise ValueError("marimo=True returns a cell's source; it does not edit a notebook file")
|
|
159
|
+
return _marimo_source(text)
|
|
160
|
+
if notebook is not None:
|
|
161
|
+
insert_cell(
|
|
162
|
+
notebook,
|
|
163
|
+
{"cell_type": "markdown", "metadata": {}, "source": source_lines(text)},
|
|
164
|
+
where="first",
|
|
165
|
+
cell_id=cell_id,
|
|
166
|
+
)
|
|
167
|
+
return text
|
typedstandards/_cli.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Run the vendored CLI as a child process and read its result."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import subprocess
|
|
8
|
+
from collections.abc import Sequence
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from ._node import locate_node
|
|
13
|
+
from .errors import (
|
|
14
|
+
CliError,
|
|
15
|
+
CliNotVendoredError,
|
|
16
|
+
InternalError,
|
|
17
|
+
SeedError,
|
|
18
|
+
UsageError,
|
|
19
|
+
VerificationError,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
#: The CLI's entry file inside the vendored tree that hatch_build.py writes.
|
|
23
|
+
CLI_ENTRY = Path(__file__).parent / "_vendor" / "node_modules" / "@typedstandards" / "cli" / "dist" / "bin" / "main.js"
|
|
24
|
+
|
|
25
|
+
#: The CLI's exit codes (packages/cli/src/errors.ts:3-14 in typedstandards). 1 is handled apart,
|
|
26
|
+
#: since verify prints its verdict before exiting 1.
|
|
27
|
+
_RAISES: dict[int, type[CliError]] = {2: UsageError, 3: SeedError, 4: InternalError}
|
|
28
|
+
|
|
29
|
+
_log = logging.getLogger("typedstandards")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def cli_entry() -> Path:
|
|
33
|
+
"""The vendored CLI's entry file; raises :class:`CliNotVendoredError` when it is missing."""
|
|
34
|
+
if not CLI_ENTRY.is_file():
|
|
35
|
+
raise CliNotVendoredError(
|
|
36
|
+
f"the vendored @typedstandards/cli is missing ({CLI_ENTRY}); reinstall typedstandards, or in a "
|
|
37
|
+
"checkout run `uv sync --reinstall-package typedstandards`"
|
|
38
|
+
)
|
|
39
|
+
return CLI_ENTRY
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _parse(stdout: bytes) -> Any:
|
|
43
|
+
return json.loads(stdout.decode("utf-8"))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def run(command: str, args: Sequence[str] = (), *, stdin: bytes | None = None) -> Any:
|
|
47
|
+
"""Run one CLI command and return its stdout parsed as JSON.
|
|
48
|
+
|
|
49
|
+
The child inherits this process's environment: no ``env`` is passed, and nothing here reads
|
|
50
|
+
or sets a variable for it. What the CLI writes on stderr when it succeeds (attention
|
|
51
|
+
readings, such as an offline ``registry_unavailable``) is logged at INFO on the
|
|
52
|
+
``typedstandards`` logger.
|
|
53
|
+
"""
|
|
54
|
+
node = locate_node()
|
|
55
|
+
entry = cli_entry()
|
|
56
|
+
feed: dict[str, Any] = {"input": stdin} if stdin is not None else {"stdin": subprocess.DEVNULL}
|
|
57
|
+
proc = subprocess.run([node, str(entry), command, *args], capture_output=True, check=False, **feed)
|
|
58
|
+
stderr = proc.stderr.decode("utf-8", errors="replace")
|
|
59
|
+
code = proc.returncode
|
|
60
|
+
if code == 0:
|
|
61
|
+
for line in stderr.splitlines():
|
|
62
|
+
_log.info("%s", line)
|
|
63
|
+
try:
|
|
64
|
+
return _parse(proc.stdout)
|
|
65
|
+
except ValueError as err:
|
|
66
|
+
raise CliError(0, f"stdout is not JSON ({err}); stderr: {stderr}", command) from err
|
|
67
|
+
if code == 1:
|
|
68
|
+
# verify prints {ok: false, ...} before exiting 1; sign, withdraw and attest print nothing.
|
|
69
|
+
try:
|
|
70
|
+
document = _parse(proc.stdout) if proc.stdout.strip() else None
|
|
71
|
+
except ValueError:
|
|
72
|
+
document = None
|
|
73
|
+
raise VerificationError(code, stderr, command, document)
|
|
74
|
+
raise _RAISES.get(code, CliError)(code, stderr, command)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""The five pass-through commands: ``sign``, ``withdraw``, ``attest``, ``view``, ``verify``.
|
|
2
|
+
|
|
3
|
+
Each takes the CLI's inputs as Python values and returns the CLI's stdout parsed as JSON.
|
|
4
|
+
An input given as a mapping is sent as JSON on standard input (``--input -``); a ``str`` or
|
|
5
|
+
``os.PathLike`` is a path the CLI reads. The wrapper computes nothing the format defines: the
|
|
6
|
+
CLI builds, signs, hashes and verifies.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import tempfile
|
|
14
|
+
from collections.abc import Iterable, Mapping
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from ._cli import run
|
|
19
|
+
|
|
20
|
+
#: An input: a JSON object as a mapping, or the path of a JSON file.
|
|
21
|
+
Input = Mapping[str, Any] | str | os.PathLike[str]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _json_bytes(value: Mapping[str, Any]) -> bytes:
|
|
25
|
+
return json.dumps(value, ensure_ascii=False, allow_nan=False).encode("utf-8")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _input_args(value: Input, flag: str) -> tuple[list[str], bytes | None]:
|
|
29
|
+
if isinstance(value, Mapping):
|
|
30
|
+
return [flag, "-"], _json_bytes(value)
|
|
31
|
+
if isinstance(value, (str, os.PathLike)):
|
|
32
|
+
return [flag, os.fspath(value)], None
|
|
33
|
+
raise TypeError(f"{flag} takes a mapping (sent as JSON on stdin) or a path, not {type(value).__name__}")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _as_file(value: Input, directory: str, name: str, what: str) -> str:
|
|
37
|
+
if isinstance(value, Mapping):
|
|
38
|
+
path = Path(directory) / name
|
|
39
|
+
path.write_bytes(_json_bytes(value))
|
|
40
|
+
return str(path)
|
|
41
|
+
if isinstance(value, (str, os.PathLike)):
|
|
42
|
+
return os.fspath(value)
|
|
43
|
+
raise TypeError(f"{what} takes a mapping or a path, not {type(value).__name__}")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def sign(
|
|
47
|
+
input: Input,
|
|
48
|
+
*,
|
|
49
|
+
output_file: str | os.PathLike[str] | None = None,
|
|
50
|
+
output_url: str | None = None,
|
|
51
|
+
content_type: str | None = None,
|
|
52
|
+
) -> dict[str, Any]:
|
|
53
|
+
"""``typedstandards sign``: build and sign a record from an envelope input.
|
|
54
|
+
|
|
55
|
+
``output_file`` signs a file's bytes inline under ``raw-bytes/v1``; with ``output_url``, by
|
|
56
|
+
reference as a BlobRef (``content_type`` names its type). Returns
|
|
57
|
+
``{package, envelopeHash, signature}``. The CLI reads the signing seed from its own
|
|
58
|
+
environment, which it inherits from this process.
|
|
59
|
+
"""
|
|
60
|
+
args, stdin = _input_args(input, "--input")
|
|
61
|
+
if output_file is not None:
|
|
62
|
+
args += ["--output-file", os.fspath(output_file)]
|
|
63
|
+
if output_url is not None:
|
|
64
|
+
args += ["--output-url", output_url]
|
|
65
|
+
if content_type is not None:
|
|
66
|
+
args += ["--content-type", content_type]
|
|
67
|
+
return run("sign", args, stdin=stdin)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def withdraw(input: Input) -> dict[str, Any]:
|
|
71
|
+
"""``typedstandards withdraw``: sign an ``attestation/withdraws/v1``. Returns ``{node, nodeId, signature}``."""
|
|
72
|
+
args, stdin = _input_args(input, "--input")
|
|
73
|
+
return run("withdraw", args, stdin=stdin)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def attest(input: Input) -> dict[str, Any]:
|
|
77
|
+
"""``typedstandards attest``: sign a ``supersedes``, ``revises``, ``corroborates`` or ``contradicts``
|
|
78
|
+
attestation. Returns ``{node, nodeId, signature}``."""
|
|
79
|
+
args, stdin = _input_args(input, "--input")
|
|
80
|
+
return run("attest", args, stdin=stdin)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def view(
|
|
84
|
+
signed: Input,
|
|
85
|
+
*,
|
|
86
|
+
visibility: str,
|
|
87
|
+
attestations: Iterable[Input] = (),
|
|
88
|
+
trust_registry_url: str | None = None,
|
|
89
|
+
package_url: str | None = None,
|
|
90
|
+
title: str | None = None,
|
|
91
|
+
) -> dict[str, Any]:
|
|
92
|
+
"""``typedstandards view``: build the commitment view a host serves, with the package inline.
|
|
93
|
+
|
|
94
|
+
``signed`` is what :func:`sign` returned (or its path); each of ``attestations`` is what
|
|
95
|
+
:func:`withdraw` or :func:`attest` returned (or its path). Mappings are written to temporary
|
|
96
|
+
files, removed before this returns, since only one input can be standard input.
|
|
97
|
+
"""
|
|
98
|
+
if isinstance(attestations, (Mapping, str, os.PathLike)):
|
|
99
|
+
raise TypeError("attestations takes a list of attestations, not one")
|
|
100
|
+
with tempfile.TemporaryDirectory(prefix="typedstandards-view-") as directory:
|
|
101
|
+
args = ["--signed", _as_file(signed, directory, "signed.json", "signed"), "--visibility", visibility]
|
|
102
|
+
for i, attestation in enumerate(attestations):
|
|
103
|
+
args += ["--attestation", _as_file(attestation, directory, f"attestation-{i}.json", "attestations")]
|
|
104
|
+
if trust_registry_url is not None:
|
|
105
|
+
args += ["--trust-registry-url", trust_registry_url]
|
|
106
|
+
if package_url is not None:
|
|
107
|
+
args += ["--package-url", package_url]
|
|
108
|
+
if title is not None:
|
|
109
|
+
args += ["--title", title]
|
|
110
|
+
return run("view", args)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _without_trust_registry(value: Input) -> Input:
|
|
114
|
+
"""G0 D9 = A, typedstandards#136: CLI 0.2.0's verify exits 2 on a bundle's top-level
|
|
115
|
+
``trustRegistry``, which host-core inlines in every bundle it serves under a registry. Drop that
|
|
116
|
+
one key from a bundle (a document with ``packageHash``) and change nothing else; any other
|
|
117
|
+
document, and a bundle file without the key, reach the CLI as given.
|
|
118
|
+
|
|
119
|
+
Remove this workaround when the wrapper pins a CLI whose verify accepts the key.
|
|
120
|
+
"""
|
|
121
|
+
document: Any = value
|
|
122
|
+
if not isinstance(value, Mapping):
|
|
123
|
+
if not isinstance(value, (str, os.PathLike)):
|
|
124
|
+
return value
|
|
125
|
+
try:
|
|
126
|
+
document = json.loads(Path(value).read_bytes())
|
|
127
|
+
except (OSError, ValueError):
|
|
128
|
+
return value # the CLI reports an unreadable or malformed file
|
|
129
|
+
if isinstance(document, Mapping) and "packageHash" in document and "trustRegistry" in document:
|
|
130
|
+
return {key: item for key, item in document.items() if key != "trustRegistry"}
|
|
131
|
+
return value
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def verify(
|
|
135
|
+
input: Input,
|
|
136
|
+
*,
|
|
137
|
+
blobs: Iterable[str | os.PathLike[str]] = (),
|
|
138
|
+
full: bool = True,
|
|
139
|
+
) -> dict[str, Any]:
|
|
140
|
+
"""``typedstandards verify``: verify what :func:`sign` or :func:`view` printed, offline.
|
|
141
|
+
|
|
142
|
+
``full`` (the default) passes ``--json``, so the result carries every check's fields
|
|
143
|
+
(``checks``) and the lifecycle resolution (``lifecycle``) beside ``ok``, ``nodeId`` and
|
|
144
|
+
``failures``. ``blobs`` are local files for the record's BlobRefs. A record that does not
|
|
145
|
+
verify raises :class:`~typedstandards.errors.VerificationError`, whose ``document`` is the
|
|
146
|
+
verdict. A bundle's top-level ``trustRegistry`` is dropped before the CLI sees it
|
|
147
|
+
(typedstandards#136); nothing else is changed.
|
|
148
|
+
"""
|
|
149
|
+
if isinstance(blobs, (str, os.PathLike)):
|
|
150
|
+
raise TypeError("blobs takes a list of paths, not one")
|
|
151
|
+
args, stdin = _input_args(_without_trust_registry(input), "--input")
|
|
152
|
+
for blob in blobs:
|
|
153
|
+
args += ["--blob", os.fspath(blob)]
|
|
154
|
+
if full:
|
|
155
|
+
args.append("--json")
|
|
156
|
+
return run("verify", args, stdin=stdin)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""``comparison_cell``: the spec §8.7.4 comparison cell, appended as a notebook's last cell.
|
|
2
|
+
|
|
3
|
+
The cell embeds the values an execution produced as Python literals, recomputes them with the
|
|
4
|
+
caller's expression, and prints each delta, in the canonical shape of spec §8.7.4 (the hub's
|
|
5
|
+
``docs/architecture/typed-standards-specification.md``, lines 860-874 at ``6957a3a``)::
|
|
6
|
+
|
|
7
|
+
# ORIGINAL VALUES (captured at executedAt = <ISO-8601 timestamp>)
|
|
8
|
+
original = {
|
|
9
|
+
"<metric-name>": <literal value>,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# CURRENT VALUES (re-computed against live data using the same helpers + queries above)
|
|
13
|
+
current = <the caller's recompute expression>
|
|
14
|
+
|
|
15
|
+
# DELTAS
|
|
16
|
+
for k in original:
|
|
17
|
+
delta = ...
|
|
18
|
+
print(...)
|
|
19
|
+
|
|
20
|
+
The spec's block indents by one space; the cell indents by four, as Python code usually is. The
|
|
21
|
+
cell is appended before signing, so it is inside the signed bytes.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import ast
|
|
27
|
+
import json
|
|
28
|
+
import math
|
|
29
|
+
import os
|
|
30
|
+
from collections.abc import Mapping
|
|
31
|
+
from datetime import UTC, datetime
|
|
32
|
+
from typing import Any
|
|
33
|
+
|
|
34
|
+
from ._notebook import insert_cell, source_lines
|
|
35
|
+
|
|
36
|
+
#: The comparison cell's id (nbformat 4.5).
|
|
37
|
+
COMPARISON_CELL_ID = "typedstandards-comparison"
|
|
38
|
+
|
|
39
|
+
_CURRENT_COMMENT = "# CURRENT VALUES (re-computed against live data using the same helpers + queries above)"
|
|
40
|
+
_DELTAS = (
|
|
41
|
+
"# DELTAS\n"
|
|
42
|
+
"for k in original:\n"
|
|
43
|
+
" delta = (current[k] - original[k]) if isinstance(original[k], (int, float)) else (original[k], current[k])\n"
|
|
44
|
+
' print(f"{k}: original={original[k]}, current={current[k]}, delta={delta}")\n'
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _string(value: str, where: str) -> str:
|
|
49
|
+
try:
|
|
50
|
+
value.encode("utf-8")
|
|
51
|
+
except UnicodeEncodeError as err:
|
|
52
|
+
raise ValueError(f"{where} is a string that is not valid UTF-8 text") from err
|
|
53
|
+
return json.dumps(value, ensure_ascii=False) # a JSON string is also a Python string literal
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def literal(value: Any, where: str = "value") -> str:
|
|
57
|
+
"""``value`` as Python literal source. Admits ``None``, ``bool``, ``int``, finite ``float``,
|
|
58
|
+
``str``, and lists and str-keyed dicts of those; a subclass of ``int``, ``float`` or ``str``
|
|
59
|
+
(a NumPy float, an ``IntEnum``) is written as its base value. Anything else raises
|
|
60
|
+
``TypeError``, and a non-finite float ``ValueError``."""
|
|
61
|
+
if value is None:
|
|
62
|
+
return "None"
|
|
63
|
+
if isinstance(value, bool):
|
|
64
|
+
return "True" if value else "False"
|
|
65
|
+
if isinstance(value, int):
|
|
66
|
+
return repr(int(value))
|
|
67
|
+
if isinstance(value, float):
|
|
68
|
+
number = float(value)
|
|
69
|
+
if not math.isfinite(number):
|
|
70
|
+
raise ValueError(f"{where} is {number!r}, which has no Python literal")
|
|
71
|
+
return repr(number)
|
|
72
|
+
if isinstance(value, str):
|
|
73
|
+
return _string(str(value), where)
|
|
74
|
+
if type(value) is list:
|
|
75
|
+
return "[" + ", ".join(literal(item, f"{where}[{i}]") for i, item in enumerate(value)) + "]"
|
|
76
|
+
if type(value) is dict:
|
|
77
|
+
parts = []
|
|
78
|
+
for key, item in value.items():
|
|
79
|
+
if not isinstance(key, str):
|
|
80
|
+
raise TypeError(f"{where} has a key {key!r} that is not a string")
|
|
81
|
+
parts.append(f"{_string(str(key), where)}: {literal(item, f'{where}[{key!r}]')}")
|
|
82
|
+
return "{" + ", ".join(parts) + "}"
|
|
83
|
+
raise TypeError(
|
|
84
|
+
f"{where} is a {type(value).__name__}, not a literal: comparison values are None, bool, int, float, "
|
|
85
|
+
"str, or lists and str-keyed dicts of those"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _captured_at(value: str | datetime) -> str:
|
|
90
|
+
if isinstance(value, datetime):
|
|
91
|
+
if value.tzinfo is None:
|
|
92
|
+
raise ValueError("captured_at must be timezone-aware")
|
|
93
|
+
return value.astimezone(UTC).isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
|
94
|
+
if not isinstance(value, str) or "\n" in value or "\r" in value:
|
|
95
|
+
raise ValueError("captured_at must be an ISO 8601 timestamp on one line")
|
|
96
|
+
try:
|
|
97
|
+
datetime.fromisoformat(value)
|
|
98
|
+
except ValueError as err:
|
|
99
|
+
raise ValueError(f"captured_at {value!r} is not an ISO 8601 timestamp") from err
|
|
100
|
+
return value
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _recompute(expression: str) -> str:
|
|
104
|
+
if not isinstance(expression, str) or not expression.strip() or "\n" in expression or "\r" in expression:
|
|
105
|
+
raise ValueError("recompute must be one line of Python: an expression that returns the current values")
|
|
106
|
+
try:
|
|
107
|
+
ast.parse(expression, mode="eval")
|
|
108
|
+
except SyntaxError as err:
|
|
109
|
+
raise ValueError(f"recompute {expression!r} is not a Python expression") from err
|
|
110
|
+
return expression.strip()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def comparison_source(values: Mapping[str, Any], *, recompute: str, captured_at: str | datetime) -> str:
|
|
114
|
+
"""The comparison cell's source, without writing it anywhere."""
|
|
115
|
+
if not isinstance(values, Mapping) or not values:
|
|
116
|
+
raise ValueError("values must be a non-empty mapping of names to literal values")
|
|
117
|
+
items = []
|
|
118
|
+
for name, value in values.items():
|
|
119
|
+
if not isinstance(name, str) or not name:
|
|
120
|
+
raise TypeError(f"value names must be non-empty strings, not {name!r}")
|
|
121
|
+
items.append(f" {_string(name, 'a value name')}: {literal(value, repr(name))},\n")
|
|
122
|
+
original = "original = {\n" + "".join(items) + "}\n"
|
|
123
|
+
if ast.literal_eval(original.split("=", 1)[1].strip()) != dict(values): # pragma: no cover - a defect here
|
|
124
|
+
raise AssertionError("the literal does not read back as the values")
|
|
125
|
+
return (
|
|
126
|
+
f"# ORIGINAL VALUES (captured at executedAt = {_captured_at(captured_at)})\n"
|
|
127
|
+
f"{original}"
|
|
128
|
+
"\n"
|
|
129
|
+
f"{_CURRENT_COMMENT}\n"
|
|
130
|
+
f"current = {_recompute(recompute)}\n"
|
|
131
|
+
"\n"
|
|
132
|
+
f"{_DELTAS}"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def comparison_cell(
|
|
137
|
+
notebook: str | os.PathLike[str],
|
|
138
|
+
values: Mapping[str, Any],
|
|
139
|
+
*,
|
|
140
|
+
recompute: str,
|
|
141
|
+
captured_at: str | datetime,
|
|
142
|
+
cell_id: str = COMPARISON_CELL_ID,
|
|
143
|
+
) -> str:
|
|
144
|
+
"""Append the spec §8.7.4 comparison cell to ``notebook`` as its last cell; return its source.
|
|
145
|
+
|
|
146
|
+
``values`` names the values the execution produced, each a literal (``None``, ``bool``,
|
|
147
|
+
``int``, finite ``float``, ``str``, or lists and str-keyed dicts of those); anything else is
|
|
148
|
+
refused. ``recompute`` is one line of Python, an expression that returns a mapping with the
|
|
149
|
+
same names, run against live data when the notebook is re-executed (for example
|
|
150
|
+
``recompute_key_metrics()``). ``captured_at`` is the execution's time, an ISO 8601 string or
|
|
151
|
+
a timezone-aware ``datetime``.
|
|
152
|
+
|
|
153
|
+
The cell is a code cell with id ``cell_id`` (nbformat 4.5 or later), no outputs and no
|
|
154
|
+
execution count, and every other byte of the notebook is kept. Append it after executing the
|
|
155
|
+
notebook and before signing it: the cell is part of the signed bytes.
|
|
156
|
+
"""
|
|
157
|
+
source = comparison_source(values, recompute=recompute, captured_at=captured_at)
|
|
158
|
+
insert_cell(
|
|
159
|
+
notebook,
|
|
160
|
+
{"cell_type": "code", "execution_count": None, "metadata": {}, "outputs": [], "source": source_lines(source)},
|
|
161
|
+
where="last",
|
|
162
|
+
cell_id=cell_id,
|
|
163
|
+
)
|
|
164
|
+
return source
|