primarysource 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 (303) hide show
  1. primarysource/__init__.py +5 -0
  2. primarysource/bbf/__init__.py +56 -0
  3. primarysource/bbf/_xml.py +63 -0
  4. primarysource/bbf/catalogue.json +777 -0
  5. primarysource/bbf/catalogue.py +137 -0
  6. primarysource/bbf/cli.py +79 -0
  7. primarysource/bbf/datamodel.py +281 -0
  8. primarysource/bbf/importer.py +151 -0
  9. primarysource/bbf/library.py +380 -0
  10. primarysource/bbf/server.py +72 -0
  11. primarysource/budget.py +63 -0
  12. primarysource/c2sp/__init__.py +53 -0
  13. primarysource/c2sp/_fetch.py +5 -0
  14. primarysource/c2sp/catalogue.json +231 -0
  15. primarysource/c2sp/catalogue.py +95 -0
  16. primarysource/c2sp/cli.py +62 -0
  17. primarysource/c2sp/importer.py +90 -0
  18. primarysource/c2sp/library.py +309 -0
  19. primarysource/c2sp/markdown.py +31 -0
  20. primarysource/c2sp/server.py +68 -0
  21. primarysource/cavp/__init__.py +42 -0
  22. primarysource/cavp/cli.py +84 -0
  23. primarysource/cavp/data/vectors.toml +47 -0
  24. primarysource/cavp/importer.py +156 -0
  25. primarysource/cavp/model.py +279 -0
  26. primarysource/cavp/parse.py +269 -0
  27. primarysource/cavp/paths.py +17 -0
  28. primarysource/cavp/server.py +67 -0
  29. primarysource/cavp/tools.py +169 -0
  30. primarysource/cli.py +107 -0
  31. primarysource/cursor.py +82 -0
  32. primarysource/cve/__init__.py +5 -0
  33. primarysource/cve/__main__.py +6 -0
  34. primarysource/cve/cli.py +144 -0
  35. primarysource/cve/collection.py +20 -0
  36. primarysource/cve/config.py +58 -0
  37. primarysource/cve/errors.py +16 -0
  38. primarysource/cve/kernel.py +232 -0
  39. primarysource/cve/mcp.py +139 -0
  40. primarysource/cve/parse.py +318 -0
  41. primarysource/cve/py.typed +0 -0
  42. primarysource/cve/service.py +760 -0
  43. primarysource/cve/store.py +214 -0
  44. primarysource/cve/sync.py +438 -0
  45. primarysource/cve/views.py +304 -0
  46. primarysource/debpolicy/__init__.py +16 -0
  47. primarysource/debpolicy/catalogue.py +263 -0
  48. primarysource/debpolicy/cli.py +12 -0
  49. primarysource/debpolicy/server.py +11 -0
  50. primarysource/draft/__init__.py +11 -0
  51. primarysource/draft/catalogue.py +117 -0
  52. primarysource/draft/cli.py +149 -0
  53. primarysource/draft/collection.py +21 -0
  54. primarysource/draft/datatracker.py +158 -0
  55. primarysource/draft/importer.py +163 -0
  56. primarysource/draft/licence.py +89 -0
  57. primarysource/draft/names.py +64 -0
  58. primarysource/draft/net.py +133 -0
  59. primarysource/draft/segment.py +192 -0
  60. primarysource/draft/server.py +91 -0
  61. primarysource/draft/status.py +146 -0
  62. primarysource/draft/store.py +199 -0
  63. primarysource/draft/tracked.json +104 -0
  64. primarysource/draft/views.py +430 -0
  65. primarysource/draft/xmlmeta.py +74 -0
  66. primarysource/ecma/__init__.py +52 -0
  67. primarysource/ecma/_pdf.py +143 -0
  68. primarysource/ecma/catalogue.json +22 -0
  69. primarysource/ecma/catalogue.py +100 -0
  70. primarysource/ecma/cli.py +67 -0
  71. primarysource/ecma/identify.py +25 -0
  72. primarysource/ecma/importer.py +108 -0
  73. primarysource/ecma/library.py +240 -0
  74. primarysource/ecma/server.py +67 -0
  75. primarysource/errors.py +29 -0
  76. primarysource/fetch.py +360 -0
  77. primarysource/fsutil.py +150 -0
  78. primarysource/gitdocs/__init__.py +15 -0
  79. primarysource/gitdocs/catalogue.py +116 -0
  80. primarysource/gitdocs/cli.py +12 -0
  81. primarysource/gitdocs/server.py +8 -0
  82. primarysource/home.py +65 -0
  83. primarysource/iana/__init__.py +32 -0
  84. primarysource/iana/catalogue.json +244 -0
  85. primarysource/iana/catalogue.py +69 -0
  86. primarysource/iana/cli.py +69 -0
  87. primarysource/iana/importer.py +157 -0
  88. primarysource/iana/model.py +325 -0
  89. primarysource/iana/reader.py +315 -0
  90. primarysource/iana/server.py +67 -0
  91. primarysource/iana/store.py +83 -0
  92. primarysource/iana/xmlsafe.py +47 -0
  93. primarysource/identify.py +186 -0
  94. primarysource/ieee/__init__.py +58 -0
  95. primarysource/ieee/_html.py +49 -0
  96. primarysource/ieee/catalogue.json +74 -0
  97. primarysource/ieee/catalogue.py +123 -0
  98. primarysource/ieee/cli.py +79 -0
  99. primarysource/ieee/identify.py +24 -0
  100. primarysource/ieee/ieee802.py +47 -0
  101. primarysource/ieee/importer.py +305 -0
  102. primarysource/ieee/posix.py +233 -0
  103. primarysource/ieee/server.py +79 -0
  104. primarysource/index.py +446 -0
  105. primarysource/iso/__init__.py +51 -0
  106. primarysource/iso/cli.py +98 -0
  107. primarysource/iso/designation.py +95 -0
  108. primarysource/iso/identify.py +100 -0
  109. primarysource/iso/importer.py +224 -0
  110. primarysource/iso/library.py +249 -0
  111. primarysource/iso/server.py +69 -0
  112. primarysource/itu/__init__.py +41 -0
  113. primarysource/itu/__main__.py +8 -0
  114. primarysource/itu/asn1mod.py +478 -0
  115. primarysource/itu/catalog.py +472 -0
  116. primarysource/itu/cli.py +445 -0
  117. primarysource/itu/collection.py +20 -0
  118. primarysource/itu/corpus.py +118 -0
  119. primarysource/itu/errata.py +16 -0
  120. primarysource/itu/formal.py +565 -0
  121. primarysource/itu/identify.py +26 -0
  122. primarysource/itu/index.py +490 -0
  123. primarysource/itu/mcp.py +474 -0
  124. primarysource/itu/py.typed +0 -0
  125. primarysource/itu/sources.py +600 -0
  126. primarysource/itu/text.py +609 -0
  127. primarysource/kconfig/__init__.py +16 -0
  128. primarysource/kconfig/catalogue.py +98 -0
  129. primarysource/kconfig/cli.py +12 -0
  130. primarysource/kconfig/server.py +8 -0
  131. primarysource/model.py +63 -0
  132. primarysource/nist/__init__.py +5 -0
  133. primarysource/nist/collection.py +46 -0
  134. primarysource/nist/data/acquired-nist.json +1657 -0
  135. primarysource/nist/data/nist-catalog.json +12090 -0
  136. primarysource/nist/data/sources.toml +1996 -0
  137. primarysource/nist/identify.py +41 -0
  138. primarysource/nist/ingest/__init__.py +1 -0
  139. primarysource/nist/ingest/acquire_nist.py +128 -0
  140. primarysource/nist/ingest/controls.py +82 -0
  141. primarysource/nist/ingest/discover_nist.py +218 -0
  142. primarysource/nist/ingest/extract.py +48 -0
  143. primarysource/nist/ingest/fetch.py +211 -0
  144. primarysource/nist/ingest/index.py +14 -0
  145. primarysource/nist/ingest/oscal.py +480 -0
  146. primarysource/nist/ingest/prose_layouts.py +236 -0
  147. primarysource/nist/ingest/sources.py +94 -0
  148. primarysource/nist/ingest/status.py +255 -0
  149. primarysource/nist/models.py +7 -0
  150. primarysource/nist/paths.py +33 -0
  151. primarysource/nist/server/__init__.py +3 -0
  152. primarysource/nist/server/corpus.py +38 -0
  153. primarysource/nist/server/mcp.py +19 -0
  154. primarysource/nist/server/tools.py +35 -0
  155. primarysource/nist/spec.py +38 -0
  156. primarysource/pdfcorpus/__init__.py +16 -0
  157. primarysource/pdfcorpus/corpus.py +159 -0
  158. primarysource/pdfcorpus/fts.py +175 -0
  159. primarysource/pdfcorpus/index.py +70 -0
  160. primarysource/pdfcorpus/models.py +30 -0
  161. primarysource/pdfcorpus/server.py +96 -0
  162. primarysource/pdfcorpus/spec.py +37 -0
  163. primarysource/pdfcorpus/split.py +391 -0
  164. primarysource/pdfcorpus/tools.py +244 -0
  165. primarysource/pep/__init__.py +15 -0
  166. primarysource/pep/catalogue.py +191 -0
  167. primarysource/pep/cli.py +12 -0
  168. primarysource/pep/server.py +8 -0
  169. primarysource/pkgdocs/__init__.py +19 -0
  170. primarysource/pkgdocs/fetch.py +356 -0
  171. primarysource/pkgdocs/index.py +89 -0
  172. primarysource/pkgdocs/model.py +76 -0
  173. primarysource/pkgdocs/readers/__init__.py +1 -0
  174. primarysource/pkgdocs/readers/_common.py +134 -0
  175. primarysource/pkgdocs/readers/asciidoc.py +160 -0
  176. primarysource/pkgdocs/readers/latex.py +315 -0
  177. primarysource/pkgdocs/readers/markdown.py +173 -0
  178. primarysource/pkgdocs/readers/pod.py +132 -0
  179. primarysource/pkgdocs/readers/rst.py +239 -0
  180. primarysource/pkgdocs/readers/scdoc.py +108 -0
  181. primarysource/pkgdocs/runtime.py +552 -0
  182. primarysource/pkgdocs/serving.py +158 -0
  183. primarysource/pkgdocs/store.py +167 -0
  184. primarysource/pms/__init__.py +16 -0
  185. primarysource/pms/catalogue.py +88 -0
  186. primarysource/pms/cli.py +12 -0
  187. primarysource/pms/server.py +8 -0
  188. primarysource/py.typed +0 -0
  189. primarysource/readers/__init__.py +10 -0
  190. primarysource/readers/bookmarks.py +192 -0
  191. primarysource/readers/html.py +1312 -0
  192. primarysource/readers/markdown.py +189 -0
  193. primarysource/readers/pdf.py +187 -0
  194. primarysource/readers/xml.py +230 -0
  195. primarysource/refs.py +87 -0
  196. primarysource/registry.py +161 -0
  197. primarysource/render.py +49 -0
  198. primarysource/rfc/__init__.py +3 -0
  199. primarysource/rfc/__main__.py +6 -0
  200. primarysource/rfc/cite.py +204 -0
  201. primarysource/rfc/cli.py +903 -0
  202. primarysource/rfc/collection.py +19 -0
  203. primarysource/rfc/config.py +138 -0
  204. primarysource/rfc/errata.py +203 -0
  205. primarysource/rfc/fsutil.py +82 -0
  206. primarysource/rfc/graph.py +213 -0
  207. primarysource/rfc/index/__init__.py +1 -0
  208. primarysource/rfc/index/chunker.py +281 -0
  209. primarysource/rfc/index/embed.py +166 -0
  210. primarysource/rfc/index/hybrid.py +305 -0
  211. primarysource/rfc/index/lexical.py +223 -0
  212. primarysource/rfc/index/rfcxml.py +673 -0
  213. primarysource/rfc/index/search.py +508 -0
  214. primarysource/rfc/index/txtseg.py +585 -0
  215. primarysource/rfc/library.py +294 -0
  216. primarysource/rfc/mcp/__init__.py +1 -0
  217. primarysource/rfc/mcp/server.py +381 -0
  218. primarysource/rfc/packs/__init__.py +1 -0
  219. primarysource/rfc/packs/builder.py +228 -0
  220. primarysource/rfc/packs/collections/all-current-txt.json +10 -0
  221. primarysource/rfc/packs/collections/essentials.json +19 -0
  222. primarysource/rfc/packs/collections/fyi-current.json +10 -0
  223. primarysource/rfc/packs/collections/standards-current.json +10 -0
  224. primarysource/rfc/packs/collections/year-2025.json +10 -0
  225. primarysource/rfc/packs/collections/year-2026.json +10 -0
  226. primarysource/rfc/packs/collections.py +163 -0
  227. primarysource/rfc/packs/docsetexport.py +163 -0
  228. primarysource/rfc/packs/epub.py +159 -0
  229. primarysource/rfc/packs/export.py +306 -0
  230. primarysource/rfc/packs/htmlexport.py +82 -0
  231. primarysource/rfc/packs/serve.py +143 -0
  232. primarysource/rfc/packs/zimexport.py +171 -0
  233. primarysource/rfc/query.py +466 -0
  234. primarysource/rfc/subjects.py +457 -0
  235. primarysource/rfc/sync/__init__.py +1 -0
  236. primarysource/rfc/sync/downloader.py +132 -0
  237. primarysource/rfc/sync/manifest.py +162 -0
  238. primarysource/rfc/sync/scanner.py +49 -0
  239. primarysource/rfc/sync/state.py +464 -0
  240. primarysource/rfc/views.py +1067 -0
  241. primarysource/rpmdocs/__init__.py +15 -0
  242. primarysource/rpmdocs/catalogue.py +117 -0
  243. primarysource/rpmdocs/cli.py +12 -0
  244. primarysource/rpmdocs/server.py +8 -0
  245. primarysource/scaffold.py +223 -0
  246. primarysource/serve.py +75 -0
  247. primarysource/settings.py +152 -0
  248. primarysource/usb/__init__.py +5 -0
  249. primarysource/usb/collection.py +37 -0
  250. primarysource/usb/data/acquired.json +2025 -0
  251. primarysource/usb/data/sources-usb.toml +852 -0
  252. primarysource/usb/data/sources.toml +10 -0
  253. primarysource/usb/data/usb-catalog.json +1663 -0
  254. primarysource/usb/data/usb-classification.json +1986 -0
  255. primarysource/usb/data/usb-corpus-build.json +608 -0
  256. primarysource/usb/data/usb-pdf-choice.json +839 -0
  257. primarysource/usb/data/usb-split-measure.json +1803 -0
  258. primarysource/usb/ingest/__init__.py +1 -0
  259. primarysource/usb/ingest/acquire.py +130 -0
  260. primarysource/usb/ingest/discover_usb.py +88 -0
  261. primarysource/usb/ingest/extract.py +48 -0
  262. primarysource/usb/ingest/fetch.py +197 -0
  263. primarysource/usb/ingest/index.py +14 -0
  264. primarysource/usb/ingest/sources.py +85 -0
  265. primarysource/usb/models.py +7 -0
  266. primarysource/usb/paths.py +33 -0
  267. primarysource/usb/server/__init__.py +3 -0
  268. primarysource/usb/server/corpus.py +38 -0
  269. primarysource/usb/server/mcp.py +19 -0
  270. primarysource/usb/server/tools.py +35 -0
  271. primarysource/usb/spec.py +20 -0
  272. primarysource/usbids/__init__.py +56 -0
  273. primarysource/usbids/cli.py +65 -0
  274. primarysource/usbids/importer.py +272 -0
  275. primarysource/usbids/parse.py +283 -0
  276. primarysource/usbids/server.py +55 -0
  277. primarysource/usbids/store.py +80 -0
  278. primarysource/usbids/tools.py +174 -0
  279. primarysource/zim/__init__.py +5 -0
  280. primarysource/zim/__main__.py +8 -0
  281. primarysource/zim/_libzim.py +37 -0
  282. primarysource/zim/catalog.py +594 -0
  283. primarysource/zim/cli.py +555 -0
  284. primarysource/zim/collection.py +41 -0
  285. primarysource/zim/errors.py +100 -0
  286. primarysource/zim/groups.py +30 -0
  287. primarysource/zim/library.py +668 -0
  288. primarysource/zim/manage.py +636 -0
  289. primarysource/zim/markdown.py +1247 -0
  290. primarysource/zim/mcp.py +201 -0
  291. primarysource/zim/purl.py +130 -0
  292. primarysource/zim/py.typed +0 -0
  293. primarysource/zim/refs.py +90 -0
  294. primarysource/zim/service.py +1182 -0
  295. primarysource/zim/snapshot.py +197 -0
  296. primarysource/zim/sync.py +269 -0
  297. primarysource/zim/views.py +230 -0
  298. primarysource-0.1.0.dist-info/METADATA +118 -0
  299. primarysource-0.1.0.dist-info/RECORD +303 -0
  300. primarysource-0.1.0.dist-info/WHEEL +4 -0
  301. primarysource-0.1.0.dist-info/entry_points.txt +2 -0
  302. primarysource-0.1.0.dist-info/licenses/LICENSE +203 -0
  303. primarysource-0.1.0.dist-info/licenses/NOTICE +51 -0
