markdown-to-confluence 0.2.7__py3-none-any.whl → 0.3.1__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/application.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Publish Markdown files to Confluence wiki.
3
3
 
4
- Copyright 2022-2024, Levente Hunyadi
4
+ Copyright 2022-2025, Levente Hunyadi
5
5
 
6
6
  :see: https://github.com/hunyadi/md2conf
7
7
  """
@@ -9,7 +9,7 @@ Copyright 2022-2024, Levente Hunyadi
9
9
  import logging
10
10
  import os.path
11
11
  from pathlib import Path
12
- from typing import Dict, List, Optional
12
+ from typing import Optional
13
13
 
14
14
  from .api import ConfluencePage, ConfluenceSession
15
15
  from .converter import (
@@ -77,7 +77,7 @@ class Application:
77
77
  LOGGER.info("Synchronizing directory: %s", local_dir)
78
78
 
79
79
  # Step 1: build index of all page metadata
80
- page_metadata: Dict[Path, ConfluencePageMetadata] = {}
80
+ page_metadata: dict[Path, ConfluencePageMetadata] = {}
81
81
  root_id = (
82
82
  ConfluenceQualifiedID(self.options.root_page_id, self.api.space_key)
83
83
  if self.options.root_page_id
@@ -94,24 +94,19 @@ class Application:
94
94
  self,
95
95
  page_path: Path,
96
96
  root_dir: Path,
97
- page_metadata: Dict[Path, ConfluencePageMetadata],
97
+ page_metadata: dict[Path, ConfluencePageMetadata],
98
98
  ) -> None:
99
99
  base_path = page_path.parent
100
100
 
101
101
  LOGGER.info("Synchronizing page: %s", page_path)
102
102
  document = ConfluenceDocument(page_path, self.options, root_dir, page_metadata)
103
-
104
- if document.id.space_key:
105
- with self.api.switch_space(document.id.space_key):
106
- self._update_document(document, base_path)
107
- else:
108
- self._update_document(document, base_path)
103
+ self._update_document(document, base_path)
109
104
 
110
105
  def _index_directory(
111
106
  self,
112
107
  local_dir: Path,
113
108
  root_id: Optional[ConfluenceQualifiedID],
114
- page_metadata: Dict[Path, ConfluencePageMetadata],
109
+ page_metadata: dict[Path, ConfluencePageMetadata],
115
110
  ) -> None:
116
111
  "Indexes Markdown files in a directory recursively."
117
112
 
@@ -119,8 +114,8 @@ class Application:
119
114
 
120
115
  matcher = Matcher(MatcherOptions(source=".mdignore", extension="md"), local_dir)
121
116
 
122
- files: List[Path] = []
123
- directories: List[Path] = []
117
+ files: list[Path] = []
118
+ directories: list[Path] = []
124
119
  for entry in os.scandir(local_dir):
125
120
  if matcher.is_excluded(entry.name, entry.is_dir()):
126
121
  continue
@@ -136,6 +131,15 @@ class Application:
136
131
  parent_doc = Path(local_dir) / "index.md"
137
132
  elif (Path(local_dir) / "README.md") in files:
138
133
  parent_doc = Path(local_dir) / "README.md"
134
+ elif (Path(local_dir) / f"{local_dir.name}.md") in files:
135
+ parent_doc = Path(local_dir) / f"{local_dir.name}.md"
136
+
137
+ if parent_doc is None and self.options.keep_hierarchy:
138
+ parent_doc = Path(local_dir) / "index.md"
139
+
140
+ # create a blank page in Confluence for the directory entry
141
+ with open(parent_doc, "w"):
142
+ pass
139
143
 
140
144
  if parent_doc is not None:
141
145
  files.remove(parent_doc)
@@ -175,9 +179,7 @@ class Application:
175
179
  frontmatter_title, _ = extract_frontmatter_title(document)
176
180
 
177
181
  if qualified_id is not None:
178
- confluence_page = self.api.get_page(
179
- qualified_id.page_id, space_key=qualified_id.space_key
180
- )
182
+ confluence_page = self.api.get_page(qualified_id.page_id)
181
183
  else:
182
184
  if parent_id is None:
183
185
  raise ValueError(
@@ -189,11 +191,17 @@ class Application:
189
191
  absolute_path, document, title or frontmatter_title, parent_id
190
192
  )
191
193
 
194
+ space_key = (
195
+ self.api.space_id_to_key(confluence_page.space_id)
196
+ if confluence_page.space_id
197
+ else self.api.space_key
198
+ )
199
+
192
200
  return ConfluencePageMetadata(
193
201
  domain=self.api.domain,
194
202
  base_path=self.api.base_path,
195
203
  page_id=confluence_page.id,
196
- space_key=confluence_page.space_key or self.api.space_key,
204
+ space_key=space_key,
197
205
  title=confluence_page.title or "",
198
206
  )
199
207
 
@@ -217,7 +225,7 @@ class Application:
217
225
  absolute_path,
218
226
  document,
219
227
  confluence_page.id,
220
- confluence_page.space_key,
228
+ self.api.space_id_to_key(confluence_page.space_id),
221
229
  )
222
230
  return confluence_page
223
231
 
@@ -251,7 +259,7 @@ class Application:
251
259
  ) -> None:
252
260
  "Writes the Confluence page ID and space key at the beginning of the Markdown file."
253
261
 
254
- content: List[str] = []
262
+ content: list[str] = []
255
263
 
256
264
  # check if the file has frontmatter
257
265
  index = 0
md2conf/converter.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Publish Markdown files to Confluence wiki.
3
3
 
4
- Copyright 2022-2024, Levente Hunyadi
4
+ Copyright 2022-2025, Levente Hunyadi
5
5
 
6
6
  :see: https://github.com/hunyadi/md2conf
7
7
  """
