specitems 1.5.2__tar.gz → 1.6.0__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 (25) hide show
  1. {specitems-1.5.2 → specitems-1.6.0}/PKG-INFO +1 -1
  2. {specitems-1.5.2 → specitems-1.6.0}/pyproject.toml +1 -1
  3. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/itemmapper.py +35 -18
  4. {specitems-1.5.2 → specitems-1.6.0}/README.md +0 -0
  5. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/__init__.py +0 -0
  6. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/cite.py +0 -0
  7. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/clihash.py +0 -0
  8. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/clipickle.py +0 -0
  9. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/clispecdoc.py +0 -0
  10. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/cliutil.py +0 -0
  11. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/cliyamlquery.py +0 -0
  12. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/content.py +0 -0
  13. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/contentcommonmark.py +0 -0
  14. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/contentmarkdown.py +0 -0
  15. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/contentsphinx.py +0 -0
  16. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/contenttext.py +0 -0
  17. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/getvaluesubprocess.py +0 -0
  18. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/glossary.py +0 -0
  19. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/hashutil.py +0 -0
  20. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/items.py +0 -0
  21. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/py.typed +0 -0
  22. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/spec.pickle +0 -0
  23. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/specdoc.py +0 -0
  24. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/specverify.py +0 -0
  25. {specitems-1.5.2 → specitems-1.6.0}/src/specitems/subprocessaction.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specitems
3
- Version: 1.5.2
3
+ Version: 1.6.0
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.5.2"
7
+ version = "1.6.0"
8
8
  description = "Provides interfaces to work with specification items."
9
9
  authors = [
10
10
  {name = "The specitems Authors", email = "specthings@embedded-brains.de"}
@@ -30,7 +30,6 @@ import copy
30
30
  import dataclasses
31
31
  import os
32
32
  import re
33
- import string
34
33
  from typing import Any, Callable, Iterator, Optional
35
34
 
36
35
  from .items import Item, ItemType
@@ -38,29 +37,44 @@ from .items import Item, ItemType
38
37
  ItemGetValue = Callable[["ItemGetValueContext"], Any]
39
38
  ItemGetValueMap = dict[str, tuple[ItemGetValue, Any]]
40
39
 
40
+ _VAR_NAME = "(?:[a-zA-Z0-9._/-]+|\\*):[\\[\\]a-zA-Z0-9._/-]+(?::[^${}]*)?"
41
+ _VAR_SINGLE = re.compile(f"^[$@]\\{{{_VAR_NAME}\\}}$")
42
+ _VAR_TOKENS = re.compile(r"(?:\$\$(?=\{|\$\{))|(?:@@(?=\{|@\{))|"
43
+ f"[$@]\\{{{_VAR_NAME}\\}}|(?:[$@]\\{{)")
41
44
 
42
- class _ItemTemplate(string.Template):
43
- """ String template for item mapper identifiers. """
44
- idpattern = "(?:[a-zA-Z0-9._/-]+|\\*):[\\[\\]a-zA-Z0-9._/-]+(?::[^${}]*)?"
45
45
 
46
+ class _ItemMapperError(Exception):
46
47
 
47
- class _ItemMapperContext(dict):
48
+ def __init__(self, start: int, end: int) -> None:
49
+ super().__init__()
50
+ self.start = start
51
+ self.end = end
52
+
53
+
54
+ class _ItemMapperContext:
48
55
  """
49
56
  Provides a context to map identifiers to items and attribute values.
50
57
  """
51
58
 
59
+ # pylint: disable=too-few-public-methods
52
60
  def __init__(self, mapper: "ItemMapper", item: Optional[Item],
53
61
  prefix: str) -> None:
54
- super().__init__()
55
62
  self._mapper = mapper
56
63
  self._item = item
57
64
  self._prefix = prefix
58
65
 
59
- def __getitem__(self, identifier: str) -> Any:
60
- return self._mapper.map(identifier, self._item, self._prefix)[2]
61
-
62
-
63
- _SINGLE_SUBSTITUTION = re.compile(f"^\\${{{_ItemTemplate.idpattern}}}$")
66
+ def replace(self, mobj: re.Match) -> str:
67
+ """ Replace the match. """
68
+ token = mobj.group(0)
69
+ if len(token) > 2:
70
+ try:
71
+ return str(
72
+ self._mapper.map(token[2:-1], self._item, self._prefix)[2])
73
+ except Exception as err:
74
+ raise _ItemMapperError(mobj.start(), mobj.end()) from err
75
+ if token[1] == "{":
76
+ raise _ItemMapperError(mobj.start(), mobj.end())
77
+ return token[0]
64
78
 
65
79
 
66
80
  class _GetValueDictionary(dict):
@@ -464,13 +478,16 @@ class ItemMapper(abc.ABC):
464
478
  """
465
479
  if not text:
466
480
  return ""
481
+ context = _ItemMapperContext(self, item, prefix)
467
482
  try:
468
- context = _ItemMapperContext(self, item, prefix)
469
- return _ItemTemplate(text).substitute(context)
470
- except Exception as err:
483
+ return _VAR_TOKENS.sub(context.replace, text)
484
+ except _ItemMapperError as err:
485
+ start = max(text.count("\n", 0, err.start) - 3, 0)
486
+ end = text.count("\n", 0, err.end) + 4
471
487
  spec = self.item.spec if item is None else item.spec
472
- enumerated = "\n".join(f"{i + 1:>5}: {line}"
473
- for i, line in enumerate(text.splitlines()))
488
+ enumerated = "\n".join(
489
+ f"{i + start + 1}: {line}"
490
+ for i, line in enumerate(text.splitlines()[start:end]))
474
491
  msg = (f"substitution for {spec} using prefix '{prefix}' "
475
492
  f"failed for text:\n{enumerated}")
476
493
  raise ValueError(msg) from err
@@ -490,7 +507,7 @@ class ItemMapper(abc.ABC):
490
507
  for index, element in enumerate(data):
491
508
  prefix_2 = f"{prefix}[{index}]"
492
509
  if isinstance(element, str):
493
- match = _SINGLE_SUBSTITUTION.search(element)
510
+ match = _VAR_SINGLE.search(element)
494
511
  if match:
495
512
  new_element = self.map(element[2:-1], item,
496
513
  prefix_2)[2]
@@ -537,7 +554,7 @@ class ItemMapper(abc.ABC):
537
554
  element = self.substitute_data(element["value"], item,
538
555
  prefix_2)
539
556
  elif isinstance(element, str):
540
- if _SINGLE_SUBSTITUTION.search(element):
557
+ if _VAR_SINGLE.search(element):
541
558
  element = self.map(element[2:-1], item, prefix_2)[2]
542
559
  else:
543
560
  element = self.substitute(element, item, prefix_2)
File without changes