specitems 1.7.0__tar.gz → 1.7.2__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.
Files changed (33) hide show
  1. {specitems-1.7.0 → specitems-1.7.2}/PKG-INFO +1 -1
  2. {specitems-1.7.0 → specitems-1.7.2}/pyproject.toml +1 -1
  3. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/__init__.py +11 -4
  4. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/cite.py +108 -10
  5. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/spec.pickle +0 -0
  6. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/specconfig.py +38 -2
  7. {specitems-1.7.0 → specitems-1.7.2}/README.md +0 -0
  8. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/clangformat.py +0 -0
  9. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/clihash.py +0 -0
  10. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/clipickle.py +0 -0
  11. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/clispecdoc.py +0 -0
  12. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/clitypeview.py +0 -0
  13. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/cliutil.py +0 -0
  14. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/cliverify.py +0 -0
  15. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/cliyamlquery.py +0 -0
  16. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/content.py +0 -0
  17. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/contentcommonmark.py +0 -0
  18. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/contentmarkdown.py +0 -0
  19. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/contentsphinx.py +0 -0
  20. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/contenttext.py +0 -0
  21. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/copyrights.py +0 -0
  22. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/getvaluesubprocess.py +0 -0
  23. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/glossary.py +0 -0
  24. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/hashutil.py +0 -0
  25. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/itemmapper.py +0 -0
  26. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/items.py +1 -1
  27. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/licenseinfo.py +0 -0
  28. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/py.typed +0 -0
  29. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/spdx.py +0 -0
  30. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/specdoc.py +0 -0
  31. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/specformatter.py +0 -0
  32. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/specverify.py +0 -0
  33. {specitems-1.7.0 → specitems-1.7.2}/src/specitems/subprocessaction.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specitems
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: Provides interfaces to work with specification items.
5
5
  Keywords: certification,documentation,markdown,qualification,requirements-management,traceability
6
6
  Author: The specitems Authors
@@ -4,7 +4,7 @@
4
4
 
5
5
  [project]
6
6
  name = "specitems"
7
- version = "1.7.0"
7
+ version = "1.7.2"
8
8
  description = "Provides interfaces to work with specification items."
