fux-decoder-drawio 4.1.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.
@@ -0,0 +1,12 @@
1
+ """`fux_decoder_drawio` — the fux-decoder-drawio package (W-289, SR-PLUGINS).
2
+
3
+ One fux decoder module per format it reads: `fux_decoder_drawio.drawio`. Each is the
4
+ decoder protocol of SR-DECODE — `EXTENSIONS`, `VERSION`, `USES` and
5
+ `decode(raw, rel_path)` — moved here unchanged from `src/fux/decode/` in
6
+ W-289 phase 2.
7
+
8
+ **fux loads a module from here only when `.fux/plugins.toml` declares this
9
+ package**, after verifying its version and digest (SR-PLUGINS P3, P4), by its
10
+ fixed import name (P7). A same-named file in `.fux/decoders/` overrides it
11
+ (P8). This file runs nothing: importing the package imports no decoder.
12
+ """
@@ -0,0 +1,130 @@
1
+ """`.drawio` / `.dio` -> Markdown — the labels in an architecture diagram.
2
+
3
+ Niche and worth it: a team's architecture lives in diagrams, and the box labels
4
+ are exactly the terms someone searches for. Today they are invisible.
5
+
6
+ The format is XML, but the interesting part is usually **deflated and
7
+ base64'd** inside a `<diagram>` element — draw.io compresses the model by
8
+ default. `zlib` and `base64` are both stdlib, so this stays `$0`; the only
9
+ subtlety is that the payload is *raw* deflate with no zlib header, which needs
10
+ a negative window size.
11
+
12
+ ⚠ **Labels are HTML fragments**, so they are converted through `html` rather
13
+ than stripped by hand — one HTML implementation, per SR-DECODE.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import base64
19
+ import re
20
+ import zlib
21
+ from urllib.parse import unquote
22
+ from fux.decode.ext.limits import limit
23
+
24
+ # Imports are ABSOLUTE, not relative, and that is what makes this file work in
25
+ # both places it runs: as a package module, and as a consumer copy in
26
+ # `.fux/decoders/` loaded by path. A path-loaded file has no parent package, so
27
+ # `from . import _xml` raises `attempted relative import with no known parent
28
+ # package` — the copy would be dead on arrival. Absolute imports mean the file
29
+ # fux ships and the file you edit are byte-identical (SR-DECODE decision 11).
30
+ from fux.decode.ext import xml as _xml
31
+ from fux_decoder_html.html import html_to_markdown
32
+ from fux.constants import fixed
33
+
34
+ #: **The reuse key's handle on this decoder** (W-166). Bump it by hand in the
35
+ #: same change as any edit that can change what `decode()` returns, and the next
36
+ #: `fux ingest` re-extracts the documents bound to THIS decoder and no others.
37
+ #: Leaving it alone is the claim that the edit cannot move a byte of output.
38
+ #: `tests/decode/test_decoder_versions.py` fails on a changed module that did
39
+ #: not bump it. [SR-DECODE](../../../../records/0139_decode.md) decision 11a.
40
+ VERSION = fixed("decoders.drawio", "version") # not bumped by W-225 4a (caps moved, same values) nor 5b (numerals moved, same output); not bumped by W-288 (imports moved to decode/ext, same output; USES now carries the extensions' VERSIONs)
41
+
42
+ EXTENSIONS = tuple(fixed("decoders.drawio", "extensions"))
43
+ #: The decoder extensions this decoder calls, read by the reuse key (SR-DECODE decision 24).
44
+ USES = tuple(fixed("decoders.drawio", "uses"))
45
+
46
+ #: A diagram page is a complete unit — a short page was being absorbed by
47
+ #: the next one and cited under the wrong name.
48
+
49
+ #: ⚠ **`MAX_INFLATED` is `[limits.drawio] max_inflated` in .fux/formats.toml** since W-225 stage 4a
50
+ #: (SR-LAW-12 decision 9b): read per call through `limit()`, in the extract-config digest.
51
+ _TAG_RE = re.compile(r"<[^>]+>")
52
+
53
+
54
+ def decode(raw: bytes, rel_path: str) -> str | None:
55
+ try:
56
+ root = _xml.parse(raw)
57
+ except _xml.UnsafeXml:
58
+ return None
59
+
60
+ blocks: list[str] = []
61
+ for diagram in root.iter():
62
+ if _xml.local(diagram.tag) != "diagram":
63
+ continue
64
+ name = ""
65
+ for key, value in diagram.attrib.items():
66
+ if _xml.local(key) == "name":
67
+ name = value
68
+ model = _model(diagram)
69
+ if model is None:
70
+ continue
71
+ labels = _labels(model)
72
+ if not labels:
73
+ continue
74
+ blocks.append(f"## {name}" if name else "## Diagram")
75
+ blocks.extend(f"- {label}" for label in labels)
76
+
77
+ body = "\n\n".join(blocks)
78
+ return body if body.strip() else None
79
+
80
+
81
+ def _model(diagram):
82
+ """The `mxGraphModel` element, inflating the compressed payload if needed."""
83
+ for child in diagram:
84
+ if _xml.local(child.tag) == "mxGraphModel":
85
+ return child # stored uncompressed; nothing to do
86
+ payload = (diagram.text or "").strip()
87
+ if not payload:
88
+ return None
89
+ try:
90
+ compressed = base64.b64decode(payload, validate=True)
91
+ # Negative window bits: raw deflate, no zlib header. draw.io writes it
92
+ # this way and a default-window inflate simply fails, which reads as
93
+ # "not a diagram".
94
+ inflated = zlib.decompressobj(-zlib.MAX_WBITS).decompress(
95
+ compressed, limit("drawio", "max_inflated")
96
+ )
97
+ except (ValueError, zlib.error):
98
+ return None
99
+ try:
100
+ return _xml.parse_text(unquote(inflated.decode("utf-8", errors="replace")))
101
+ except _xml.UnsafeXml:
102
+ return None
103
+
104
+
105
+ def _labels(model) -> list[str]:
106
+ out: list[str] = []
107
+ seen: set[str] = set()
108
+ # Document order, not sorted: a diagram's cells are laid out in a meaningful
109
+ # sequence and `extract.py` scores earlier text slightly higher.
110
+ for cell in model.iter():
111
+ if _xml.local(cell.tag) != "mxCell":
112
+ continue
113
+ value = ""
114
+ for key, attr in cell.attrib.items():
115
+ if _xml.local(key) == "value":
116
+ value = attr
117
+ if not value.strip():
118
+ continue
119
+ text = _plain(value)
120
+ if text and text not in seen:
121
+ seen.add(text)
122
+ out.append(text)
123
+ return out
124
+
125
+
126
+ def _plain(value: str) -> str:
127
+ if "<" not in value:
128
+ return " ".join(value.split())
129
+ converted = html_to_markdown(value)
130
+ return " ".join(_TAG_RE.sub(" ", converted).split())
@@ -0,0 +1,31 @@
1
+ Metadata-Version: 2.5
2
+ Name: fux-decoder-drawio
3
+ Version: 4.1.1
4
+ Summary: Fux decoder package: draw.io diagrams.
5
+ Project-URL: Homepage, https://github.com/arpitarya/fux
6
+ Project-URL: Repository, https://github.com/arpitarya/fux
7
+ Author-email: arpit arya <arpitarya.me@gmail.com>
8
+ License-Expression: MIT
9
+ Requires-Python: >=3.12
10
+ Requires-Dist: fux-core==4.1.1
11
+ Requires-Dist: fux-decoder-html==4.1.1
12
+ Description-Content-Type: text/markdown
13
+
14
+ # fux-decoder-drawio
15
+
16
+ Part of [fux](https://github.com/arpitarya/fux) — rank your written knowledge
17
+ from a small index committed to git; fetch content from the systems that own
18
+ it; verify freshness at answer time.
19
+
20
+ **`fux-decoder-drawio` is a fux decoder package: draw.io diagrams.** Only `fux-engine` is mandatory;
21
+ everything else is declared per repository in the committed
22
+ `.fux/plugins.toml`, pinned to one version and digest, and refused loudly when
23
+ it is missing or differs, so two machines on one repository never build
24
+ different indexes.
25
+
26
+ It reads `.drawio` `.dio`. Install it and declare it in `.fux/plugins.toml` --
27
+ `fux setup` proposes it when your sources hold those files -- or fux does not
28
+ read them, and `fux doctor` says so.
29
+
30
+ It is released on fux's own version, together with every other fux package.
31
+ Documentation: <https://github.com/arpitarya/fux#readme>
@@ -0,0 +1,5 @@
1
+ fux_decoder_drawio/__init__.py,sha256=Q-K4HIMHkXoehwUQMl1GiqqLuSjDtsfTlWHuSyVt3XM,631
2
+ fux_decoder_drawio/drawio.py,sha256=Ali9ioJJ9XiD-GT303cUsZpmwRFQTFtGqdVmwLtsK_w,5227
3
+ fux_decoder_drawio-4.1.1.dist-info/METADATA,sha256=1eXKxhc7gTUBJhTZ1_ad-y_UWuDlI0lTcJF_PwhF2xE,1310
4
+ fux_decoder_drawio-4.1.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
5
+ fux_decoder_drawio-4.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any