@@ -13,12 +13,11 @@ import importlib.resources as resources
13
13
  import logging
14
14
  import os.path
15
15
  import re
16
- import sys
17
16
  import uuid
18
17
  import xml.etree.ElementTree
19
18
  from dataclasses import dataclass
20
19
  from pathlib import Path
21
- from typing import Any, Dict, List, Literal, Optional, Tuple, Union
20
+ from typing import Any, Literal, Optional, Union
22
21
  from urllib.parse import ParseResult, urlparse, urlunparse
23
22
 
24
23
  import lxml.etree as ET
@@ -46,7 +45,7 @@ class ParseError(RuntimeError):
46
45
  pass
47
46
 
48
47
 
49
- def starts_with_any(text: str, prefixes: List[str]) -> bool:
48
+ def starts_with_any(text: str, prefixes: list[str]) -> bool:
50
49
  "True if text starts with any of the listed prefixes."
51
50
 
52
51
  for prefix in prefixes:
@@ -73,7 +72,7 @@ def emoji_generator(
73
72
  alt: str,
74
73
  title: Optional[str],
75
74
  category: Optional[str],
76
- options: Dict[str, Any],
75
+ options: dict[str, Any],
77
76
  md: markdown.Markdown,
78
77
  ) -> xml.etree.ElementTree.Element:
79
78
  name = (alias or shortname).strip(":")
@@ -107,7 +106,7 @@ def markdown_to_html(content: str) -> str:
107
106
  )
108
107
 
109
108
 