9
9
  authors = [
10
10
  {name = "The specitems Authors", email = "specthings@embedded-brains.de"}
@@ -24,7 +24,8 @@
24
24
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
25
  # POSSIBILITY OF SUCH DAMAGE.
26
26
 
27
- from .cite import BibTeXCitationProvider
27
+ from .cite import (BibTeXCitationProvider, get_reference_target,
28
+ get_reference_work, make_anchor, ReferenceTarget)
28
29
  from .clangformat import ClangFormatter
29
30
  from .cliutil import (
30
31
  LoggingStatus,
@@ -144,9 +145,10 @@ from .itemmapper import (
144
145
  from .licenseinfo import (LicenseAggregate, LicenseEntry, LicenseProvider)
145
146
  from .spdx import (get_license_list_version, parse_license_expression,
146
147
  parse_license_identifier, permits)
147
- from .specconfig import (CONFIG_FILE, CONFIG_UID, check_license_items,
148
- create_content_context, create_type_provider,
149
- find_config_file, load_config_item, yield_tasks)
148
+ from .specconfig import (CONFIG_FILE, CONFIG_UID, add_inline_items,
149
+ check_license_items, create_content_context,
150
+ create_type_provider, find_config_file,
151
+ load_config_item, yield_tasks)
150
152
  from .specdoc import (SpecDocumentConfig, add_specification_documentation,
151
153
  generate_specification_documentation)
152
154
  from .specformatter import SpecYAMLFormatter
@@ -207,6 +209,7 @@ __all__ = [
207
209
  "MarkdownContent",
208
210
  "MarkdownMapper",
209
211
  "ROW_SPAN",
212
+ "ReferenceTarget",
210
213
  "SpecDocumentConfig",
211
214
  "SpecTypeProvider",
212
215
  "SpecVerifier",
@@ -216,6 +219,7 @@ __all__ = [
216
219
  "SubstitutionError",
217
220
  "TextContent",
218
221
  "TextMapper",
222
+ "add_inline_items",
219
223
  "add_license_arguments",
220
224
  "add_specification_documentation",
221
225
  "atomic_dump_to_file",
@@ -238,6 +242,8 @@ __all__ = [
238
242
  "get_item_cache_arguments",
239
243
  "get_license_list_version",
240
244
  "get_reference",
245
+ "get_reference_target",
246
+ "get_reference_work",
241
247
  "get_value_default",
242
248
  "get_value_plural",
243
249
  "get_value_subprocess",
@@ -258,6 +264,7 @@ __all__ = [
258
264
  "load_config_item",
259
265
  "load_data",
260
266
  "load_data_by_uid",
267
+ "make_anchor",
261
268
  "make_copyright_statement",
262
269
  "make_label",
263
270
  "make_lines",
@@ -24,9 +24,11 @@
24
24
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
25
  # POSSIBILITY OF SUCH DAMAGE.
26
26
 
27
- from typing import Callable
27
+ import dataclasses
28
+ import re
29
+ from typing import Callable, Optional
28
30
 
29
- from .items import Item
31
+ from .items import Item, Link
30
32
  from .itemmapper import ItemGetValueContext, ItemType, ItemValueProvider
31
33
  from .content import list_terms
32
34
  from .contenttext import TextContent, TextMapper, latex_escape
@@ -50,6 +52,79 @@ _DOUBLE_QUOTE = {
50
52
 
51
53
  _PERSONS = {"author", "editor"}
52
54
 
55
+ _NOT_ID_CHARS = re.compile(r"[^a-z0-9]+")
56
+
57
+
58
+ def make_anchor(label: str) -> str:
59
+ """
60
+ Make the anchor of the label in the way Sphinx makes the identifier of a
61
+ label target.
62
+ """
63
+ return _NOT_ID_CHARS.sub("-", label.lower()).strip("-")
64
+
65
+
66
+ @dataclasses.dataclass
67
+ class ReferenceTarget:
68
+ """ Represents the target of a reference link. """
69
+
70
+ # pylint: disable=too-many-instance-attributes
71
+
72
+ #: The referenced work.
73
+ work: Item
74
+
75
+ #: The name of the area within the work.
76
+ name: Optional[str]
77
+
78
+ #: The label of the area within the work.
79
+ label: Optional[str]
80
+
81
+ #: The location of the area within the work.
82
+ location: Optional[str]
83
+
84
+ #: The path of the area relative to the base URL of the work.
85
+ path: str
86
+
87
+ #: The URL of the area. The caller substitutes it in the context of the
88
+ #: work.
89
+ url: Optional[str]
90
+
91
+
92
+ def get_reference_work(item: Item) -> Item:
93
+ """
94
+ Get the referenced work of the reference or reference location item.
95
+ """
96
+ if item.type == "reference-location":
97
+ works = list(item.parents("reference-location"))
98
+ if len(works) != 1:
99
+ raise ValueError(f"{item.uid}: a reference location shall have "
100
+ "exactly one link to the referenced work")
101
+ return works[0]
102
+ return item
103
+
104
+
105
+ def get_reference_target(item: Item,
106
+ link: Optional[Link] = None) -> ReferenceTarget:
107
+ """
108
+ Get the target of the reference or reference location item.
109
+
110
+ The attributes of the optional reference link take precedence over the
111
+ attributes of the reference location. The paths of the reference location
112
+ and the reference link are concatenated.
113
+ """
114
+ work = get_reference_work(item)
115
+ link_data = {} if link is None else link.data
116
+ area = item.data if item is not work else {}
117
+ name = area.get("name")
118
+ label = link_data.get("label", area.get("label"))
119
+ location = link_data.get("location", area.get("location"))
120
+ path = f"{area.get('path', '')}{link_data.get('path', '')}"
121
+ base = work.get("work-base-url")
122
+ url: Optional[str] = None
123
+ if base is not None:
124
+ anchor = "" if label is None else f"#{make_anchor(label)}"
125
+ url = f"{base}{path}{anchor}"
126
+ return ReferenceTarget(work, name, label, location, path, url)
127
+
53
128
 
54
129
  def _get_fields(item: Item) -> tuple[str, _Fields]:
55
130
  _, _, publication_type = item.type.partition("/")
@@ -70,6 +145,10 @@ class BibTeXCitationProvider(ItemValueProvider):
70
145
  self._get_fields: dict[str, _GetFields] = {}
71
146
  mapper.add_get_value("reference:/cite", self._get_cite)
72
147
  mapper.add_get_value("reference:/cite-long", self._get_cite_long)
148
+ mapper.add_get_value("reference-location:/cite",
149
+ self._get_cite_location)
150
+ mapper.add_get_value("reference-location:/cite-long",
151
+ self._get_cite_long_location)
73
152
 
74
153
  def reset(self) -> None:
75
154
  self._citations.clear()
@@ -135,18 +214,37 @@ class BibTeXCitationProvider(ItemValueProvider):
135
214
  content.append(f" {field} = {{{value}}},")
136
215
  content.append("}")
137
216
 
138
- def _get_cite(self, ctx: ItemGetValueContext) -> str:
139
- self._citations.add(ctx.item)
217
+ def _cite(self, work: Item) -> str:
218
+ self._citations.add(work)
140
219
  assert isinstance(self.mapper, TextMapper)
141
220
  content = self.mapper.create_content()
142
- return content.cite(ctx.item.ident)
221
+ return content.cite(work.ident)
143
222
 
144
- def _get_cite_long(self, ctx: ItemGetValueContext) -> str:
145
- self._citations.add(ctx.item)
146
- _, fields = self.get_fields(ctx.item)
223
+ def _cite_long(self, ctx: ItemGetValueContext, work: Item,
224
+ name: Optional[str]) -> str:
225
+ _, fields = self.get_fields(work)
147
226
  assert isinstance(self.mapper, TextMapper)
148
227
  content = self.mapper.create_content()
149
228
  title = fields["title"]
150
229
  assert isinstance(title, str)
151
- title = content.emphasize(ctx.substitute_and_transform(title))
152
- return f"{title} {content.cite(ctx.item.ident)}"
230
+ if work is ctx.item:
231
+ title = ctx.substitute(title)
232
+ else:
233
+ title = self.mapper.substitute_data(title, work)
234
+ title = content.emphasize(ctx.transform(title))
235
+ if name is not None:
236
+ title = f"{title}, {ctx.substitute_and_transform(name)}"
237
+ return f"{title} {self._cite(work)}"
238
+
239
+ def _get_cite(self, ctx: ItemGetValueContext) -> str:
240
+ return self._cite(ctx.item)
241
+
242
+ def _get_cite_long(self, ctx: ItemGetValueContext) -> str:
243
+ return self._cite_long(ctx, ctx.item, None)
244
+
245
+ def _get_cite_location(self, ctx: ItemGetValueContext) -> str:
246
+ return self._cite(get_reference_work(ctx.item))
247
+
248
+ def _get_cite_long_location(self, ctx: ItemGetValueContext) -> str:
249
+ return self._cite_long(ctx, get_reference_work(ctx.item),
250
+ ctx.item.get("name"))
@@ -24,6 +24,7 @@
24
24
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
25
  # POSSIBILITY OF SUCH DAMAGE.
26
26
 
27
+ import copy
27
28
  import logging
28
29
  from pathlib import Path
29
30
  from typing import Any, Iterator, Optional
@@ -31,8 +32,8 @@ from typing import Any, Iterator, Optional
31
32
  from .cliutil import load_config
32
33
  from .content import ContentContext
33
34
  from .licenseinfo import LicenseAggregate, LicenseProvider
34
- from .items import (EmptyItemCache, Item, ItemDataByUID, ItemTypeProvider,
35
- SpecTypeProvider)
35
+ from .items import (EmptyItemCache, Item, ItemCache, ItemDataByUID,
36
+ ItemTypeProvider, SpecTypeProvider)
36
37
  from .specverify import SpecVerifier
37
38
 
38
39
  #: The name of the configuration file of a tree.
@@ -204,6 +205,41 @@ def check_license_items(config: Item, provider: LicenseProvider) -> None:
204
205
  raise ValueError("\n".join(errors))
205
206
 
206
207
 
208
+ def add_inline_items(config: Item, item_cache: ItemCache) -> None:
209
+ """
210
+ Add the inline items of the configuration to the item cache.
211
+
212
+ An inline item is part of the configuration file. It takes the license
213
+ and the copyrights of the configuration item, unless it states its own.
214
+ An inline item without links gets an empty list of links. An inline item
215
+ without an enabled-by attribute is enabled. An inline item without a type
216
+ has the inline type. An inline item may link to another inline item.
217
+
218
+ Args:
219
+ config: The configuration item.
220
+ item_cache: The item cache of the tree.
221
+
222
+ Raises:
223
+ ValueError: An inline item exists already in the item cache or in the
224
+ configuration.
225
+ """
226
+ data_by_uid: ItemDataByUID = {}
227
+ for entry in config.get("inline-items", []):
228
+ uid = entry["uid"]
229
+ if (uid in item_cache or uid in item_cache.proxies
230
+ or uid in data_by_uid):
231
+ raise ValueError(f"the inline item {uid} exists already")
232
+ data = copy.deepcopy(entry["data"])
233
+ for key, value in (("SPDX-License-Identifier",
234
+ config["SPDX-License-Identifier"]),
235
+ ("copyrights", config["copyrights"]),
236
+ ("enabled-by", True), ("links", []), ("type",
237
+ "inline")):
238
+ data.setdefault(key, copy.deepcopy(value))
239
+ data_by_uid[uid] = data
240
+ item_cache.add_items(data_by_uid)
241
+
242
+
207
243
  def yield_tasks(config: Item, task_type: str) -> Iterator[dict]:
208
244
  """
209
245
  Yield the tasks of the type in the order of the configuration.
File without changes
@@ -1215,11 +1215,11 @@ class ItemCache(dict):
1215
1215
  data_by_uid.update(
1216
1216
  load_data_by_uid(path, self._cache_directory, uid_prefix,
1217
1217
  the_format))
1218
- self.add_items(data_by_uid, config.initialize_links, False)
1219
1218
  if type_provider is None:
1220
1219
  type_provider = ItemTypeProvider(data_by_uid,
1221
1220
  config.spec_type_root_uid)
1222
1221
  self.type_provider = type_provider
1222
+ self.add_items(data_by_uid, config.initialize_links, False)
1223
1223
  for item in self.values():
1224
1224
  self.type_provider.set_type(item)
1225
1225