markdown-to-confluence 0.4.4__py3-none-any.whl → 0.4.6__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.
md2conf/scanner.py CHANGED
@@ -15,6 +15,8 @@ import yaml
15
15
  from strong_typing.core import JsonType
16
16
  from strong_typing.serialization import DeserializerOptions, json_to_object
17
17
 
18
+ from .mermaid import MermaidConfigProperties
19
+
18
20
  T = TypeVar("T")
19
21
 
20
22
 
@@ -155,3 +157,47 @@ class Scanner:
155
157
  properties=properties,
156
158
  text=text,
157
159
  )
160
+
161
+
162
+ @dataclass
163
+ class MermaidProperties:
164
+ """
165
+ An object that holds the front-matter properties structure for Mermaid diagrams.
166
+
167
+ :param title: The title of the diagram.
168
+ :param config: Configuration options for rendering.
169
+ """
170
+
171
+ title: Optional[str] = None
172
+ config: Optional[MermaidConfigProperties] = None
173
+
174
+
175
+ class MermaidScanner:
176
+ """
177
+ Extracts properties from the JSON/YAML front-matter of a Mermaid diagram.
178
+ """
179
+
180
+ def read(self, content: str) -> MermaidProperties:
181
+ """
182
+ Extracts rendering preferences from a Mermaid front-matter content.
183
+
184
+ ```
185
+ ---
186
+ title: Tiny flow diagram
187
+ config:
188
+ scale: 1
189
+ ---
190
+ flowchart LR
191
+ A[Component A] --> B[Component B]
192
+ B --> C[Component C]
193
+ ```
194
+ """
195
+
196
+ properties, text = extract_frontmatter_properties(content)
197
+ if properties is not None:
198
+ front_matter = _json_to_object(MermaidProperties, properties)
199
+ config = front_matter.config or MermaidConfigProperties()
200
+
201
+ return MermaidProperties(title=front_matter.title, config=config)
202
+
203
+ return MermaidProperties()
md2conf/text.py ADDED
@@ -0,0 +1,54 @@
1
+ """
2
+ Publish Markdown files to Confluence wiki.
3
+
4
+ Copyright 2022-2025, Levente Hunyadi
5
+
6
+ :see: https://github.com/hunyadi/md2conf
7
+ """
8
+
9
+
10
+ def wrap_text(text: str, line_length: int = 160) -> str:
11
+ """
12
+ Wraps text by replacing individual whitespace characters with a linefeed such that lines in the output honor the specified line length.
13
+
14
+ :param text: Input text, optionally with existing UNIX line endings.
15
+ :param line_length: Desired line length.
16
+ :returns: Wrapped output text. Long words that exceed the specified line length are not broken.
17
+ """
18
+
19
+ if line_length < 1:
20
+ raise ValueError("expected: line_length > 0")
21
+
22
+ input = text.encode("utf-8")
23
+ output = bytearray(len(input))
24
+ pos = 0
25
+ length = len(input)
26
+
27
+ while pos < length:
28
+ end = min(pos + line_length, length)
29
+
30
+ # find any linefeed already in input
31
+ left = pos
32
+ while left < end and input[left] != 0x0A:
33
+ left += 1
34
+ if left != end:
35
+ output[pos : left + 1] = input[pos : left + 1]
36
+ pos = left + 1 # include linefeed
37
+ continue
38
+
39
+ # find the nearest whitespace before end of line
40
+ right = end
41
+ while right > pos and input[right - 1] not in b"\t\v\f\r ":
42
+ right -= 1
43
+
44
+ if right == pos or end == length:
45
+ # no whitespace found or at end of input; copy the rest
46
+ output[pos:end] = input[pos:end]
47
+ pos = end
48
+ else:
49
+ # replace the whitespace with a newline
50
+ output[pos : right - 1] = input[pos : right - 1]
51
+ output[right - 1] = 0x0A # linefeed '\n'
52
+ pos = right # skip the whitespace (already replaced)
53
+
54
+ return output.decode("utf-8")
md2conf/xml.py CHANGED
@@ -87,6 +87,8 @@ def is_xml_equal(
87
87
  """
88
88
  Compare two XML documents for equivalence, ignoring leading/trailing whitespace differences and attribute definition order.
89
89
 
90
+ Elements may be excluded, in which case they compare equal to any element of the same type that has the same tail text.
91
+
90
92
  :param tree1: XML document as an element tree.
91
93
  :param tree2: XML document as an element tree.
92
94
  :param skip_attributes: Attributes to exclude, in `{namespace}name` notation.
@@ -101,3 +103,38 @@ def element_to_text(node: ET._Element) -> str:
101
103
  "Returns all text contained in an element as a concatenated string."
102
104
 
103
105
  return "".join(node.itertext()).strip()
106
+
107
+
108
+ def unwrap_substitute(name: str, root: ET._Element) -> None:
109
+ """
110
+ Substitutes all occurrences of an element with its contents.
111
+
112
+ :param name: Element tag name to find and replace.
113
+ :param root: Top-most element at which to start.
114
+ """
115
+
116
+ for node in root.iterdescendants(name):
117
+ if node.text:
118
+ # append first piece of text in this element at the end of previous sibling, or text contained by parent
119
+ if (prev_node := node.getprevious()) is not None:
120
+ prev_node.tail = (prev_node.tail or "") + node.text
121
+ elif (parent_node := node.getparent()) is not None: # always true except for root
122
+ parent_node.text = (parent_node.text or "") + node.text
123
+ else:
124
+ raise NotImplementedError("must always have a previous sibling or a parent")
125
+ if node.tail:
126
+ if len(node) > 0:
127
+ # append text immediately following the closing tag of this element to the last child element of this element
128
+ last_node = node[-1]
129
+ last_node.tail = (last_node.tail or "") + node.tail
130
+ else: # node has no child elements, only text
131
+ if (prev_node := node.getprevious()) is not None:
132
+ prev_node.tail = (prev_node.tail or "") + node.tail
133
+ elif (parent_node := node.getparent()) is not None: # always true except for root
134
+ parent_node.text = (parent_node.text or "") + node.tail
135
+ else:
136
+ raise NotImplementedError("must always have a previous sibling or a parent")
137
+ for child in node.iterchildren(reversed=True):
138
+ node.addnext(child)
139
+ if (parent_node := node.getparent()) is not None: # always true except for root
140
+ parent_node.remove(node)
@@ -1,31 +0,0 @@
1
- markdown_to_confluence-0.4.4.dist-info/licenses/LICENSE,sha256=Pv43so2bPfmKhmsrmXFyAvS7M30-1i1tzjz6-dfhyOo,1077
2
- md2conf/__init__.py,sha256=nfPrvkkECt_Szpol9kTkli5cL-tFqFzn8JefKlCyb3s,402
3
- md2conf/__main__.py,sha256=RImfFrO2m9C5iebmBrHKlLjosy_A8AY4O7PK9CmiWSw,11120
4
- md2conf/api.py,sha256=-UrtFi3pFOVMsYj7VTgCqHIBinHsSLUucmGSeureEaE,37253
5
- md2conf/application.py,sha256=MBV4Lg6s40Q0Z4SK7W9cSo-k2A-ckmu8wosuwo_2XWM,8301
6
- md2conf/collection.py,sha256=EobgMRJgkYloWlY03NZJ52MRC_SGLpTVCHkltDbQyt0,837
7
- md2conf/converter.py,sha256=WNfxGEacibINQYCZXszaMoUvJIP4Rq8IDMjz5RSH0IY,51162
8
- md2conf/csf.py,sha256=tEbl3e5UQmbRSQK3lQqmpADoy638tlJihutqJoWhumA,4374
9
- md2conf/domain.py,sha256=tA9V0vb5Vo9Nt0eQvwAFARaM9TX88LBVQ73nVvdcaqA,1851
10
- md2conf/drawio.py,sha256=P_t7Wp7Tg9XkZM2ZchWCWWEdBaU1KgZ_YX9ZlkZo4Dk,8293
11
- md2conf/entities.dtd,sha256=M6NzqL5N7dPs_eUA_6sDsiSLzDaAacrx9LdttiufvYU,30215
12
- md2conf/extra.py,sha256=VuMxuOnnC2Qwy6y52ukIxsaYhrZArRqMmRHRE4QZl8g,687
13
- md2conf/local.py,sha256=mUe2QBHhdZesygmYq5kXyGyl_ZmfUECgxDxylQwTa7g,3732
14
- md2conf/markdown.py,sha256=z9sVTA_Hcn551DZgdO7Nbod-IZ-43B4eJLklrYQ2KYI,3141
15
- md2conf/matcher.py,sha256=m5rZjYZSjhKfdeKS8JdPq7cG861Mc6rVZBkrIOZTHGE,6916
16
- md2conf/mermaid.py,sha256=f0x7ISj-41ZMh4zTAFPhIWwr94SDcsVZUc1NWqmH_G4,2508
17
- md2conf/metadata.py,sha256=LzZM-oPNnzCULmLhF516tPlV5zZBknccwMHt8Nan-xg,1007
18
- md2conf/processor.py,sha256=z2d2KMPEYWaxflOtH2UTwrjzpPU8TtLSEUvor85ez1Q,9732
19
- md2conf/properties.py,sha256=RC1jY_TKVbOv2bJxXn27Fj4fNWzyoNUQt6ltgUyVQAQ,3987
20
- md2conf/puppeteer-config.json,sha256=-dMTAN_7kNTGbDlfXzApl0KJpAWna9YKZdwMKbpOb60,159
21
- md2conf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- md2conf/scanner.py,sha256=Cyvjab8tBvKgubttQvNagS8nailuTvFBqUGoiX5MNp8,5351
23
- md2conf/toc.py,sha256=hpqqDbFgNJg5-ul8qWjOglI3Am0sbwR-TLwGN5G9Qo0,2447
24
- md2conf/uri.py,sha256=KbLBdRFtZTQTZd8b4j0LtE8Pb68Ly0WkemF4iW-EAB4,1158
25
- md2conf/xml.py,sha256=ERkGLuJgVFrOkzsMLd1RHU5SGFfFSxxNFZAeKrfdtRs,3454
26
- markdown_to_confluence-0.4.4.dist-info/METADATA,sha256=Ql8vwKX6wtZNtUVoT0z35HgiFQcW-fFWsWFUMLkNErw,32306
27
- markdown_to_confluence-0.4.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
- markdown_to_confluence-0.4.4.dist-info/entry_points.txt,sha256=F1zxa1wtEObtbHS-qp46330WVFLHdMnV2wQ-ZorRmX0,50
29
- markdown_to_confluence-0.4.4.dist-info/top_level.txt,sha256=_FJfl_kHrHNidyjUOuS01ngu_jDsfc-ZjSocNRJnTzU,8
30
- markdown_to_confluence-0.4.4.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
31
- markdown_to_confluence-0.4.4.dist-info/RECORD,,
File without changes