110
- def _elements_from_strings(dtd_path: Path, items: List[str]) -> ET._Element:
109
+ def _elements_from_strings(dtd_path: Path, items: list[str]) -> ET._Element:
111
110
  """
112
111
  Creates a fragment of several XML nodes from their string representation wrapped in a root element.
113
112
 
@@ -129,7 +128,7 @@ def _elements_from_strings(dtd_path: Path, items: List[str]) -> ET._Element:
129
128
 
130
129
  data = [
131
130
  '<?xml version="1.0"?>',
132
- f'<!DOCTYPE ac:confluence PUBLIC "-//Atlassian//Confluence 4 Page//EN" "{dtd_path}">'
131
+ f'<!DOCTYPE ac:confluence PUBLIC "-//Atlassian//Confluence 4 Page//EN" "{dtd_path.as_posix()}">'
133
132
  f"<root{ns_attr_list}>",
134
133
  ]
135
134
  data.extend(items)
@@ -141,16 +140,12 @@ def _elements_from_strings(dtd_path: Path, items: List[str]) -> ET._Element:
141
140
  raise ParseError(e)
142
141
 
143
142
 
144
- def elements_from_strings(items: List[str]) -> ET._Element:
143
+ def elements_from_strings(items: list[str]) -> ET._Element:
145
144
  "Creates a fragment of several XML nodes from their string representation wrapped in a root element."
146
145
 
147
- if sys.version_info >= (3, 9):
148
- resource_path = resources.files(__package__).joinpath("entities.dtd")
149
- with resources.as_file(resource_path) as dtd_path:
150
- return _elements_from_strings(dtd_path, items)
151
- else:
152
- with resources.path(__package__, "entities.dtd") as dtd_path:
153
- return _elements_from_strings(dtd_path, items)
146
+ resource_path = resources.files(__package__).joinpath("entities.dtd")
147
+ with resources.as_file(resource_path) as dtd_path:
148
+ return _elements_from_strings(dtd_path, items)
154
149
 
155
150
 
156
151
  def elements_from_string(content: str) -> ET._Element:
@@ -244,7 +239,7 @@ class ConfluencePageMetadata:
244
239
  domain: str
245
240
  base_path: str
246
241
  page_id: str
247
- space_key: str
242
+ space_key: Optional[str]
248
243
  title: str
249
244
 
250
245
 
@@ -287,7 +282,7 @@ class ConfluenceConverterOptions:
287
282
  conversion rules for the identifier.
288
283
  :param render_mermaid: Whether to pre-render Mermaid diagrams into PNG/SVG images.
289
284
  :param diagram_output_format: Target image format for diagrams.
290
- :param web_links: When true, convert relative URLs to Confluence Web UI links.
285
+ :param webui_links: When true, convert relative URLs to Confluence Web UI links.
291
286
  """
292
287
 
293
288
  ignore_invalid_url: bool = False
@@ -304,17 +299,17 @@ class ConfluenceStorageFormatConverter(NodeVisitor):
304
299
  path: Path
305
300
  base_dir: Path
306
301
  root_dir: Path
307
- links: List[str]
308
- images: List[Path]
309
- embedded_images: Dict[str, bytes]
310
- page_metadata: Dict[Path, ConfluencePageMetadata]
302
+ links: list[str]
303
+ images: list[Path]
304
+ embedded_images: dict[str, bytes]
305
+ page_metadata: dict[Path, ConfluencePageMetadata]
311
306
 
312
307
  def __init__(
313
308
  self,
314
309
  options: ConfluenceConverterOptions,
315
310
  path: Path,
316
311
  root_dir: Path,
317
- page_metadata: Dict[Path, ConfluencePageMetadata],
312
+ page_metadata: dict[Path, ConfluencePageMetadata],
318
313
  ) -> None:
319
314
  super().__init__()
320
315
  self.options = options
@@ -438,7 +433,7 @@ class ConfluenceStorageFormatConverter(NodeVisitor):
438
433
  if not src:
439
434
  raise DocumentError("image lacks `src` attribute")
440
435
 
