mkdocs2confluence 0.9.5__tar.gz → 0.9.6__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 (87) hide show
  1. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/PKG-INFO +1 -1
  2. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/pyproject.toml +1 -1
  3. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs2confluence.egg-info/PKG-INFO +1 -1
  4. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/transforms/mermaid.py +10 -9
  5. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_mermaid.py +50 -5
  6. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/LICENSE +0 -0
  7. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/README.md +0 -0
  8. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/setup.cfg +0 -0
  9. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs2confluence.egg-info/SOURCES.txt +0 -0
  10. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
  11. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
  12. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
  13. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
  14. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/__init__.py +0 -0
  15. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/cli.py +0 -0
  16. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
  17. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/emitter/xhtml.py +0 -0
  18. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
  19. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/ir/document.py +0 -0
  20. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/ir/nodes.py +0 -0
  21. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
  22. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
  23. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/loader/config.py +0 -0
  24. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
  25. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/loader/nav.py +0 -0
  26. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/loader/page.py +0 -0
  27. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
  28. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
  29. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
  30. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
  31. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/pdf/render.py +0 -0
  32. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
  33. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
  34. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
  35. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
  36. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
  37. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
  38. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
  39. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
  40. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/preview/render.py +0 -0
  41. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/preview/server.py +0 -0
  42. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
  43. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/publisher/client.py +0 -0
  44. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/publisher/pipeline.py +0 -0
  45. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/sync/__init__.py +0 -0
  46. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/sync/anchoring.py +0 -0
  47. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/sync/command.py +0 -0
  48. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/sync/comments.py +0 -0
  49. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/sync/github.py +0 -0
  50. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/sync/platform.py +0 -0
  51. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/sync/state.py +0 -0
  52. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
  53. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
  54. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
  55. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
  56. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/transforms/footer.py +0 -0
  57. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/transforms/images.py +0 -0
  58. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
  59. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_abbrevs.py +0 -0
  60. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_children_macro.py +0 -0
  61. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_cli.py +0 -0
  62. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_editlink.py +0 -0
  63. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_emitter.py +0 -0
  64. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_extra_css.py +0 -0
  65. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_footer.py +0 -0
  66. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_frontmatter.py +0 -0
  67. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_icons.py +0 -0
  68. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_images.py +0 -0
  69. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_internallinks.py +0 -0
  70. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_ir.py +0 -0
  71. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_linkdefs.py +0 -0
  72. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_loader.py +0 -0
  73. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_page_loader.py +0 -0
  74. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_parser.py +0 -0
  75. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_pdf.py +0 -0
  76. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_preprocess.py +0 -0
  77. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_preview.py +0 -0
  78. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_publish_client.py +0 -0
  79. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_publish_config.py +0 -0
  80. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_publish_pipeline.py +0 -0
  81. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_server.py +0 -0
  82. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_sync_anchoring.py +0 -0
  83. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_sync_command.py +0 -0
  84. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_sync_comments.py +0 -0
  85. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_sync_github.py +0 -0
  86. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_sync_state.py +0 -0
  87. {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.6}/tests/test_treeutil.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.9.5
3
+ Version: 0.9.6
4
4
  Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
5
5
  Author: Anders Hybertz
6
6
  License: GPL-3.0-or-later
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mkdocs2confluence"
3
- version = "0.9.5"
3
+ version = "0.9.6"
4
4
  description = "Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more"
5
5
  readme = "README.md"