@@ -0,0 +1,98 @@
1
+ """The `kconfig` collection: the kernel's Kconfig and Kbuild documents, read as reStructuredText.
2
+
3
+ Source, placement and pin (read from the repositories on 2026-11-13, see `docs/kconfig.md`):
4
+
5
+ - the tree is `Documentation/kbuild/` of torvalds/linux at the commit of the tag `v7.3`, fetched as a gitiles
6
+ subtree archive from kernel.googlesource.com (57 KB; the whole-repository tarball is two orders larger). The
7
+ documents are the twelve `.rst` files there: `kconfig-language` (the language), `kconfig-macro-language`,
8
+ `kconfig` (the configuration targets), `kbuild`, `makefiles`, `modules`, and the rest;
9
+ - the licence is GPL-2.0-only: the kernel's `COPYING` at the root says "GPL-2.0 WITH Linux-syscall-note" and
10
+ names the syscall note's scope as the user-space API headers, and `Documentation/process/license-rules.rst`
11
+ gives the kernel as "GPL version 2 only". Three of the twelve files carry an `SPDX-License-Identifier` line;
12
+ the build records it as the document's `licence` and records the rest as `GPL-2.0-only (COPYING, no file tag)`;
13
+ - placement is **layer**.
14
+
15
+ pkgtrust R165 and kconfigpy read `kconfig-language` for the grammar of a Kconfig file; refs into it are
16
+ `kconfig:kconfig-language§Menu entries` and, once pinned, `kconfig:kconfig-language@c03c1a14§Menu entries`.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import re
22
+ from collections import Counter
23
+ from collections.abc import Mapping
24
+
25
+ from primarysource.errors import LibError
26
+ from primarysource.pkgdocs.fetch import Source
27
+ from primarysource.pkgdocs.model import Doc
28
+ from primarysource.pkgdocs.readers._common import decode
29
+ from primarysource.pkgdocs.readers.rst import read_rst
30
+ from primarysource.pkgdocs.runtime import Build, Licence, Pin, Spec, TreeSpec, matching
31
+
32
+ COMMIT = "c03c1a14507dfaf8b856c2608f9ac216021fc222"
33
+
34
+ PIN = Pin(
35
+ commit=COMMIT,
36
+ label="v7.3",
37
+ date="2026-10-18",
38
+ files_sha256="36d3fe2bf74fee4efef91f7a16a56297966652812e314f8122eb4e5d0cd13ecd",
39
+ sources=(
40
+ Source(
41
+ f"https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux/+archive/{COMMIT}/Documentation/kbuild.tar.gz",
42
+ strip=0,
43
+ ),
44
+ ),
45
+ )
46
+
47
+ LICENCE = Licence(
48
+ spdx="GPL-2.0-only",
49
+ redistribute="layer",
50
+ attribution="Linux kernel developers; GPL-2.0-only",
51
+ evidence="COPYING and Documentation/process/license-rules.rst at torvalds/linux " + COMMIT[:12],
52
+ )
53
+
54
+ _SPDX = re.compile(r"SPDX-License-Identifier:\s*([^\n]+)")
55
+
56
+
57
+ def build(files: Mapping[str, bytes], pin: Pin) -> Build:
58
+ docs: list[Doc] = []
59
+ skipped: Counter[str] = Counter()
60
+ for path in sorted(files):
61
+ stem = path[: -len(".rst")]
62
+ try:
63
+ doc = read_rst(files[path], stem)
64
+ tag = _SPDX.search(decode(files[path], path))
65
+ except LibError:
66
+ skipped["not UTF-8 text"] += 1
67
+ continue
68
+ licence = tag.group(1).strip() if tag else "GPL-2.0-only (COPYING, no file tag)"
69
+ docs.append(doc.with_meta(path=f"Documentation/kbuild/{path}", licence=licence))
70
+ return Build(tuple(docs), {}, skipped)
71
+
72
+
73
+ SPEC = Spec(
74
+ name="kconfig",
75
+ title="Linux Kconfig and Kbuild documentation",
76
+ licence=LICENCE,
77
+ trees=(
78
+ TreeSpec(
79
+ name="kbuild",
80
+ title="Documentation/kbuild/ of torvalds/linux (reStructuredText)",
81
+ pins=(PIN,),
82
+ include=matching(r"[a-z][a-z0-9_.-]*\.rst"),
83
+ build=build,
84
+ ),
85
+ ),
86
+ instructions=(
87
+ "The kernel's Kconfig language reference (kconfig-language), the macro language (kconfig-macro-language), "
88
+ "the configuration targets (kconfig) and the Kbuild documents (kbuild, makefiles, modules, ...) from the "
89
+ "user's imported copy at a pinned release. Loop: kconfig_search('<words>') -> "
90
+ "kconfig_outline('kconfig-language') -> kconfig_read('<ref>'). A document id is its file name without "
91
+ ".rst. A section is an id (2.1), an anchor (menu-entries), a heading or a unique part of one."
92
+ ),
93
+ example_refs=(
94
+ "kconfig:kconfig-language§Menu entries",
95
+ "kconfig:kconfig-language§Menu attributes¶3",
96
+ "kconfig:kconfig-macro-language§Built-in functions",
97
+ ),
98
+ )
@@ -0,0 +1,12 @@
1
+ """`primarysource kconfig import|status|docs|outline|read|search|cite`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+
7
+ from primarysource.kconfig.catalogue import SPEC
8
+ from primarysource.pkgdocs.serving import run
9
+
10
+
11
+ def main(argv: Sequence[str]) -> int:
12
+ return run(SPEC, argv)
@@ -0,0 +1,8 @@
1
+ """The MCP server for `kconfig` (`primarysource mcp --collection kconfig`). Imports `mcp` when this module loads."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from primarysource.kconfig.catalogue import SPEC
6
+ from primarysource.pkgdocs.serving import build_server
7
+
8
+ server = build_server(SPEC)
primarysource/model.py ADDED
@@ -0,0 +1,63 @@
1
+ """The sectioned-document model the libraries share.
2
+
3
+ A document is an ordered list of sections; a section is an ordered list of units. What a unit IS differs by
4
+ corpus and cannot be unified honestly: an RFC has real paragraphs (RFCXML `pn` ids), an extracted PDF clause
5
+ only has sentences, a converted HTML article has blocks. So the unit kind is declared, and every result that
6
+ hands out a `¶n` ref says which kind `n` counts.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import hashlib
12
+ from dataclasses import dataclass, field
13
+ from enum import StrEnum
14
+
15
+
16
+ class Unit(StrEnum):
17
+ """What `¶n` counts within a section."""
18
+
19
+ PARAGRAPH = "paragraph"
20
+ SENTENCE = "sentence"
21
+ BLOCK = "block"
22
+
23
+
24
+ @dataclass(frozen=True, slots=True)
25
+ class Section:
26
+ """One addressable part of a document. `units` are in reading order; `¶1` is `units[0]`."""
27
+
28
+ id: str
29
+ title: str
30
+ units: tuple[str, ...] = ()
31
+ depth: int = 1
32
+
33
+
34
+ @dataclass(frozen=True, slots=True)
35
+ class Document:
36
+ """A document as the core sees it: an id, a title, the unit kind, and its sections in order."""
37
+
38
+ id: str
39
+ title: str
40
+ unit: Unit
41
+ sections: tuple[Section, ...] = field(default=())
42
+
43
+ def section(self, section_id: str) -> Section | None:
44
+ """The first section with this id, or None. Duplicate ids are the segmenter's to report."""
45
+ for candidate in self.sections:
46
+ if candidate.id == section_id:
47
+ return candidate
48
+ return None
49
+
50
+ def duplicate_ids(self) -> tuple[str, ...]:
51
+ """Section ids that occur more than once, in first-seen order: an ambiguity to warn about."""
52
+ seen: set[str] = set()
53
+ repeated: list[str] = []
54
+ for candidate in self.sections:
55
+ if candidate.id in seen and candidate.id not in repeated:
56
+ repeated.append(candidate.id)
57
+ seen.add(candidate.id)
58
+ return tuple(repeated)
59
+
60
+
61
+ def span_hash(text: str) -> str:
62
+ """`sha256:<16 hex>` of the exact text cited, so a citation can be checked against a later extraction."""
63
+ return "sha256:" + hashlib.sha256(text.encode("utf-8")).hexdigest()[:16]
@@ -0,0 +1,5 @@
1
+ """NIST publications: corpus index, search, and citation."""
2
+
3
+ from primarysource.nist.models import Document, Section
4
+
5
+ __all__ = ["Document", "Section"]
@@ -0,0 +1,46 @@
1
+ """NIST publications (SP 800, FIPS) as a primarysource collection. Importing this needs nothing beyond the core."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+
7
+ from primarysource.model import Unit
8
+ from primarysource.pdfcorpus.tools import GRAMMAR
9
+ from primarysource.registry import Collection
10
+
11
+ # The server registers the bare names (search, read, outline, info, cite); `primarysource mcp` serves them as
12
+ # nist_search, ... and `--bare-names` keeps nistlib's names. Refs: `<doc_id>§<section>` and `<doc_id>:<first>-<last>`.
13
+ COLLECTION = Collection(
14
+ name="nist",
15
+ title="NIST publications (SP 800, SP 1800, FIPS), fetched by the user",
16
+ prefix="nist_",
17
+ unit=Unit.BLOCK,
18
+ grammar=GRAMMAR,
19
+ server="primarysource.nist.server.mcp:mcp",
20
+ cli="primarysource.nist.collection:main",
21
+ identify="primarysource.nist.identify:identify",
22
+ )
23
+
24
+
25
+ def main(argv: Sequence[str]) -> int:
26
+ """`primarysource nist fetch ...` (download, extract, index), `primarysource nist oscal ...`,
27
+ `primarysource nist index` (rebuild the search index from the fetched corpus) and `primarysource nist status`
28
+ (write `defaults.json`, so a bare name such as "NIST SP 800-53" resolves to its current final revision)."""
29
+ commands = {
30
+ "fetch": "primarysource.nist.ingest.fetch",
31
+ "oscal": "primarysource.nist.ingest.oscal",
32
+ "status": "primarysource.nist.ingest.status",
33
+ }
34
+ if argv and argv[0] == "index":
35
+ from primarysource.nist.spec import SPEC
36
+ from primarysource.pdfcorpus import fts
37
+
38
+ return fts.main(SPEC, argv[1:])
39
+ if not argv or argv[0] not in commands:
40
+ names = ",".join([*commands, "index"])
41
+ print(f"usage: primarysource nist {{{names}}} [args]", flush=True)
42
+ return 2
43
+ import importlib
44
+
45
+ module = importlib.import_module(commands[argv[0]])
46
+ return int(module.main(list(argv[1:])))