441
- attributes: Dict[str, Any] = {
436
+ attributes: dict[str, Any] = {
442
437
  ET.QName(namespaces["ac"], "align"): "center",
443
438
  ET.QName(namespaces["ac"], "layout"): "center",
444
439
  }
@@ -457,11 +452,11 @@ class ConfluenceStorageFormatConverter(NodeVisitor):
457
452
  return self._transform_attached_image(Path(src), caption, attributes)
458
453
 
459
454
  def _transform_external_image(
460
- self, url: str, caption: Optional[str], attributes: Dict[str, Any]
455
+ self, url: str, caption: Optional[str], attributes: dict[str, Any]
461
456
  ) -> ET._Element:
462
457
  "Emits Confluence Storage Format XHTML for an external image."
463
458
 
464
- elements: List[ET._Element] = []
459
+ elements: list[ET._Element] = []
465
460
  elements.append(
466
461
  RI(
467
462
  "url",
@@ -475,7 +470,7 @@ class ConfluenceStorageFormatConverter(NodeVisitor):
475
470
  return AC("image", attributes, *elements)
476
471
 
477
472
  def _transform_attached_image(
478
- self, path: Path, caption: Optional[str], attributes: Dict[str, Any]
473
+ self, path: Path, caption: Optional[str], attributes: dict[str, Any]
479
474
  ) -> ET._Element:
480
475
  "Emits Confluence Storage Format XHTML for an attached image."
481
476
 
@@ -487,7 +482,7 @@ class ConfluenceStorageFormatConverter(NodeVisitor):
487
482
  self.images.append(path)
488
483
  image_name = attachment_name(path)
489
484
 
490
- elements: List[ET._Element] = []
485
+ elements: list[ET._Element] = []
491
486
  elements.append(
492
487
  RI(
493
488
  "attachment",
@@ -525,7 +520,7 @@ class ConfluenceStorageFormatConverter(NodeVisitor):
525
520
  AC(
526
521
  "parameter",
527
522
  {ET.QName(namespaces["ac"], "name"): "theme"},
528
- "Midnight",
523
+ "Default",
529
524
  ),
530
525
  AC(
531
526
  "parameter",
@@ -899,8 +894,8 @@ class DocumentError(RuntimeError):
899
894
  pass
900
895
 
901
896
 
902
- def extract_value(pattern: str, text: str) -> Tuple[Optional[str], str]:
903
- values: List[str] = []
897
+ def extract_value(pattern: str, text: str) -> tuple[Optional[str], str]:
898
+ values: list[str] = []
904
899
 
905
900
  def _repl_func(matchobj: re.Match) -> str:
906
901
  values.append(matchobj.group(1))
@@ -921,7 +916,7 @@ class ConfluenceQualifiedID:
921
916
  self.space_key = space_key
922
917
 
923
918
 
924
- def extract_qualified_id(text: str) -> Tuple[Optional[ConfluenceQualifiedID], str]:
919
+ def extract_qualified_id(text: str) -> tuple[Optional[ConfluenceQualifiedID], str]:
925
920
  "Extracts the Confluence page ID and space key from a Markdown document."
926
921
 
927
922
  page_id, text = extract_value(r"<!--\s+confluence-page-id:\s*(\d+)\s+-->", text)
@@ -935,13 +930,13 @@ def extract_qualified_id(text: str) -> Tuple[Optional[ConfluenceQualifiedID], st
935
930
  return ConfluenceQualifiedID(page_id, space_key), text
936
931
 
937
932
 
938
- def extract_frontmatter(text: str) -> Tuple[Optional[str], str]:
933
+ def extract_frontmatter(text: str) -> tuple[Optional[str], str]:
939
934
  "Extracts the front matter from a Markdown document."
940
935
 
941
936
  return extract_value(r"(?ms)\A---$(.+?)^---$", text)
942
937
 
943
938
 
944
- def extract_frontmatter_title(text: str) -> Tuple[Optional[str], str]:
939
+ def extract_frontmatter_title(text: str) -> tuple[Optional[str], str]:
945
940
  frontmatter, text = extract_frontmatter(text)
946
941
 
947
942
  title: Optional[str] = None
@@ -974,8 +969,9 @@ class ConfluenceDocumentOptions:
974
969
  plain text; when false, raise an exception.
975
970
  :param heading_anchors: When true, emit a structured macro *anchor* for each section heading using GitHub
976
971
  conversion rules for the identifier.
977
- :param generated_by: Text to use as the generated-by prompt.
978
- :param show_generated: Whether to display a prompt "This page has been generated with a tool."
972
+ :param generated_by: Text to use as the generated-by prompt (or `None` to omit a prompt).
973
+ :param root_page_id: Confluence page to assume root page role for publishing a directory of Markdown files.
974
+ :param keep_hierarchy: Whether to maintain source directory structure when exporting to Confluence.
979
975
  :param render_mermaid: Whether to pre-render Mermaid diagrams into PNG/SVG images.
980
976
  :param diagram_output_format: Target image format for diagrams.
981
977
  :param webui_links: When true, convert relative URLs to Confluence Web UI links.
@@ -985,6 +981,7 @@ class ConfluenceDocumentOptions:
985
981
  heading_anchors: bool = False
986
982
  generated_by: Optional[str] = "This page has been generated with a tool."
987
983
  root_page_id: Optional[str] = None
984
+ keep_hierarchy: bool = False
988
985
  render_mermaid: bool = False
989
986
  diagram_output_format: Literal["png", "svg"] = "png"
990
987
  webui_links: bool = False
@@ -993,8 +990,8 @@ class ConfluenceDocumentOptions:
993
990
  class ConfluenceDocument:
994
991
  id: ConfluenceQualifiedID
995
992
  title: Optional[str]
996
- links: List[str]
997
- images: List[Path]
993
+ links: list[str]
994
+ images: list[Path]
998
995
 
999
996
  options: ConfluenceDocumentOptions
1000
997
  root: ET._Element
@@ -1004,7 +1001,7 @@ class ConfluenceDocument:
1004
1001
  path: Path,
1005
1002
  options: ConfluenceDocumentOptions,
1006
1003
  root_dir: Path,
1007
- page_metadata: Dict[Path, ConfluencePageMetadata],
1004
+ page_metadata: dict[Path, ConfluencePageMetadata],
1008
1005
  ) -> None:
1009
1006
  self.options = options
1010
1007
  path = path.resolve(True)
@@ -1132,10 +1129,6 @@ def _content_to_string(dtd_path: Path, content: str) -> str:
1132
1129
  def content_to_string(content: str) -> str:
1133
1130
  "Converts a Confluence Storage Format document returned by the API into a readable XML document."
1134
1131
 
1135
- if sys.version_info >= (3, 9):
1136
- resource_path = resources.files(__package__).joinpath("entities.dtd")
1137
- with resources.as_file(resource_path) as dtd_path:
1138
- return _content_to_string(dtd_path, content)
1139
- else:
1140
- with resources.path(__package__, "entities.dtd") as dtd_path:
1141
- return _content_to_string(dtd_path, content)
1132
+ resource_path = resources.files(__package__).joinpath("entities.dtd")
1133
+ with resources.as_file(resource_path) as dtd_path:
1134
+ return _content_to_string(dtd_path, content)
md2conf/emoji.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Publish Markdown files to Confluence wiki.
3
3
 
4
- Copyright 2022-2024, Levente Hunyadi
4
+ Copyright 2022-2025, Levente Hunyadi
5
5
 
6
6
  :see: https://github.com/hunyadi/md2conf
7
7
  """
md2conf/matcher.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Publish Markdown files to Confluence wiki.
3
3
 
4
- Copyright 2022-2024, Levente Hunyadi
4
+ Copyright 2022-2025, Levente Hunyadi
5
5
 
6
6
  :see: https://github.com/hunyadi/md2conf
7
7
  """
@@ -10,12 +10,17 @@ import os.path
10
10
  from dataclasses import dataclass
11
11
  from fnmatch import fnmatch
12
12
  from pathlib import Path
13
- from typing import Iterable, List, Optional
13
+ from typing import Iterable, Optional
14
14
 
15
15
 
16
16
  @dataclass
17
17
  class Entry:
18
- "Represents a file or directory entry."
18
+ """
19
+ Represents a file or directory entry.
20
+
21
+ :param name: Name of the file-system entry.
22
+ :param is_dir: True if the entry is a directory.
23
+ """
19
24
 
20
25
  name: str
21
26
  is_dir: bool
@@ -42,7 +47,7 @@ class Matcher:
42
47
  "Compares file and directory names against a list of exclude/include patterns."
43
48
 
44
49
  options: MatcherOptions
45
- rules: List[str]
50
+ rules: list[str]
46
51
 
47
52
  def __init__(self, options: MatcherOptions, directory: Path) -> None:
48
53
  self.options = options
@@ -92,7 +97,7 @@ class Matcher:
92
97
 
93
98
  return not self.is_excluded(name, is_dir)
94
99
 
95
- def filter(self, items: Iterable[Entry]) -> List[Entry]:
100
+ def filter(self, items: Iterable[Entry]) -> list[Entry]:
96
101
  """
97
102
  Returns only those elements from the input that don't match any of the exclusion rules.
98
103
 
@@ -102,7 +107,7 @@ class Matcher:
102
107
 
103
108
  return [item for item in items if self.is_included(item.name, item.is_dir)]
104
109
 
105
- def scandir(self, path: Path) -> List[Entry]:
110
+ def scandir(self, path: Path) -> list[Entry]:
106
111
  """
107
112
  Returns only those entries in a directory whose name doesn't match any of the exclusion rules.
108
113
 
md2conf/mermaid.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Publish Markdown files to Confluence wiki.
3
3
 
4
- Copyright 2022-2024, Levente Hunyadi
4
+ Copyright 2022-2025, Levente Hunyadi
5
5
 
6
6
  :see: https://github.com/hunyadi/md2conf
7
7
  """
@@ -29,7 +29,11 @@ def get_mmdc() -> str:
29
29
  "Path to the Mermaid diagram converter."
30
30
 
31
31
  if is_docker():
32
- return "/home/md2conf/node_modules/.bin/mmdc"
32
+ full_path = "/home/md2conf/node_modules/.bin/mmdc"
33
+ if os.path.exists(full_path):
34
+ return full_path
35
+ else:
36
+ return "mmdc"
33
37
  elif os.name == "nt":
34
38
  return "mmdc.cmd"
35
39
  else:
md2conf/processor.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Publish Markdown files to Confluence wiki.
3
3
 
4
- Copyright 2022-2024, Levente Hunyadi
4
+ Copyright 2022-2025, Levente Hunyadi
5
5
 
6
6
  :see: https://github.com/hunyadi/md2conf
7
7
  """
@@ -10,7 +10,7 @@ import hashlib
10
10
  import logging
11
11
  import os
12
12
  from pathlib import Path
13
- from typing import Dict, List, Optional
13
+ from typing import Optional
14
14
 
15
15
  from .converter import (
16
16
  ConfluenceDocument,
@@ -60,7 +60,7 @@ class Processor:
60
60
  LOGGER.info("Synchronizing directory: %s", local_dir)
61
61
 
62
62
  # Step 1: build index of all page metadata
63
- page_metadata: Dict[Path, ConfluencePageMetadata] = {}
63
+ page_metadata: dict[Path, ConfluencePageMetadata] = {}
64
64
  self._index_directory(local_dir, page_metadata)
65
65
  LOGGER.info("Indexed %d page(s)", len(page_metadata))
66
66
 
@@ -83,7 +83,7 @@ class Processor:
83
83
  self,
84
84
  path: Path,
85
85
  root_dir: Path,
86
- page_metadata: Dict[Path, ConfluencePageMetadata],
86
+ page_metadata: dict[Path, ConfluencePageMetadata],
87
87
  ) -> None:
88
88
  "Processes a single Markdown file."
89
89
 
@@ -95,7 +95,7 @@ class Processor:
95
95
  def _index_directory(
96
96
  self,
97
97
  local_dir: Path,
98
- page_metadata: Dict[Path, ConfluencePageMetadata],
98
+ page_metadata: dict[Path, ConfluencePageMetadata],
99
99
  ) -> None:
100
100
  "Indexes Markdown files in a directory recursively."
101
101
 
@@ -103,8 +103,8 @@ class Processor:
103
103
 
104
104
  matcher = Matcher(MatcherOptions(source=".mdignore", extension="md"), local_dir)
105
105
 
106
- files: List[Path] = []
107
- directories: List[Path] = []
106
+ files: list[Path] = []
107
+ directories: list[Path] = []
108
108
  for entry in os.scandir(local_dir):
109
109
  if matcher.is_excluded(entry.name, entry.is_dir()):
110
110
  continue
md2conf/properties.py CHANGED
@@ -1,13 +1,13 @@
1
1
  """
2
2
  Publish Markdown files to Confluence wiki.
3
3
 
4
- Copyright 2022-2024, Levente Hunyadi
4
+ Copyright 2022-2025, Levente Hunyadi
5
5
 
6
6
  :see: https://github.com/hunyadi/md2conf
7
7
  """
8
8
 
9
9
  import os
10
- from typing import Dict, Optional
10
+ from typing import Optional
11
11
 
12
12
 
13
13
  class ConfluenceError(RuntimeError):
@@ -17,10 +17,10 @@ class ConfluenceError(RuntimeError):
17
17
  class ConfluenceProperties:
18
18
  domain: str
19
19
  base_path: str
20
- space_key: str
20
+ space_key: Optional[str]
21
21
  user_name: Optional[str]
22
22
  api_key: str
23
- headers: Optional[Dict[str, str]]
23
+ headers: Optional[dict[str, str]]
24
24
 
25
25
  def __init__(
26
26
  self,
@@ -29,7 +29,7 @@ class ConfluenceProperties:
29
29
  user_name: Optional[str] = None,
30
30
  api_key: Optional[str] = None,
31
31
  space_key: Optional[str] = None,
32
- headers: Optional[Dict[str, str]] = None,
32
+ headers: Optional[dict[str, str]] = None,
33
33
  ) -> None:
34
34
  opt_domain = domain or os.getenv("CONFLUENCE_DOMAIN")
35
35
  opt_base_path = base_path or os.getenv("CONFLUENCE_PATH")
@@ -43,8 +43,6 @@ class ConfluenceProperties:
43
43
  opt_base_path = "/wiki/"
44
44
  if not opt_api_key:
45
45
  raise ConfluenceError("Confluence API key not specified")
46
- if not opt_space_key:
47
- raise ConfluenceError("Confluence space key not specified")
48
46
 
49
47
  if opt_domain.startswith(("http://", "https://")) or opt_domain.endswith("/"):
50
48
  raise ConfluenceError(
@@ -1,21 +0,0 @@
1
- md2conf/__init__.py,sha256=U8zdop7-AIrfwCYzWiwKfhCEPF_1QEKPt4Zwq-38LlU,402
2
- md2conf/__main__.py,sha256=6iOI28W_d71tlnCMFpZwvkBmBt5-HazlZsz69gS4Oak,6894
3
- md2conf/api.py,sha256=NmAbNWTrTSi2ZDGYymy70Fw6HcgrmB-Ua4re4yLJvVc,17715
4
- md2conf/application.py,sha256=-kFpMRtSpQUU1hsiW5O73gL1X9McQWpvyAAEUxEnpuU,8869
5
- md2conf/converter.py,sha256=S8Kka35Y99w0J00CYi-DQwsKzlHAvBfaSCf10mb1FZk,36596
6
- md2conf/emoji.py,sha256=w9oiOIxzObAE7HTo3f6aETT1_D3t3yZwr88ynU4ENm0,1924
7
- md2conf/entities.dtd,sha256=M6NzqL5N7dPs_eUA_6sDsiSLzDaAacrx9LdttiufvYU,30215
8
- md2conf/matcher.py,sha256=mYMltZOLypK4O-SJugLgicOwUMem67hiNLg_kPFoJkU,3583
9
- md2conf/mermaid.py,sha256=gqA6Hg6WcPDdR7JOClezAgNZj2Gq4pXJSgmOUlUt6Dk,2192
10
- md2conf/processor.py,sha256=E-Na-a8tNp4CaoRPA5etcXdHXNRdgyMrf6bfKa9P7O4,4781
11
- md2conf/properties.py,sha256=iVIc0h0XtS3Y2LCywX1C9cvmVQ0WljOMt8pl2MDMVCI,1990
12
- md2conf/puppeteer-config.json,sha256=-dMTAN_7kNTGbDlfXzApl0KJpAWna9YKZdwMKbpOb60,159
13
- md2conf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- md2conf/util.py,sha256=ftf60MiW7S7rW45ipWX6efP_Sv2F2qpyIDHrGA0cBiw,743
15
- markdown_to_confluence-0.2.7.dist-info/LICENSE,sha256=Pv43so2bPfmKhmsrmXFyAvS7M30-1i1tzjz6-dfhyOo,1077
16
- markdown_to_confluence-0.2.7.dist-info/METADATA,sha256=76K_O_5b__MnKT7FuLXgCHX6hR5dZio3mK6RWR4DyCA,13551
17
- markdown_to_confluence-0.2.7.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
18
- markdown_to_confluence-0.2.7.dist-info/entry_points.txt,sha256=F1zxa1wtEObtbHS-qp46330WVFLHdMnV2wQ-ZorRmX0,50
19
- markdown_to_confluence-0.2.7.dist-info/top_level.txt,sha256=_FJfl_kHrHNidyjUOuS01ngu_jDsfc-ZjSocNRJnTzU,8
20
- markdown_to_confluence-0.2.7.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
21
- markdown_to_confluence-0.2.7.dist-info/RECORD,,
md2conf/util.py DELETED
@@ -1,27 +0,0 @@
1
- """
2
- Publish Markdown files to Confluence wiki.
3
-
4
- Copyright 2022-2024, Levente Hunyadi
5
-
6
- :see: https://github.com/hunyadi/md2conf
7
- """
8
-
9
- import sys
10
-
11
- if sys.version_info >= (3, 9):
12
-
13
- def removeprefix(string: str, prefix: str) -> str:
14
- "If the string starts with the prefix, return the string without the prefix; otherwise, return the original string."
15
-
16
- return string.removeprefix(prefix)
17
-
18
- else:
19
-
20
- def removeprefix(string: str, prefix: str) -> str:
21
- "If the string starts with the prefix, return the string without the prefix; otherwise, return the original string."
22
-
23
- if string.startswith(prefix):
24
- prefix_len = len(prefix)
25
- return string[prefix_len:]
26
- else:
27
- return string