6
6
  license = { text = "GPL-3.0-or-later" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mkdocs2confluence
3
- Version: 0.9.5
3
+ Version: 0.9.6
4
4
  Summary: Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more
5
5
  Author: Anders Hybertz
6
6
  License: GPL-3.0-or-later
@@ -99,9 +99,10 @@ def _render_one(source: str, kroki_url: str, *, quiet: bool = False) -> Path | N
99
99
  Transient HTTP errors (429, 5xx) and network blips are retried up to
100
100
  ``_RETRY_ATTEMPTS`` times with exponential backoff.
101
101
 
102
- When using the public kroki.io and all retries are exhausted due to 504s,
103
- one final attempt is made via mermaid.ink before giving up. Self-hosted
104
- Kroki instances never fall back to mermaid.ink.
102
+ When using the public kroki.io and all retries are exhausted due to kroki
103
+ being unreachable (504 or timeout), one final attempt is made via
104
+ mermaid.ink before giving up. Self-hosted Kroki instances never fall back
105
+ to mermaid.ink.
105
106
  """
106
107
  path = _cache_path(source)
107
108
  if path.exists():
@@ -111,7 +112,7 @@ def _render_one(source: str, kroki_url: str, *, quiet: bool = False) -> Path | N
111
112
 
112
113
  use_public_kroki = kroki_url.rstrip("/") == DEFAULT_KROKI_URL.rstrip("/")
113
114
  last_exc: Exception | None = None
114
- last_was_504 = False
115
+ kroki_unavailable = False # True when kroki is unreachable (504 or timeout)
115
116
 
116
117
  for attempt in range(_RETRY_ATTEMPTS):
117
118
  if attempt > 0:
@@ -130,22 +131,22 @@ def _render_one(source: str, kroki_url: str, *, quiet: bool = False) -> Path | N
130
131
  except urllib.error.HTTPError as exc:
131
132
  if exc.code in _RETRYABLE_HTTP:
132
133
  last_exc = exc
133
- last_was_504 = exc.code == 504
134
+ kroki_unavailable = exc.code == 504
134
135
  continue # retry
135
136
  _warn(f"mermaid diagram: Kroki returned HTTP {exc.code} {exc.reason} — falling back to code block")
136
137
  return None
137
138
  except urllib.error.URLError as exc:
138
139
  last_exc = exc
139
- last_was_504 = False
140
+ kroki_unavailable = True # timeout or connection refused — kroki unreachable
140
141
  continue # retry — network blip
141
142
  except (OSError, ValueError) as exc:
142
143
  _warn(f"mermaid diagram: {exc} — falling back to code block")
143
144
  return None
144
145
 
145
- # All Kroki retries exhausted — try mermaid.ink if on public kroki.io and last error was 504.
146
- if use_public_kroki and last_was_504:
146
+ # All Kroki retries exhausted — try mermaid.ink if on public kroki.io and kroki was unreachable.
147
+ if use_public_kroki and kroki_unavailable:
147
148
  try:
148
- _warn("mermaid diagram: kroki.io unavailable (504), trying mermaid.ink as fallback")
149
+ _warn("mermaid diagram: kroki.io unreachable, trying mermaid.ink as fallback")
149
150
  png = _mermaid_ink_png(source)
150
151
  if len(png) < _MIN_PNG_BYTES:
151
152
  raise ValueError(f"mermaid.ink returned {len(png)} bytes (expected a valid PNG)")
@@ -102,7 +102,7 @@ def test_render_deduplicates_identical_diagrams(tmp_path):
102
102
 
103
103
 
104
104
  def test_render_fallback_on_network_error(tmp_path, capsys):
105
- """When Kroki is unreachable, node is left unchanged (code block fallback)."""
105
+ """When Kroki is unreachable and mermaid.ink also fails, node falls back to code block."""
106
106
  import urllib.error
107
107
 
108
108
  node = MermaidDiagram(source=_SAMPLE_SOURCE)
@@ -114,6 +114,10 @@ def test_render_fallback_on_network_error(tmp_path, capsys):
114
114
  "mkdocs_to_confluence.transforms.mermaid._kroki_png",
115
115
  side_effect=urllib.error.URLError("timed out"),
116
116
  ),
117
+ patch(
118
+ "mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
119
+ side_effect=urllib.error.URLError("timed out"),
120
+ ),
117
121
  ):
118
122
  updated_nodes, attachments = render_mermaid_diagrams((node,))
119
123
 
@@ -218,7 +222,7 @@ def test_render_multiple_diagrams_concurrently(tmp_path):
218
222
 
219
223
 
220
224
  def test_render_one_failure_does_not_block_others(tmp_path):
221
- """If one diagram fails, the rest still render successfully."""
225
+ """If one diagram fails (both kroki and mermaid.ink), the rest still render successfully."""
222
226
  import urllib.error
223
227
 
224
228
  good_source = "graph TD\n A --> B\n"
@@ -231,7 +235,9 @@ def test_render_one_failure_does_not_block_others(tmp_path):
231
235
  return _FAKE_PNG
232
236
 
233
237
  with patch("mkdocs_to_confluence.transforms.mermaid._CACHE_DIR", tmp_path), \
234
- patch("mkdocs_to_confluence.transforms.mermaid._kroki_png", side_effect=fake_kroki):
238
+ patch("mkdocs_to_confluence.transforms.mermaid._kroki_png", side_effect=fake_kroki), \
239
+ patch("mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
240
+ side_effect=urllib.error.URLError("timeout")):
235
241
  updated, attachments = render_mermaid_diagrams(nodes)
236
242
 
237
243
  # One attachment for the successful diagram
@@ -258,7 +264,7 @@ def test_render_one_cached(tmp_path):
258
264
 
259
265
 
260
266
  def test_render_one_network_failure_returns_none(tmp_path):
261
- """_render_one returns None after all retries on persistent network failure."""
267
+ """_render_one returns None after all retries when both kroki and mermaid.ink fail."""
262
268
  import urllib.error
263
269
 
264
270
  from mkdocs_to_confluence.transforms.mermaid import _render_one
@@ -266,6 +272,8 @@ def test_render_one_network_failure_returns_none(tmp_path):
266
272
  with patch("mkdocs_to_confluence.transforms.mermaid._CACHE_DIR", tmp_path), \
267
273
  patch("mkdocs_to_confluence.transforms.mermaid.time.sleep"), \
268
274
  patch("mkdocs_to_confluence.transforms.mermaid._kroki_png",
275
+ side_effect=urllib.error.URLError("connection refused")), \
276
+ patch("mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
269
277
  side_effect=urllib.error.URLError("connection refused")):
270
278
  result = _render_one(_SAMPLE_SOURCE, "https://kroki.io")
271
279
 
@@ -371,7 +379,7 @@ def test_render_mermaid_cached_quiet_suppresses_stdout(tmp_path, capsys):
371
379
 
372
380
 
373
381
  def test_render_one_504_on_public_kroki_falls_back_to_mermaid_ink(tmp_path, capsys):
374
- """Public kroki.io 504 → automatic fallback to mermaid.ink for Mermaid."""
382
+ """Public kroki.io 504 → automatic fallback to mermaid.ink."""
375
383
  import urllib.error
376
384
 
377
385
  from mkdocs_to_confluence.transforms.mermaid import _render_one
@@ -389,6 +397,25 @@ def test_render_one_504_on_public_kroki_falls_back_to_mermaid_ink(tmp_path, caps
389
397
  mock_ink.assert_called_once_with(_SAMPLE_SOURCE)
390
398
 
391
399
 
400
+ def test_render_one_timeout_on_public_kroki_falls_back_to_mermaid_ink(tmp_path):
401
+ """Public kroki.io timeout → automatic fallback to mermaid.ink."""
402
+ import urllib.error
403
+
404
+ from mkdocs_to_confluence.transforms.mermaid import _render_one
405
+
406
+ with patch("mkdocs_to_confluence.transforms.mermaid._CACHE_DIR", tmp_path), \
407
+ patch("mkdocs_to_confluence.transforms.mermaid.time.sleep"), \
408
+ patch("mkdocs_to_confluence.transforms.mermaid._kroki_png",
409
+ side_effect=urllib.error.URLError("timed out")), \
410
+ patch("mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
411
+ return_value=_FAKE_PNG) as mock_ink:
412
+ result = _render_one(_SAMPLE_SOURCE, "https://kroki.io")
413
+
414
+ assert result is not None
415
+ assert result.exists()
416
+ mock_ink.assert_called_once_with(_SAMPLE_SOURCE)
417
+
418
+
392
419
  def test_render_one_504_on_self_hosted_kroki_does_not_fall_back(tmp_path):
393
420
  """Self-hosted Kroki 504 → no mermaid.ink fallback (privacy isolation)."""
394
421
  import urllib.error
@@ -407,6 +434,24 @@ def test_render_one_504_on_self_hosted_kroki_does_not_fall_back(tmp_path):
407
434
  mock_ink.assert_not_called()
408
435
 
409
436
 
437
+ def test_render_one_timeout_on_self_hosted_kroki_does_not_fall_back(tmp_path):
438
+ """Self-hosted Kroki timeout → no mermaid.ink fallback (privacy isolation)."""
439
+ import urllib.error
440
+
441
+ from mkdocs_to_confluence.transforms.mermaid import _render_one
442
+
443
+ with patch("mkdocs_to_confluence.transforms.mermaid._CACHE_DIR", tmp_path), \
444
+ patch("mkdocs_to_confluence.transforms.mermaid.time.sleep"), \
445
+ patch("mkdocs_to_confluence.transforms.mermaid._kroki_png",
446
+ side_effect=urllib.error.URLError("timed out")), \
447
+ patch("mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
448
+ return_value=_FAKE_PNG) as mock_ink:
449
+ result = _render_one(_SAMPLE_SOURCE, "https://my-internal-kroki.corp")
450
+
451
+ assert result is None
452
+ mock_ink.assert_not_called()
453
+
454
+
410
455
  def test_render_one_mermaid_ink_fallback_also_fails_returns_none(tmp_path, capsys):
411
456
  """When mermaid.ink fallback also fails, returns None (code-block fallback)."""
412
457
  import urllib.error