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.
Files changed (249) hide show
  1. typedstandards/__init__.py +67 -0
  2. typedstandards/_badge.py +167 -0
  3. typedstandards/_cli.py +74 -0
  4. typedstandards/_commands.py +156 -0
  5. typedstandards/_comparison.py +164 -0
  6. typedstandards/_node.py +68 -0
  7. typedstandards/_notebook.py +200 -0
  8. typedstandards/_show.py +381 -0
  9. typedstandards/_sidecar.py +102 -0
  10. typedstandards/_vendor/node_modules/.package-lock.json +89 -0
  11. typedstandards/_vendor/node_modules/@noble/curves/LICENSE +21 -0
  12. typedstandards/_vendor/node_modules/@noble/curves/README.md +844 -0
  13. typedstandards/_vendor/node_modules/@noble/curves/abstract/bls.d.ts +429 -0
  14. typedstandards/_vendor/node_modules/@noble/curves/abstract/bls.js +568 -0
  15. typedstandards/_vendor/node_modules/@noble/curves/abstract/curve.d.ts +497 -0
  16. typedstandards/_vendor/node_modules/@noble/curves/abstract/curve.js +752 -0
  17. typedstandards/_vendor/node_modules/@noble/curves/abstract/der.d.ts +97 -0
  18. typedstandards/_vendor/node_modules/@noble/curves/abstract/der.js +168 -0
  19. typedstandards/_vendor/node_modules/@noble/curves/abstract/edwards.d.ts +329 -0
  20. typedstandards/_vendor/node_modules/@noble/curves/abstract/edwards.js +712 -0
  21. typedstandards/_vendor/node_modules/@noble/curves/abstract/fft.d.ts +444 -0
  22. typedstandards/_vendor/node_modules/@noble/curves/abstract/fft.js +644 -0
  23. typedstandards/_vendor/node_modules/@noble/curves/abstract/frost.d.ts +471 -0
  24. typedstandards/_vendor/node_modules/@noble/curves/abstract/frost.js +870 -0
  25. typedstandards/_vendor/node_modules/@noble/curves/abstract/hash-to-curve.d.ts +291 -0
  26. typedstandards/_vendor/node_modules/@noble/curves/abstract/hash-to-curve.js +568 -0
  27. typedstandards/_vendor/node_modules/@noble/curves/abstract/modular.d.ts +581 -0
  28. typedstandards/_vendor/node_modules/@noble/curves/abstract/modular.js +972 -0
  29. typedstandards/_vendor/node_modules/@noble/curves/abstract/montgomery.d.ts +185 -0
  30. typedstandards/_vendor/node_modules/@noble/curves/abstract/montgomery.js +311 -0
  31. typedstandards/_vendor/node_modules/@noble/curves/abstract/oprf.d.ts +354 -0
  32. typedstandards/_vendor/node_modules/@noble/curves/abstract/oprf.js +370 -0
  33. typedstandards/_vendor/node_modules/@noble/curves/abstract/poseidon.d.ts +198 -0
  34. typedstandards/_vendor/node_modules/@noble/curves/abstract/poseidon.js +482 -0
  35. typedstandards/_vendor/node_modules/@noble/curves/abstract/tower.d.ts +211 -0
  36. typedstandards/_vendor/node_modules/@noble/curves/abstract/tower.js +949 -0
  37. typedstandards/_vendor/node_modules/@noble/curves/abstract/weierstrass.d.ts +455 -0
  38. typedstandards/_vendor/node_modules/@noble/curves/abstract/weierstrass.js +1165 -0
  39. typedstandards/_vendor/node_modules/@noble/curves/bls12-381.d.ts +23 -0
  40. typedstandards/_vendor/node_modules/@noble/curves/bls12-381.js +753 -0
  41. typedstandards/_vendor/node_modules/@noble/curves/bn254.d.ts +74 -0
  42. typedstandards/_vendor/node_modules/@noble/curves/bn254.js +275 -0
  43. typedstandards/_vendor/node_modules/@noble/curves/ed25519.d.ts +212 -0
  44. typedstandards/_vendor/node_modules/@noble/curves/ed25519.js +649 -0
  45. typedstandards/_vendor/node_modules/@noble/curves/ed448.d.ts +186 -0
  46. typedstandards/_vendor/node_modules/@noble/curves/ed448.js +623 -0
  47. typedstandards/_vendor/node_modules/@noble/curves/index.d.ts +1 -0
  48. typedstandards/_vendor/node_modules/@noble/curves/index.js +38 -0
  49. typedstandards/_vendor/node_modules/@noble/curves/misc.d.ts +113 -0
  50. typedstandards/_vendor/node_modules/@noble/curves/misc.js +211 -0
  51. typedstandards/_vendor/node_modules/@noble/curves/nist.d.ts +138 -0
  52. typedstandards/_vendor/node_modules/@noble/curves/nist.js +270 -0
  53. typedstandards/_vendor/node_modules/@noble/curves/package.json +98 -0
  54. typedstandards/_vendor/node_modules/@noble/curves/secp256k1.d.ts +156 -0
  55. typedstandards/_vendor/node_modules/@noble/curves/secp256k1.js +474 -0
  56. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/bls.ts +1063 -0
  57. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/curve.ts +1080 -0
  58. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/der.ts +223 -0
  59. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/edwards.ts +1045 -0
  60. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/fft.ts +926 -0
  61. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/frost.ts +1477 -0
  62. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/hash-to-curve.ts +717 -0
  63. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/modular.ts +1208 -0
  64. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/montgomery.ts +420 -0
  65. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/oprf.ts +805 -0
  66. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/poseidon.ts +556 -0
  67. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/tower.ts +1150 -0
  68. typedstandards/_vendor/node_modules/@noble/curves/src/abstract/weierstrass.ts +1666 -0
  69. typedstandards/_vendor/node_modules/@noble/curves/src/bls12-381.ts +862 -0
  70. typedstandards/_vendor/node_modules/@noble/curves/src/bn254.ts +310 -0
  71. typedstandards/_vendor/node_modules/@noble/curves/src/ed25519.ts +736 -0
  72. typedstandards/_vendor/node_modules/@noble/curves/src/ed448.ts +743 -0
  73. typedstandards/_vendor/node_modules/@noble/curves/src/index.ts +37 -0
  74. typedstandards/_vendor/node_modules/@noble/curves/src/misc.ts +260 -0
  75. typedstandards/_vendor/node_modules/@noble/curves/src/nist.ts +329 -0
  76. typedstandards/_vendor/node_modules/@noble/curves/src/secp256k1.ts +591 -0
  77. typedstandards/_vendor/node_modules/@noble/curves/src/utils.ts +930 -0
  78. typedstandards/_vendor/node_modules/@noble/curves/src/webcrypto.ts +649 -0
  79. typedstandards/_vendor/node_modules/@noble/curves/utils.d.ts +610 -0
  80. typedstandards/_vendor/node_modules/@noble/curves/utils.js +710 -0
  81. typedstandards/_vendor/node_modules/@noble/curves/webcrypto.d.ts +240 -0
  82. typedstandards/_vendor/node_modules/@noble/curves/webcrypto.js +431 -0
  83. typedstandards/_vendor/node_modules/@noble/hashes/LICENSE +21 -0
  84. typedstandards/_vendor/node_modules/@noble/hashes/README.md +478 -0
  85. typedstandards/_vendor/node_modules/@noble/hashes/_blake.d.ts +20 -0
  86. typedstandards/_vendor/node_modules/@noble/hashes/_blake.js +59 -0
  87. typedstandards/_vendor/node_modules/@noble/hashes/_md.d.ts +95 -0
  88. typedstandards/_vendor/node_modules/@noble/hashes/_md.js +209 -0
  89. typedstandards/_vendor/node_modules/@noble/hashes/_u64.d.ts +36 -0
  90. typedstandards/_vendor/node_modules/@noble/hashes/_u64.js +77 -0
  91. typedstandards/_vendor/node_modules/@noble/hashes/argon2.d.ts +148 -0
  92. typedstandards/_vendor/node_modules/@noble/hashes/argon2.js +577 -0
  93. typedstandards/_vendor/node_modules/@noble/hashes/blake1.d.ts +162 -0
  94. typedstandards/_vendor/node_modules/@noble/hashes/blake1.js +630 -0
  95. typedstandards/_vendor/node_modules/@noble/hashes/blake2.d.ts +203 -0
  96. typedstandards/_vendor/node_modules/@noble/hashes/blake2.js +573 -0
  97. typedstandards/_vendor/node_modules/@noble/hashes/blake3.d.ts +68 -0
  98. typedstandards/_vendor/node_modules/@noble/hashes/blake3.js +310 -0
  99. typedstandards/_vendor/node_modules/@noble/hashes/eskdf.d.ts +91 -0
  100. typedstandards/_vendor/node_modules/@noble/hashes/eskdf.js +225 -0
  101. typedstandards/_vendor/node_modules/@noble/hashes/hkdf.d.ts +70 -0
  102. typedstandards/_vendor/node_modules/@noble/hashes/hkdf.js +152 -0
  103. typedstandards/_vendor/node_modules/@noble/hashes/hmac.d.ts +50 -0
  104. typedstandards/_vendor/node_modules/@noble/hashes/hmac.js +93 -0
  105. typedstandards/_vendor/node_modules/@noble/hashes/index.d.ts +1 -0
  106. typedstandards/_vendor/node_modules/@noble/hashes/index.js +34 -0
  107. typedstandards/_vendor/node_modules/@noble/hashes/legacy.d.ts +107 -0
  108. typedstandards/_vendor/node_modules/@noble/hashes/legacy.js +348 -0
  109. typedstandards/_vendor/node_modules/@noble/hashes/package.json +91 -0
  110. typedstandards/_vendor/node_modules/@noble/hashes/pbkdf2.d.ts +66 -0
  111. typedstandards/_vendor/node_modules/@noble/hashes/pbkdf2.js +176 -0
  112. typedstandards/_vendor/node_modules/@noble/hashes/scrypt.d.ts +98 -0
  113. typedstandards/_vendor/node_modules/@noble/hashes/scrypt.js +304 -0
  114. typedstandards/_vendor/node_modules/@noble/hashes/sha2.d.ts +176 -0
  115. typedstandards/_vendor/node_modules/@noble/hashes/sha2.js +446 -0
  116. typedstandards/_vendor/node_modules/@noble/hashes/sha3-addons.d.ts +533 -0
  117. typedstandards/_vendor/node_modules/@noble/hashes/sha3-addons.js +973 -0
  118. typedstandards/_vendor/node_modules/@noble/hashes/sha3.d.ts +208 -0
  119. typedstandards/_vendor/node_modules/@noble/hashes/sha3.js +475 -0
  120. typedstandards/_vendor/node_modules/@noble/hashes/src/_blake.ts +67 -0
  121. typedstandards/_vendor/node_modules/@noble/hashes/src/_md.ts +227 -0
  122. typedstandards/_vendor/node_modules/@noble/hashes/src/_u64.ts +111 -0
  123. typedstandards/_vendor/node_modules/@noble/hashes/src/argon2.ts +693 -0
  124. typedstandards/_vendor/node_modules/@noble/hashes/src/blake1.ts +677 -0
  125. typedstandards/_vendor/node_modules/@noble/hashes/src/blake2.ts +648 -0
  126. typedstandards/_vendor/node_modules/@noble/hashes/src/blake3.ts +344 -0
  127. typedstandards/_vendor/node_modules/@noble/hashes/src/eskdf.ts +265 -0
  128. typedstandards/_vendor/node_modules/@noble/hashes/src/hkdf.ts +161 -0
  129. typedstandards/_vendor/node_modules/@noble/hashes/src/hmac.ts +130 -0
  130. typedstandards/_vendor/node_modules/@noble/hashes/src/index.ts +33 -0
  131. typedstandards/_vendor/node_modules/@noble/hashes/src/legacy.ts +361 -0
  132. typedstandards/_vendor/node_modules/@noble/hashes/src/pbkdf2.ts +219 -0
  133. typedstandards/_vendor/node_modules/@noble/hashes/src/scrypt.ts +375 -0
  134. typedstandards/_vendor/node_modules/@noble/hashes/src/sha2.ts +486 -0
  135. typedstandards/_vendor/node_modules/@noble/hashes/src/sha3-addons.ts +1157 -0
  136. typedstandards/_vendor/node_modules/@noble/hashes/src/sha3.ts +525 -0
  137. typedstandards/_vendor/node_modules/@noble/hashes/src/utils.ts +978 -0
  138. typedstandards/_vendor/node_modules/@noble/hashes/src/webcrypto.ts +283 -0
  139. typedstandards/_vendor/node_modules/@noble/hashes/utils.d.ts +560 -0
  140. typedstandards/_vendor/node_modules/@noble/hashes/utils.js +687 -0
  141. typedstandards/_vendor/node_modules/@noble/hashes/webcrypto.d.ts +121 -0
  142. typedstandards/_vendor/node_modules/@noble/hashes/webcrypto.js +228 -0
  143. typedstandards/_vendor/node_modules/@typedstandards/cli/CHANGELOG.md +88 -0
  144. typedstandards/_vendor/node_modules/@typedstandards/cli/LICENSE +21 -0
  145. typedstandards/_vendor/node_modules/@typedstandards/cli/README.md +280 -0
  146. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/attest.d.ts +11 -0
  147. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/attest.js +73 -0
  148. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/bin/main.js +22 -0
  149. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/errors.d.ts +18 -0
  150. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/errors.js +23 -0
  151. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/index.d.ts +4 -0
  152. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/index.js +5 -0
  153. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/input.d.ts +50 -0
  154. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/input.js +273 -0
  155. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/io.d.ts +30 -0
  156. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/io.js +4 -0
  157. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/offline.d.ts +7 -0
  158. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/offline.js +24 -0
  159. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/readings.d.ts +73 -0
  160. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/readings.js +212 -0
  161. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/run.d.ts +5 -0
  162. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/run.js +83 -0
  163. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/seed.d.ts +3 -0
  164. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/seed.js +34 -0
  165. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/sign.d.ts +23 -0
  166. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/sign.js +120 -0
  167. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/verify.d.ts +65 -0
  168. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/verify.js +202 -0
  169. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/view.d.ts +37 -0
  170. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/view.js +101 -0
  171. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/withdraw.d.ts +11 -0
  172. typedstandards/_vendor/node_modules/@typedstandards/cli/dist/withdraw.js +59 -0
  173. typedstandards/_vendor/node_modules/@typedstandards/cli/package.json +67 -0
  174. typedstandards/_vendor/node_modules/@typedstandards/produce-core/CHANGELOG.md +227 -0
  175. typedstandards/_vendor/node_modules/@typedstandards/produce-core/LICENSE +21 -0
  176. typedstandards/_vendor/node_modules/@typedstandards/produce-core/README.md +209 -0
  177. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/attestation.d.ts +131 -0
  178. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/attestation.js +107 -0
  179. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/commitment.d.ts +103 -0
  180. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/commitment.js +141 -0
  181. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/envelope.d.ts +239 -0
  182. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/envelope.js +193 -0
  183. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/index.d.ts +9 -0
  184. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/index.js +24 -0
  185. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/provenance.d.ts +62 -0
  186. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/provenance.js +88 -0
  187. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/rekor-proposal.d.ts +44 -0
  188. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/rekor-proposal.js +72 -0
  189. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/rfc3161-request.d.ts +7 -0
  190. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/rfc3161-request.js +67 -0
  191. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/signing.d.ts +77 -0
  192. typedstandards/_vendor/node_modules/@typedstandards/produce-core/dist/signing.js +171 -0
  193. typedstandards/_vendor/node_modules/@typedstandards/produce-core/package.json +71 -0
  194. typedstandards/_vendor/node_modules/@typedstandards/verify-core/CHANGELOG.md +371 -0
  195. typedstandards/_vendor/node_modules/@typedstandards/verify-core/LICENSE +21 -0
  196. typedstandards/_vendor/node_modules/@typedstandards/verify-core/README.md +111 -0
  197. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/asn1.d.ts +36 -0
  198. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/asn1.js +154 -0
  199. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/attestation.d.ts +23 -0
  200. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/attestation.js +31 -0
  201. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/blob-ref.d.ts +67 -0
  202. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/blob-ref.js +133 -0
  203. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/canonicalization.d.ts +54 -0
  204. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/canonicalization.js +115 -0
  205. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/checks.d.ts +235 -0
  206. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/checks.js +496 -0
  207. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/did-key.d.ts +28 -0
  208. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/did-key.js +74 -0
  209. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/index.d.ts +17 -0
  210. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/index.js +31 -0
  211. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/lifecycle.d.ts +270 -0
  212. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/lifecycle.js +390 -0
  213. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/primitives.d.ts +14 -0
  214. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/primitives.js +37 -0
  215. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/profiles.d.ts +20 -0
  216. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/profiles.js +44 -0
  217. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rekor-inclusion.d.ts +109 -0
  218. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rekor-inclusion.js +291 -0
  219. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rekor.d.ts +29 -0
  220. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rekor.js +61 -0
  221. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rfc3161.d.ts +70 -0
  222. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/rfc3161.js +309 -0
  223. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/self-certifying.d.ts +46 -0
  224. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/self-certifying.js +89 -0
  225. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/signature.d.ts +48 -0
  226. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/signature.js +86 -0
  227. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/trust-registry.d.ts +81 -0
  228. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/trust-registry.js +141 -0
  229. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/types.d.ts +48 -0
  230. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/types.js +6 -0
  231. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/verify.d.ts +188 -0
  232. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/verify.js +274 -0
  233. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/x509.d.ts +70 -0
  234. typedstandards/_vendor/node_modules/@typedstandards/verify-core/dist/x509.js +228 -0
  235. typedstandards/_vendor/node_modules/@typedstandards/verify-core/package.json +67 -0
  236. typedstandards/_vendor/node_modules/canonicalize/LICENSE +201 -0
  237. typedstandards/_vendor/node_modules/canonicalize/README.md +65 -0
  238. typedstandards/_vendor/node_modules/canonicalize/bin/canonicalize.js +17 -0
  239. typedstandards/_vendor/node_modules/canonicalize/lib/canonicalize.d.ts +2 -0
  240. typedstandards/_vendor/node_modules/canonicalize/lib/canonicalize.js +49 -0
  241. typedstandards/_vendor/node_modules/canonicalize/package.json +64 -0
  242. typedstandards/_vendor/package-lock.json +96 -0
  243. typedstandards/_vendor/package.json +9 -0
  244. typedstandards/errors.py +58 -0
  245. typedstandards/pin.py +156 -0
  246. typedstandards-0.1.0.dist-info/METADATA +231 -0
  247. typedstandards-0.1.0.dist-info/RECORD +249 -0
  248. typedstandards-0.1.0.dist-info/WHEEL +4 -0
  249. 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
+ ]
@@ -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"[![{BADGE_ALT}]({CANONICAL_ORIGIN}{BADGE_ASSET_PATH})](<{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