markdown-to-confluence 0.4.7__py3-none-any.whl → 0.5.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.
- {markdown_to_confluence-0.4.7.dist-info → markdown_to_confluence-0.5.0.dist-info}/METADATA +30 -17
- markdown_to_confluence-0.5.0.dist-info/RECORD +35 -0
- md2conf/__init__.py +1 -1
- md2conf/__main__.py +13 -13
- md2conf/api.py +55 -71
- md2conf/collection.py +2 -2
- md2conf/converter.py +43 -35
- md2conf/domain.py +3 -3
- md2conf/drawio.py +1 -1
- md2conf/environment.py +22 -22
- md2conf/latex.py +9 -9
- md2conf/local.py +5 -6
- md2conf/markdown.py +7 -7
- md2conf/matcher.py +5 -5
- md2conf/mermaid.py +3 -3
- md2conf/metadata.py +1 -2
- md2conf/processor.py +12 -12
- md2conf/publisher.py +1 -2
- md2conf/scanner.py +33 -41
- md2conf/serializer.py +52 -0
- md2conf/toc.py +2 -3
- md2conf/xml.py +4 -6
- markdown_to_confluence-0.4.7.dist-info/RECORD +0 -34
- {markdown_to_confluence-0.4.7.dist-info → markdown_to_confluence-0.5.0.dist-info}/WHEEL +0 -0
- {markdown_to_confluence-0.4.7.dist-info → markdown_to_confluence-0.5.0.dist-info}/entry_points.txt +0 -0
- {markdown_to_confluence-0.4.7.dist-info → markdown_to_confluence-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {markdown_to_confluence-0.4.7.dist-info → markdown_to_confluence-0.5.0.dist-info}/top_level.txt +0 -0
- {markdown_to_confluence-0.4.7.dist-info → markdown_to_confluence-0.5.0.dist-info}/zip-safe +0 -0
md2conf/scanner.py
CHANGED
|
@@ -10,25 +10,17 @@ import re
|
|
|
10
10
|
import typing
|
|
11
11
|
from dataclasses import dataclass
|
|
12
12
|
from pathlib import Path
|
|
13
|
-
from typing import Any, Literal,
|
|
13
|
+
from typing import Any, Literal, TypeVar
|
|
14
14
|
|
|
15
15
|
import yaml
|
|
16
|
-
from strong_typing.core import JsonType
|
|
17
|
-
from strong_typing.serialization import DeserializerOptions, json_to_object
|
|
18
16
|
|
|
19
17
|
from .mermaid import MermaidConfigProperties
|
|
18
|
+
from .serializer import JsonType, json_to_object
|
|
20
19
|
|
|
21
20
|
T = TypeVar("T")
|
|
22
21
|
|
|
23
22
|
|
|
24
|
-
def
|
|
25
|
-
typ: type[T],
|
|
26
|
-
data: JsonType,
|
|
27
|
-
) -> T:
|
|
28
|
-
return json_to_object(typ, data, options=DeserializerOptions(skip_unassigned=True))
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def extract_value(pattern: str, text: str) -> tuple[Optional[str], str]:
|
|
23
|
+
def extract_value(pattern: str, text: str) -> tuple[str | None, str]:
|
|
32
24
|
values: list[str] = []
|
|
33
25
|
|
|
34
26
|
def _repl_func(matchobj: re.Match[str]) -> str:
|
|
@@ -40,18 +32,18 @@ def extract_value(pattern: str, text: str) -> tuple[Optional[str], str]:
|
|
|
40
32
|
return value, text
|
|
41
33
|
|
|
42
34
|
|
|
43
|
-
def extract_frontmatter_block(text: str) -> tuple[
|
|
35
|
+
def extract_frontmatter_block(text: str) -> tuple[str | None, str]:
|
|
44
36
|
"Extracts the front-matter from a Markdown document as a blob of unparsed text."
|
|
45
37
|
|
|
46
38
|
return extract_value(r"(?ms)\A---$(.+?)^---$", text)
|
|
47
39
|
|
|
48
40
|
|
|
49
|
-
def extract_frontmatter_properties(text: str) -> tuple[
|
|
41
|
+
def extract_frontmatter_properties(text: str) -> tuple[dict[str, JsonType] | None, str]:
|
|
50
42
|
"Extracts the front-matter from a Markdown document as a dictionary."
|
|
51
43
|
|
|
52
44
|
block, text = extract_frontmatter_block(text)
|
|
53
45
|
|
|
54
|
-
properties:
|
|
46
|
+
properties: dict[str, Any] | None = None
|
|
55
47
|
if block is not None:
|
|
56
48
|
data = yaml.safe_load(block)
|
|
57
49
|
if isinstance(data, dict):
|
|
@@ -77,16 +69,16 @@ class DocumentProperties:
|
|
|
77
69
|
:param alignment: Alignment for block-level images and formulas.
|
|
78
70
|
"""
|
|
79
71
|
|
|
80
|
-
page_id:
|
|
81
|
-
space_key:
|
|
82
|
-
confluence_page_id:
|
|
83
|
-
confluence_space_key:
|
|
84
|
-
generated_by:
|
|
85
|
-
title:
|
|
86
|
-
tags:
|
|
87
|
-
synchronized:
|
|
88
|
-
properties:
|
|
89
|
-
alignment:
|
|
72
|
+
page_id: str | None = None
|
|
73
|
+
space_key: str | None = None
|
|
74
|
+
confluence_page_id: str | None = None
|
|
75
|
+
confluence_space_key: str | None = None
|
|
76
|
+
generated_by: str | None = None
|
|
77
|
+
title: str | None = None
|
|
78
|
+
tags: list[str] | None = None
|
|
79
|
+
synchronized: bool | None = None
|
|
80
|
+
properties: dict[str, JsonType] | None = None
|
|
81
|
+
alignment: Literal["center", "left", "right"] | None = None
|
|
90
82
|
|
|
91
83
|
|
|
92
84
|
@dataclass
|
|
@@ -105,14 +97,14 @@ class ScannedDocument:
|
|
|
105
97
|
:param text: Text that remains after front-matter and inline properties have been extracted.
|
|
106
98
|
"""
|
|
107
99
|
|
|
108
|
-
page_id:
|
|
109
|
-
space_key:
|
|
110
|
-
generated_by:
|
|
111
|
-
title:
|
|
112
|
-
tags:
|
|
113
|
-
synchronized:
|
|
114
|
-
properties:
|
|
115
|
-
alignment:
|
|
100
|
+
page_id: str | None
|
|
101
|
+
space_key: str | None
|
|
102
|
+
generated_by: str | None
|
|
103
|
+
title: str | None
|
|
104
|
+
tags: list[str] | None
|
|
105
|
+
synchronized: bool | None
|
|
106
|
+
properties: dict[str, JsonType] | None
|
|
107
|
+
alignment: Literal["center", "left", "right"] | None
|
|
116
108
|
text: str
|
|
117
109
|
|
|
118
110
|
|
|
@@ -135,16 +127,16 @@ class Scanner:
|
|
|
135
127
|
# extract 'generated-by' tag text
|
|
136
128
|
generated_by, text = extract_value(r"<!--\s+generated[-_]by:\s*(.*)\s+-->", text)
|
|
137
129
|
|
|
138
|
-
title:
|
|
139
|
-
tags:
|
|
140
|
-
synchronized:
|
|
141
|
-
properties:
|
|
142
|
-
alignment:
|
|
130
|
+
title: str | None = None
|
|
131
|
+
tags: list[str] | None = None
|
|
132
|
+
synchronized: bool | None = None
|
|
133
|
+
properties: dict[str, JsonType] | None = None
|
|
134
|
+
alignment: Literal["center", "left", "right"] | None = None
|
|
143
135
|
|
|
144
136
|
# extract front-matter
|
|
145
137
|
data, text = extract_frontmatter_properties(text)
|
|
146
138
|
if data is not None:
|
|
147
|
-
p =
|
|
139
|
+
p = json_to_object(DocumentProperties, data)
|
|
148
140
|
page_id = page_id or p.confluence_page_id or p.page_id
|
|
149
141
|
space_key = space_key or p.confluence_space_key or p.space_key
|
|
150
142
|
generated_by = generated_by or p.generated_by
|
|
@@ -176,8 +168,8 @@ class MermaidProperties:
|
|
|
176
168
|
:param config: Configuration options for rendering.
|
|
177
169
|
"""
|
|
178
170
|
|
|
179
|
-
title:
|
|
180
|
-
config:
|
|
171
|
+
title: str | None = None
|
|
172
|
+
config: MermaidConfigProperties | None = None
|
|
181
173
|
|
|
182
174
|
|
|
183
175
|
class MermaidScanner:
|
|
@@ -203,7 +195,7 @@ class MermaidScanner:
|
|
|
203
195
|
|
|
204
196
|
properties, _ = extract_frontmatter_properties(content)
|
|
205
197
|
if properties is not None:
|
|
206
|
-
front_matter =
|
|
198
|
+
front_matter = json_to_object(MermaidProperties, properties)
|
|
207
199
|
config = front_matter.config or MermaidConfigProperties()
|
|
208
200
|
|
|
209
201
|
return MermaidProperties(title=front_matter.title, config=config)
|
md2conf/serializer.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
from typing import TypeVar
|
|
10
|
+
|
|
11
|
+
from cattrs.preconf.json import make_converter
|
|
12
|
+
|
|
13
|
+
JsonType = None | bool | int | float | str | dict[str, "JsonType"] | list["JsonType"]
|
|
14
|
+
JsonComposite = dict[str, "JsonType"] | list["JsonType"]
|
|
15
|
+
|
|
16
|
+
T = TypeVar("T")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_converter = make_converter(forbid_extra_keys=False)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@_converter.register_structure_hook
|
|
23
|
+
def json_type_structure_hook(value: JsonType, cls: type[JsonType]) -> JsonType:
|
|
24
|
+
return value
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@_converter.register_structure_hook
|
|
28
|
+
def json_composite_structure_hook(value: JsonComposite, cls: type[JsonComposite]) -> JsonComposite:
|
|
29
|
+
return value
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def json_to_object(typ: type[T], data: JsonType) -> T:
|
|
33
|
+
"""
|
|
34
|
+
Converts a raw JSON object to a structured object, validating input data.
|
|
35
|
+
|
|
36
|
+
:param typ: Target structured type.
|
|
37
|
+
:param data: Source data as a JSON object.
|
|
38
|
+
:returns: A valid object instance of the expected type.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
return _converter.structure(data, typ)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def object_to_json_payload(data: object) -> bytes:
|
|
45
|
+
"""
|
|
46
|
+
Converts a structured object to a JSON string encoded in UTF-8.
|
|
47
|
+
|
|
48
|
+
:param data: Object to convert to a JSON string.
|
|
49
|
+
:returns: JSON string encoded in UTF-8.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
return _converter.dumps(data).encode("utf-8")
|
md2conf/toc.py
CHANGED
|
@@ -7,7 +7,6 @@ Copyright 2022-2025, Levente Hunyadi
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
-
from typing import Optional
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
@dataclass(eq=True)
|
|
@@ -24,7 +23,7 @@ class TableOfContentsEntry:
|
|
|
24
23
|
text: str
|
|
25
24
|
children: list["TableOfContentsEntry"]
|
|
26
25
|
|
|
27
|
-
def __init__(self, level: int, text: str, children:
|
|
26
|
+
def __init__(self, level: int, text: str, children: list["TableOfContentsEntry"] | None = None) -> None:
|
|
28
27
|
self.level = level
|
|
29
28
|
self.text = text
|
|
30
29
|
self.children = children or []
|
|
@@ -74,7 +73,7 @@ class TableOfContentsBuilder:
|
|
|
74
73
|
# push new level onto the stack
|
|
75
74
|
self._stack.append(item)
|
|
76
75
|
|
|
77
|
-
def get_title(self) ->
|
|
76
|
+
def get_title(self) -> str | None:
|
|
78
77
|
"""
|
|
79
78
|
Returns a proposed document title.
|
|
80
79
|
|
md2conf/xml.py
CHANGED
|
@@ -6,7 +6,7 @@ Copyright 2022-2025, Levente Hunyadi
|
|
|
6
6
|
:see: https://github.com/hunyadi/md2conf
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
from typing import Iterable
|
|
9
|
+
from typing import Iterable
|
|
10
10
|
|
|
11
11
|
import lxml.etree as ET
|
|
12
12
|
|
|
@@ -39,7 +39,7 @@ class ElementComparator:
|
|
|
39
39
|
skip_attributes: set[str]
|
|
40
40
|
skip_elements: set[str]
|
|
41
41
|
|
|
42
|
-
def __init__(self, *, skip_attributes:
|
|
42
|
+
def __init__(self, *, skip_attributes: Iterable[str] | None = None, skip_elements: Iterable[str] | None = None):
|
|
43
43
|
"""
|
|
44
44
|
Initializes a new element tree comparator.
|
|
45
45
|
|
|
@@ -81,12 +81,10 @@ class ElementComparator:
|
|
|
81
81
|
# compare children recursively
|
|
82
82
|
if len(e1) != len(e2):
|
|
83
83
|
return False
|
|
84
|
-
return all(self.is_equal(c1, c2) for c1, c2 in zip(e1, e2))
|
|
84
|
+
return all(self.is_equal(c1, c2) for c1, c2 in zip(e1, e2, strict=True))
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
def is_xml_equal(
|
|
88
|
-
tree1: ElementType, tree2: ElementType, *, skip_attributes: Optional[Iterable[str]] = None, skip_elements: Optional[Iterable[str]] = None
|
|
89
|
-
) -> bool:
|
|
87
|
+
def is_xml_equal(tree1: ElementType, tree2: ElementType, *, skip_attributes: Iterable[str] | None = None, skip_elements: Iterable[str] | None = None) -> bool:
|
|
90
88
|
"""
|
|
91
89
|
Compare two XML documents for equivalence, ignoring leading/trailing whitespace differences and attribute definition order.
|
|
92
90
|
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
markdown_to_confluence-0.4.7.dist-info/licenses/LICENSE,sha256=56L-Y0dyZwyVlINRJRz3PNw-ka-oLVaAq-7d8zo6qlc,1077
|
|
2
|
-
md2conf/__init__.py,sha256=3R4-8WktKk6Q4JGEjAHqEU8S4u_w39RdcpBl-yL-0Tc,402
|
|
3
|
-
md2conf/__main__.py,sha256=pp3Zi60gmhXRqK0uoR4lZMVLlOO8ryAmk8UMPiI-Cew,11527
|
|
4
|
-
md2conf/api.py,sha256=uEPMcR4B-07MMFM96m92z7Znnj7pzhkLLAGcOIseERY,41398
|
|
5
|
-
md2conf/collection.py,sha256=EobgMRJgkYloWlY03NZJ52MRC_SGLpTVCHkltDbQyt0,837
|
|
6
|
-
md2conf/converter.py,sha256=wghwaa_07cN-xXY1DIOJgy6VJjfnJWDjbT4aB5NAL08,68845
|
|
7
|
-
md2conf/csf.py,sha256=rugs3qC2aJQCJSTczeBw9WhqSZZtMq14LjwT0V1b6Hc,6476
|
|
8
|
-
md2conf/domain.py,sha256=rN6QSuoP3JMj-WE8BAggHqHEO8nJbnQDf0b0uhStNZk,2221
|
|
9
|
-
md2conf/drawio.py,sha256=0FoCl0BWxyxO-KLPlDZPyYvSTRpgu9Z6yFKl10TGGMI,8594
|
|
10
|
-
md2conf/emoticon.py,sha256=P2L5oQvnRXeVifJQ3sJ2Ck-6ptbxumq2vsT-rM0W0Ms,484
|
|
11
|
-
md2conf/entities.dtd,sha256=M6NzqL5N7dPs_eUA_6sDsiSLzDaAacrx9LdttiufvYU,30215
|
|
12
|
-
md2conf/environment.py,sha256=RC1jY_TKVbOv2bJxXn27Fj4fNWzyoNUQt6ltgUyVQAQ,3987
|
|
13
|
-
md2conf/extra.py,sha256=VuMxuOnnC2Qwy6y52ukIxsaYhrZArRqMmRHRE4QZl8g,687
|
|
14
|
-
md2conf/latex.py,sha256=i5C_ZqsBG_Df1bNoMJpAKpiYMFcMEn5aoAv6bdSsE1k,7674
|
|
15
|
-
md2conf/local.py,sha256=mvp2kA_eo6JUQ_rlM7zDdEFgBPVxMr3VKP_X1nsLjHE,3747
|
|
16
|
-
md2conf/markdown.py,sha256=czabU17tUfhSX1JQGiI_TrMrTmtoVThOwFu_To_Oi_w,3176
|
|
17
|
-
md2conf/matcher.py,sha256=8g2yiKXfEkYJPIvKD2cswTG9BxFY24BtVcPgKaEVAs8,6812
|
|
18
|
-
md2conf/mermaid.py,sha256=hGrITJVvhHprjQVoezQ1nQeo6a_lqNihF8L-oJ4t5rc,2633
|
|
19
|
-
md2conf/metadata.py,sha256=LzZM-oPNnzCULmLhF516tPlV5zZBknccwMHt8Nan-xg,1007
|
|
20
|
-
md2conf/processor.py,sha256=G1icOxYsPAOjK4fnVdA-vwTsRiZMBpEAmMPOwfOebtQ,9733
|
|
21
|
-
md2conf/publisher.py,sha256=V4TzmrJ5LAv61EPpDYUGWv_6KHb-BcDjtYdByNt6su8,8665
|
|
22
|
-
md2conf/puppeteer-config.json,sha256=-dMTAN_7kNTGbDlfXzApl0KJpAWna9YKZdwMKbpOb60,159
|
|
23
|
-
md2conf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
md2conf/scanner.py,sha256=O4iOgmSqTe1CYgmdaiF1QyAESzA4SRNEKVY9gOodXDY,7035
|
|
25
|
-
md2conf/text.py,sha256=fHOrUaPXAjE4iRhHqFq-CiI-knpo4wvyHCWp0crewqA,1736
|
|
26
|
-
md2conf/toc.py,sha256=hpqqDbFgNJg5-ul8qWjOglI3Am0sbwR-TLwGN5G9Qo0,2447
|
|
27
|
-
md2conf/uri.py,sha256=KbLBdRFtZTQTZd8b4j0LtE8Pb68Ly0WkemF4iW-EAB4,1158
|
|
28
|
-
md2conf/xml.py,sha256=SpFfcfZm1BPbB4zZM2UC1K-GkzHRhFiotiPMPt5_XPI,5541
|
|
29
|
-
markdown_to_confluence-0.4.7.dist-info/METADATA,sha256=iHeyUxkIuoI9_g9dvCLlxC4BCqnH3JStloGAfP-bWsY,34628
|
|
30
|
-
markdown_to_confluence-0.4.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
31
|
-
markdown_to_confluence-0.4.7.dist-info/entry_points.txt,sha256=F1zxa1wtEObtbHS-qp46330WVFLHdMnV2wQ-ZorRmX0,50
|
|
32
|
-
markdown_to_confluence-0.4.7.dist-info/top_level.txt,sha256=_FJfl_kHrHNidyjUOuS01ngu_jDsfc-ZjSocNRJnTzU,8
|
|
33
|
-
markdown_to_confluence-0.4.7.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
34
|
-
markdown_to_confluence-0.4.7.dist-info/RECORD,,
|
|
File without changes
|
{markdown_to_confluence-0.4.7.dist-info → markdown_to_confluence-0.5.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{markdown_to_confluence-0.4.7.dist-info → markdown_to_confluence-0.5.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{markdown_to_confluence-0.4.7.dist-info → markdown_to_confluence-0.5.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|