specitems 1.1.1__tar.gz

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.
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.4
2
+ Name: specitems
3
+ Version: 1.1.1
4
+ Summary: Provides interfaces to work with specification items.
5
+ Keywords: certification,documentation,markdown,qualification,requirements-management,traceability
6
+ Author: The specitems Authors
7
+ Author-email: The specitems Authors <specthings@embedded-brains.de>
8
+ License-Expression: BSD-2-Clause
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: POSIX
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Software Development :: Documentation
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Topic :: Text Processing :: Markup
21
+ Classifier: Typing :: Typed
22
+ Requires-Dist: mdformat>=0.7.22
23
+ Requires-Dist: mdformat-deflist>=0.1.3
24
+ Requires-Dist: mdformat-footnote>=0.1.3
25
+ Requires-Dist: mdformat-frontmatter>=2.0.10
26
+ Requires-Dist: mdformat-myst>=0.3.0
27
+ Requires-Dist: mdformat-tables>=1.0.0
28
+ Requires-Dist: pyyaml>=6.0.3
29
+ Requires-Python: >=3.11
30
+ Project-URL: Source Code, https://github.com/specthings/specitems
31
+ Project-URL: Bug Tracker, https://github.com/specthings/specitems/issues
32
+ Description-Content-Type: text/markdown
33
+
34
+ <!--
35
+ SPDX-License-Identifier: CC-BY-SA-4.0
36
+
37
+ Copyright (C) 2026 embedded brains GmbH & Co. KG
38
+ -->
39
+ ## Overview
40
+
41
+ The *specitems* Python package provides interfaces to work with specification
42
+ items. Specifications are written in specification items which may contain
43
+ dictionaries, lists, integers, floating-point numbers, and strings. The format
44
+ of these items is extensible, human readable, machine readable, Git friendly,
45
+ and can be customized according to domain-specific needs. The items are
46
+ connected through links which may contain role-specific extra information.
47
+ This enables different views to a specification item graph depending on the use
48
+ case.
49
+
50
+ The package is maintained by the
51
+ [specthings](https://github.com/specthings)
52
+ project. Users of the package are
53
+ [specware](https://github.com/specthings/specitems)
54
+ and
55
+ [specbuild](https://github.com/specthings/specbuild).
56
+
57
+ ## Contributing
58
+
59
+ Please refer to our
60
+ [Contributing Guidelines](https://github.com/specthings/specitems/blob/main/CONTRIBUTING.md).
@@ -0,0 +1,27 @@
1
+ <!--
2
+ SPDX-License-Identifier: CC-BY-SA-4.0
3
+
4
+ Copyright (C) 2026 embedded brains GmbH & Co. KG
5
+ -->
6
+ ## Overview
7
+
8
+ The *specitems* Python package provides interfaces to work with specification
9
+ items. Specifications are written in specification items which may contain
10
+ dictionaries, lists, integers, floating-point numbers, and strings. The format
11
+ of these items is extensible, human readable, machine readable, Git friendly,
12
+ and can be customized according to domain-specific needs. The items are
13
+ connected through links which may contain role-specific extra information.
14
+ This enables different views to a specification item graph depending on the use
15
+ case.
16
+
17
+ The package is maintained by the
18
+ [specthings](https://github.com/specthings)
19
+ project. Users of the package are
20
+ [specware](https://github.com/specthings/specitems)
21
+ and
22
+ [specbuild](https://github.com/specthings/specbuild).
23
+
24
+ ## Contributing
25
+
26
+ Please refer to our
27
+ [Contributing Guidelines](https://github.com/specthings/specitems/blob/main/CONTRIBUTING.md).
@@ -0,0 +1,79 @@
1
+ # SPDX-License-Identifier: BSD-2-Clause
2
+
3
+ # Copyright (C) 2026 embedded brains GmbH & Co. KG
4
+
5
+ [project]
6
+ name = "specitems"
7
+ version = "1.1.1"
8
+ description = "Provides interfaces to work with specification items."
9
+ authors = [
10
+ {name = "The specitems Authors", email = "specthings@embedded-brains.de"}
11
+ ]
12
+ license = "BSD-2-Clause"
13
+ readme = "README.md"
14
+ requires-python = ">=3.11"
15
+ dependencies = [
16
+ "mdformat>=0.7.22",
17
+ "mdformat-deflist>=0.1.3",
18
+ "mdformat-footnote>=0.1.3",
19
+ "mdformat-frontmatter>=2.0.10",
20
+ "mdformat-myst>=0.3.0",
21
+ "mdformat-tables>=1.0.0",
22
+ "pyyaml>=6.0.3",
23
+ ]
24
+ keywords = [
25
+ "certification",
26
+ "documentation",
27
+ "markdown",
28
+ "qualification",
29
+ "requirements-management",
30
+ "traceability",
31
+ ]
32
+ classifiers = [
33
+ "Development Status :: 5 - Production/Stable",
34
+ "Environment :: Console",
35
+ "Intended Audience :: Developers",
36
+ "Operating System :: POSIX",
37
+ "Programming Language :: Python :: 3",
38
+ "Programming Language :: Python :: 3.11",
39
+ "Programming Language :: Python :: 3.12",
40
+ "Programming Language :: Python :: 3.13",
41
+ "Programming Language :: Python :: 3.14",
42
+ "Topic :: Software Development :: Documentation",
43
+ "Topic :: Software Development :: Libraries :: Python Modules",
44
+ "Topic :: Text Processing :: Markup",
45
+ "Typing :: Typed",
46
+ ]
47
+
48
+ [project.urls]
49
+ "Source Code" = "https://github.com/specthings/specitems"
50
+ "Bug Tracker" = "https://github.com/specthings/specitems/issues"
51
+
52
+ [project.scripts]
53
+ specpickle = "specitems.clipickle:clipickle"
54
+ specdocitems = "specitems.clispecdoc:clispecdocitems"
55
+ spechash = "specitems.clihash:clihash"
56
+ specyamlquery = "specitems.cliyamlquery:cliyamlquery"
57
+
58
+ [build-system]
59
+ requires = ["uv_build>=0.10.0,<0.11.0"]
60
+ build-backend = "uv_build"
61
+
62
+ [dependency-groups]
63
+ dev = [
64
+ "coverage>=7.13.3",
65
+ "flake8>=7.3.0",
66
+ "mypy>=1.19.1",
67
+ "pylint>=4.0.4",
68
+ "pytest>=9.0.2",
69
+ "pytest-cov>=7.0.0",
70
+ "sphinx>=9.0.4",
71
+ "sphinx-autodoc-napoleon-typehints>=2.1.6",
72
+ "sphinx-autodoc-typehints>=3.6.1",
73
+ "sphinx-rtd-theme>=3.1.0",
74
+ "types-pyyaml>=6.0.12.20250915",
75
+ "yapf>=0.43.0",
76
+ ]
77
+
78
+ [tool.pytest.ini_options]
79
+ addopts = "--cov=specitems --cov-fail-under=100 --cov-branch --cov-report=term-missing"
@@ -0,0 +1,218 @@
1
+ # SPDX-License-Identifier: BSD-2-Clause
2
+ """ The specitems package. """
3
+
4
+ # Copyright (C) 2019, 2026 embedded brains GmbH & Co. KG
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ # 1. Redistributions of source code must retain the above copyright
10
+ # notice, this list of conditions and the following disclaimer.
11
+ # 2. Redistributions in binary form must reproduce the above copyright
12
+ # notice, this list of conditions and the following disclaimer in the
13
+ # documentation and/or other materials provided with the distribution.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
+ # POSSIBILITY OF SUCH DAMAGE.
26
+
27
+ from .cite import BibTeXCitationProvider
28
+ from .cliutil import (
29
+ create_argument_parser,
30
+ create_config,
31
+ init_logging,
32
+ load_config,
33
+ )
34
+ from .content import (
35
+ Content,
36
+ ContentAddContext,
37
+ Copyright,
38
+ Copyrights,
39
+ GenericContent,
40
+ GenericContentIterable,
41
+ MARKDOWN_ROLES,
42
+ get_value_plural,
43
+ list_terms,
44
+ make_copyright_statement,
45
+ split_copyright_statement,
46
+ to_camel_case,
47
+ )
48
+ from .contenttext import (
49
+ TextContent,
50
+ TextMapper,
51
+ latex_escape,
52
+ make_label,
53
+ )
54
+ from .contentmarkdown import (
55
+ MarkdownContent,
56
+ MarkdownMapper,
57
+ )
58
+ from .contentsphinx import (
59
+ SphinxContent,
60
+ SphinxMapper,
61
+ escape_code_line,
62
+ get_reference,
63
+ )
64
+ from .getvaluesubprocess import get_value_subprocess
65
+ from .glossary import (
66
+ DocumentGlossaryConfig,
67
+ GlossaryConfig,
68
+ augment_glossary_terms,
69
+ generate_glossary,
70
+ )
71
+ from .hashutil import (
72
+ base64_to_hex,
73
+ base64_to_hex_text,
74
+ hash_file,
75
+ hash_file_lines,
76
+ hash_file_lines_md5,
77
+ hash_file_lines_sha256,
78
+ hash_file_md5,
79
+ hash_file_sha256,
80
+ )
81
+ from .items import (
82
+ EmptyItem,
83
+ EmptyItemCache,
84
+ EnabledSet,
85
+ IS_ENABLED_OPS,
86
+ IsEnabled,
87
+ IsLinkEnabled,
88
+ Item,
89
+ ItemCache,
90
+ ItemCacheConfig,
91
+ ItemDataByUID,
92
+ ItemSelection,
93
+ ItemType,
94
+ ItemTypeProvider,
95
+ ItemView,
96
+ ItemViewGetMissing,
97
+ JSONItemCache,
98
+ Link,
99
+ SpecTypeProvider,
100
+ create_unique_link,
101
+ data_digest,
102
+ is_enabled,
103
+ is_enabled_with_ops,
104
+ item_is_enabled,
105
+ link_is_enabled,
106
+ load_data,
107
+ load_data_by_uid,
108
+ pickle_load_data_by_uid,
109
+ save_data,
110
+ to_collection,
111
+ to_iterable,
112
+ )
113
+ from .itemmapper import (
114
+ ItemGetValue,
115
+ ItemGetValueContext,
116
+ ItemGetValueMap,
117
+ ItemMapper,
118
+ ItemValueProvider,
119
+ get_value_default,
120
+ unpack_arg,
121
+ )
122
+ from .specdoc import SpecDocumentConfig, generate_specification_documentation
123
+ from .specverify import (
124
+ SpecVerifier,
125
+ VerifyStatus,
126
+ verify_specification_format,
127
+ )
128
+ from .subprocessaction import (
129
+ make_subprocess_environment,
130
+ run_subprocess_action,
131
+ )
132
+
133
+ __all__ = [
134
+ "BibTeXCitationProvider",
135
+ "Content",
136
+ "ContentAddContext",
137
+ "Copyright",
138
+ "Copyrights",
139
+ "DocumentGlossaryConfig",
140
+ "EmptyItem",
141
+ "EmptyItemCache",
142
+ "EnabledSet",
143
+ "GenericContent",
144
+ "GenericContentIterable",
145
+ "GlossaryConfig",
146
+ "IS_ENABLED_OPS",
147
+ "IsEnabled",
148
+ "IsLinkEnabled",
149
+ "Item",
150
+ "ItemCache",
151
+ "ItemCacheConfig",
152
+ "ItemDataByUID",
153
+ "ItemGetValue",
154
+ "ItemGetValueContext",
155
+ "ItemGetValueMap",
156
+ "ItemMapper",
157
+ "ItemSelection",
158
+ "ItemType",
159
+ "ItemTypeProvider",
160
+ "ItemValueProvider",
161
+ "ItemView",
162
+ "ItemViewGetMissing",
163
+ "JSONItemCache",
164
+ "Link",
165
+ "MARKDOWN_ROLES",
166
+ "MarkdownContent",
167
+ "MarkdownMapper",
168
+ "SpecDocumentConfig",
169
+ "SpecTypeProvider",
170
+ "SpecVerifier",
171
+ "SphinxContent",
172
+ "SphinxMapper",
173
+ "TextContent",
174
+ "TextMapper",
175
+ "VerifyStatus",
176
+ "augment_glossary_terms",
177
+ "base64_to_hex",
178
+ "base64_to_hex_text",
179
+ "create_argument_parser",
180
+ "create_config",
181
+ "create_unique_link",
182
+ "data_digest",
183
+ "escape_code_line",
184
+ "generate_glossary",
185
+ "generate_specification_documentation",
186
+ "get_reference",
187
+ "get_value_default",
188
+ "get_value_plural",
189
+ "get_value_subprocess",
190
+ "hash_file",
191
+ "hash_file_lines",
192
+ "hash_file_lines_md5",
193
+ "hash_file_lines_sha256",
194
+ "hash_file_md5",
195
+ "hash_file_sha256",
196
+ "init_logging",
197
+ "is_enabled",
198
+ "is_enabled_with_ops",
199
+ "item_is_enabled",
200
+ "latex_escape",
201
+ "link_is_enabled",
202
+ "list_terms",
203
+ "load_config",
204
+ "load_data",
205
+ "load_data_by_uid",
206
+ "make_copyright_statement",
207
+ "make_label",
208
+ "make_subprocess_environment",
209
+ "pickle_load_data_by_uid",
210
+ "run_subprocess_action",
211
+ "save_data",
212
+ "split_copyright_statement",
213
+ "to_camel_case",
214
+ "to_collection",
215
+ "to_iterable",
216
+ "unpack_arg",
217
+ "verify_specification_format",
218
+ ]
@@ -0,0 +1,149 @@
1
+ # SPDX-License-Identifier: BSD-2-Clause
2
+ """ Provides an item value provider for citations. """
3
+
4
+ # Copyright (C) 2025, 2026 embedded brains GmbH & Co. KG
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ # 1. Redistributions of source code must retain the above copyright
10
+ # notice, this list of conditions and the following disclaimer.
11
+ # 2. Redistributions in binary form must reproduce the above copyright
12
+ # notice, this list of conditions and the following disclaimer in the
13
+ # documentation and/or other materials provided with the distribution.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
+ # POSSIBILITY OF SUCH DAMAGE.
26
+
27
+ from typing import Callable
28
+
29
+ from .items import Item
30
+ from .itemmapper import ItemGetValueContext, ItemType, ItemValueProvider
31
+ from .content import list_terms
32
+ from .contenttext import TextContent, TextMapper, latex_escape
33
+
34
+ _Fields = dict[str, str | list[str]]
35
+ _GetFields = Callable[[Item], tuple[str, _Fields]]
36
+
37
+ _FIELDS = {
38
+ "author", "booktitle", "chapter", "doi", "edition", "editor",
39
+ "howpublished", "institution", "journal", "month", "note", "number",
40
+ "organization", "pages", "publisher", "school", "series", "title",
41
+ "volume", "year"
42
+ }
43
+
44
+ _NO_LATEX_ESCAPE = {"url"}
45
+
46
+ _DOUBLE_QUOTE = {
47
+ "author", "booktitle", "editor", "howpublished", "institution",
48
+ "organization", "publisher", "school", "title"
49
+ }
50
+
51
+ _PERSONS = {"author", "editor"}
52
+
53
+
54
+ def _get_fields(item: Item) -> tuple[str, _Fields]:
55
+ _, _, publication_type = item.type.partition("/")
56
+ fields: _Fields = dict(
57
+ (key, item[key]) for key in _FIELDS.intersection(item.data.keys()))
58
+ url = item.get("work-url", None)
59
+ if url is not None:
60
+ fields["url"] = url
61
+ return publication_type, fields
62
+
63
+
64
+ class BibTeXCitationProvider(ItemValueProvider):
65
+ """ Provides citation values and BibTeX entries. """
66
+
67
+ def __init__(self, mapper: TextMapper) -> None:
68
+ super().__init__(mapper)
69
+ self._citations: set[Item] = set()
70
+ self._get_fields: dict[str, _GetFields] = {}
71
+ mapper.add_get_value("reference:/cite", self._get_cite)
72
+ mapper.add_get_value("reference:/cite-long", self._get_cite_long)
73
+
74
+ def reset(self) -> None:
75
+ self._citations.clear()
76
+
77
+ def get_cite_group(self, ctx: ItemGetValueContext) -> str:
78
+ """
79
+ Get the citations associated with the citation group key provided by
80
+ the arguments of the context.
81
+ """
82
+ citations: list[str] = []
83
+ for link in ctx.item.links_to_children("citation-group-member"):
84
+ if link["citation-group-key"] == ctx.args:
85
+ citations.append(f"${{{link.uid}:/cite}}")
86
+ return ctx.substitute(list_terms(citations))
87
+
88
+ def get_bibtex_entries(self, ctx: ItemGetValueContext) -> str:
89
+ """ Get the BibTeX entries for the collected citations. """
90
+ mapper = ctx.mapper
91
+ assert isinstance(mapper, TextMapper)
92
+ content = mapper.create_content()
93
+ self.add_bibtex_entries(content)
94
+ return content.join()
95
+
96
+ def _add_get_fields_for_subtypes(self, spec_type: ItemType, type_path: str,
97
+ get_fields: _GetFields) -> None:
98
+ if spec_type.refinements:
99
+ for key, refinement in spec_type.refinements.items():
100
+ self._add_get_fields_for_subtypes(refinement,
101
+ f"{type_path}/{key}",
102
+ get_fields)
103
+ else:
104
+ self._get_fields[type_path] = get_fields
105
+
106
+ def add_get_fields(self, item_type: str, get_fields: _GetFields) -> None:
107
+ """ Add the get fields method for the item type. """
108
+ self.mapper.add_get_value(f"{item_type}:/cite", self._get_cite)
109
+ self.mapper.add_get_value(f"{item_type}:/cite-long",
110
+ self._get_cite_long)
111
+ spec_type = self.mapper.item.cache.type_provider.root_type
112
+ for name in item_type.split("/"):
113
+ spec_type = spec_type.refinements[name]
114
+ self._add_get_fields_for_subtypes(spec_type, item_type, get_fields)
115
+
116
+ def add_bibtex_entries(self, content: TextContent) -> None:
117
+ """ Add BibTeX entries for the collected citations to the content. """
118
+ for item in sorted(self._citations):
119
+ publication_type, fields = self._get_fields.get(
120
+ item.type, _get_fields)(item)
121
+ for key in _PERSONS.intersection(fields.keys()):
122
+ if isinstance(fields[key], list):
123
+ fields[key] = " and ".join(fields[key])
124
+ content.append(f"@{publication_type}{{{item.ident},")
125
+ for field, value in sorted(fields.items()):
126
+ assert isinstance(value, str)
127
+ value = self.mapper.substitute(value)
128
+ if field not in _NO_LATEX_ESCAPE:
129
+ value = latex_escape(value)
130
+ if field in _DOUBLE_QUOTE:
131
+ value = f"{{{value}}}"
132
+ content.append(f" {field} = {{{value}}},")
133
+ content.append("}")
134
+
135
+ def _get_cite(self, ctx: ItemGetValueContext) -> str:
136
+ self._citations.add(ctx.item)
137
+ assert isinstance(self.mapper, TextMapper)
138
+ content = self.mapper.create_content()
139
+ return content.cite(ctx.item.ident)
140
+
141
+ def _get_cite_long(self, ctx: ItemGetValueContext) -> str:
142
+ self._citations.add(ctx.item)
143
+ _, fields = self._get_fields.get(ctx.item.type, _get_fields)(ctx.item)
144
+ assert isinstance(self.mapper, TextMapper)
145
+ content = self.mapper.create_content()
146
+ title = fields["title"]
147
+ assert isinstance(title, str)
148
+ title = content.emphasize(ctx.substitute_and_transform(title))
149
+ return f"{title} {content.cite(ctx.item.ident)}"
@@ -0,0 +1,92 @@
1
+ # SPDX-License-Identifier: BSD-2-Clause
2
+ """ Provides a command line interface to hash a list of files. """
3
+
4
+ # Copyright (C) 2023, 2026 embedded brains GmbH & Co. KG
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ # 1. Redistributions of source code must retain the above copyright
10
+ # notice, this list of conditions and the following disclaimer.
11
+ # 2. Redistributions in binary form must reproduce the above copyright
12
+ # notice, this list of conditions and the following disclaimer in the
13
+ # documentation and/or other materials provided with the distribution.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
+ # POSSIBILITY OF SUCH DAMAGE.
26
+
27
+ import argparse
28
+ import sys
29
+
30
+ from .hashutil import (base64_to_hex, hash_file, hash_file_lines,
31
+ hash_file_md5, hash_file_lines_md5, hash_file_sha256,
32
+ hash_file_lines_sha256)
33
+
34
+ _HASH_FILE = {
35
+ "MD5": hash_file_md5,
36
+ "SHA256": hash_file_sha256,
37
+ "SHA512": hash_file
38
+ }
39
+
40
+ _HASH_FILE_LINES = {
41
+ "MD5": hash_file_lines_md5,
42
+ "SHA256": hash_file_lines_sha256,
43
+ "SHA512": hash_file_lines
44
+ }
45
+
46
+
47
+ def _identity(digest: str) -> str:
48
+ return digest
49
+
50
+
51
+ _FORMATTER = {"base64url": _identity, "hex": base64_to_hex}
52
+
53
+
54
+ def clihash(argv: list[str] = sys.argv) -> None:
55
+ """ Hash the list of files. """
56
+ parser = argparse.ArgumentParser()
57
+ parser.add_argument('--algorithm',
58
+ choices=["MD5", "SHA256", "SHA512"],
59
+ type=str.upper,
60
+ default="SHA512",
61
+ help="hash algorithm (default: SHA512)")
62
+ parser.add_argument('--format',
63
+ choices=["base64url", "hex"],
64
+ type=str.lower,
65
+ default="base64url",
66
+ help="digest format (default: base64url)")
67
+ parser.add_argument('--line',
68
+ action="append",
69
+ default=None,
70
+ help=("hash line B (format: B) or lines "
71
+ "from B to E excluding E (format B:E); "
72
+ "the line numbering starts with one"))
73
+ parser.add_argument("files",
74
+ metavar="FILES",
75
+ nargs="+",
76
+ help="the files to hash")
77
+ args = parser.parse_args(argv[1:])
78
+ formatter = _FORMATTER[args.format]
79
+ if args.line:
80
+ lines: list[tuple[int, int]] = []
81
+ do_hash_file_lines = _HASH_FILE_LINES[args.algorithm]
82
+ for line in args.line:
83
+ begin, _, end = line.partition(":")
84
+ if not end:
85
+ end = int(begin) + 1
86
+ lines.append((int(begin), int(end)))
87
+ for path in args.files:
88
+ print(path, formatter(do_hash_file_lines(path, lines)))
89
+ else:
90
+ do_hash_file = _HASH_FILE[args.algorithm]
91
+ for path in args.files:
92
+ print(path, formatter(do_hash_file(path)))
@@ -0,0 +1,66 @@
1
+ # SPDX-License-Identifier: BSD-2-Clause
2
+ """
3
+ Provides a command line interface to convert specification items inside
4
+ specification directories to a pickle data file.
5
+ """
6
+
7
+ # Copyright (C) 2025 embedded brains GmbH & Co. KG
8
+ #
9
+ # Redistribution and use in source and binary forms, with or without
10
+ # modification, are permitted provided that the following conditions
11
+ # are met:
12
+ # 1. Redistributions of source code must retain the above copyright
13
+ # notice, this list of conditions and the following disclaimer.
14
+ # 2. Redistributions in binary form must reproduce the above copyright
15
+ # notice, this list of conditions and the following disclaimer in the
16
+ # documentation and/or other materials provided with the distribution.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ # POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ import argparse
31
+ import os
32
+ import pickle
33
+ import sys
34
+
35
+ from .items import ItemCache, ItemCacheConfig
36
+
37
+
38
+ def _filter(data: dict) -> dict:
39
+ return dict((key, value) for key, value in sorted(data.items())
40
+ if not key.startswith("_"))
41
+
42
+
43
+ def clipickle(argv: list[str] = sys.argv) -> None:
44
+ """
45
+ Convert specification items inside specification directories to a pickle
46
+ data file.
47
+ """
48
+
49
+ parser = argparse.ArgumentParser()
50
+ parser.add_argument("specdirs",
51
+ metavar="SPECIFICATION_DIRECTORIES",
52
+ nargs="+",
53
+ help="a non-empty list of specification directories")
54
+ parser.add_argument("pickle",
55
+ metavar="PICKLE",
56
+ nargs=1,
57
+ help="the pickle file to generate")
58
+ args = parser.parse_args(argv[1:])
59
+ config = ItemCacheConfig(paths=args.specdirs, initialize_links=False)
60
+ item_cache = ItemCache(config)
61
+ data = dict(
62
+ (uid, _filter(item.data)) for uid, item in sorted(item_cache.items()))
63
+ pickle_file = args.pickle[0]
64
+ os.makedirs(os.path.dirname(pickle_file), exist_ok=True)
65
+ with open(pickle_file, "wb") as out:
66
+ pickle.dump(data, out)