edwh-editorjs 2.0.0b3__tar.gz → 2.0.0b4__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 (31) hide show
  1. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/CHANGELOG.md +6 -0
  2. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/PKG-INFO +1 -1
  3. edwh_editorjs-2.0.0b4/editorjs/__about__.py +1 -0
  4. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/editorjs/blocks.py +187 -4
  5. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/editorjs/core.py +7 -2
  6. edwh_editorjs-2.0.0b4/htmlcov/.gitignore +2 -0
  7. edwh_editorjs-2.0.0b4/htmlcov/class_index.html +259 -0
  8. edwh_editorjs-2.0.0b4/htmlcov/favicon_32_cb_58284776.png +0 -0
  9. edwh_editorjs-2.0.0b4/htmlcov/function_index.html +395 -0
  10. edwh_editorjs-2.0.0b4/htmlcov/index.html +132 -0
  11. edwh_editorjs-2.0.0b4/htmlcov/keybd_closed_cb_ce680311.png +0 -0
  12. edwh_editorjs-2.0.0b4/htmlcov/status.json +1 -0
  13. edwh_editorjs-2.0.0b4/htmlcov/style_cb_8e611ae1.css +337 -0
  14. edwh_editorjs-2.0.0b4/htmlcov/z_a93c8aeb4b8fa1f9___init___py.html +125 -0
  15. edwh_editorjs-2.0.0b4/htmlcov/z_a93c8aeb4b8fa1f9_blocks_py.html +406 -0
  16. edwh_editorjs-2.0.0b4/htmlcov/z_a93c8aeb4b8fa1f9_exceptions_py.html +116 -0
  17. edwh_editorjs-2.0.0b4/htmlcov/z_a93c8aeb4b8fa1f9_parser_py.html +172 -0
  18. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/tests/test_core.py +28 -1
  19. edwh_editorjs-2.0.0b3/editorjs/__about__.py +0 -1
  20. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/.github/workflows/build_documentation.yml +0 -0
  21. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/.github/workflows/publish_to_pypi.yml +0 -0
  22. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/.github/workflows/pytest.yml +0 -0
  23. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/.gitignore +0 -0
  24. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/LICENSE +0 -0
  25. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/README.md +0 -0
  26. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/editorjs/__init__.py +0 -0
  27. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/editorjs/exceptions.py +0 -0
  28. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/editorjs/helpers.py +0 -0
  29. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/editorjs/types.py +0 -0
  30. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/pyproject.toml +0 -0
  31. {edwh_editorjs-2.0.0b3 → edwh_editorjs-2.0.0b4}/tests/__init__.py +0 -0
@@ -2,6 +2,12 @@
2
2
 
3
3
  <!--next-version-placeholder-->
4
4
 
5
+ ## v2.0.0-beta.4 (2024-11-07)
6
+
7
+ ### Feature
8
+
9
+ * Support (basic) tables, custom editorjs blocks like linkTool via <editorjs> ([`a6dfadf`](https://github.com/educationwarehouse/edwh-editorjs/commit/a6dfadf21ec008fe714704a056b9ffec751d731c))
10
+
5
11
  ## v2.0.0-beta.3 (2024-11-06)
6
12
 
7
13
  ### Fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: edwh-editorjs
3
- Version: 2.0.0b3
3
+ Version: 2.0.0b4
4
4
  Summary: EditorJS.py
5
5
  Project-URL: Homepage, https://github.com/educationwarehouse/edwh-EditorJS
6
6
  Author-email: SKevo <skevo.cw@gmail.com>, Robin van der Noord <robin.vdn@educationwarehouse.nl>
@@ -0,0 +1 @@
1
+ __version__ = "2.0.0-beta.4"
@@ -5,6 +5,7 @@ mdast to editorjs
5
5
  import abc
6
6
  import re
7
7
  import typing as t
8
+ from html.parser import HTMLParser
8
9
 
9
10
  from .exceptions import TODO
10
11
  from .types import EditorChildData, MDChildNode
@@ -56,7 +57,7 @@ def process_styled_content(item: MDChildNode, strict: bool = True) -> str:
56
57
  "strongEmphasis": "<b><i>{value}</i></b>",
57
58
  "link": '<a href="{url}">{value}</a>',
58
59
  "inlineCode": '<code class="inline-code">{value}</code>',
59
- # todo: <mark>
60
+ # todo: <mark>, linktool
60
61
  }
61
62
 
62
63
  if _type in BLOCKS:
@@ -138,16 +139,52 @@ class ParagraphBlock(EditorJSBlock):
138
139
  result = []
139
140
  current_text = ""
140
141
 
141
- for child in node.get("children"):
142
+ skip = 0
143
+ nodes = node.get("children", [])
144
+
145
+ for idx, child in enumerate(nodes):
146
+ if skip:
147
+ skip -= 1
148
+ continue
149
+
142
150
  _type = child.get("type")
143
- if _type == "image":
151
+
152
+ # deal with custom types
153
+ if _type == "html" and child.get("value", "").startswith("<editorjs"):
154
+ # special type, e.g. <editorjs type="linkTool" href=...>...</editorjs>
155
+
156
+ if child.get("value", "").endswith("/>"):
157
+ # self-closing
158
+ result.append(EditorJSCustom.to_json(node))
159
+ continue
160
+ else:
161
+ # <editorjs>something</editorjs> = 3 children
162
+ result.extend(
163
+ EditorJSCustom.to_json({"children": nodes[idx : idx + 2]})
164
+ )
165
+
166
+ skip = 2
167
+ continue
168
+
169
+ elif _type == "image":
144
170
  if current_text:
145
171
  result.append({"data": {"text": current_text}, "type": "paragraph"})
146
172
  current_text = ""
147
173
 
148
174
  result.extend(ImageBlock.to_json(child))
149
175
  else:
150
- current_text += cls.to_text(child)
176
+ child_text = cls.to_text(child)
177
+ _child_text = child_text.strip()
178
+ if _child_text.startswith("|") and _child_text.endswith("|"):
179
+ # note: this just supports text-only tables.
180
+ # tables with more complex elements break into multiple children.
181
+ # and mdast DOES support converting into a Table/TableCell structure
182
+ # via the GFM exttension
183
+ # but their default mdast->md converter does NOT implement these functionalities.
184
+ result.extend(TableBlock.to_json(child))
185
+ continue
186
+
187
+ current_text += child_text
151
188
 
152
189
  # final text after image:
153
190
  if current_text:
@@ -380,3 +417,149 @@ class QuoteBlock(EditorJSBlock):
380
417
  @classmethod
381
418
  def to_text(cls, node: MDChildNode) -> str:
382
419
  return default_to_text(node)
420
+
421
+
422
+ @block("table")
423
+ class TableBlock(EditorJSBlock):
424
+
425
+ @classmethod
426
+ def to_markdown(cls, data: EditorChildData) -> str:
427
+ """
428
+ | Script | Interpreter | User | System | |
429
+ |--------|-------------|------|--------|---|
430
+ | | | | | |
431
+ | | | | | |
432
+ | | | | | |
433
+ """
434
+
435
+ table = ""
436
+ rows = data.get("content", [])
437
+
438
+ # Add an empty header row if no headings are provided
439
+ if not data.get("withHeadings", False) and rows:
440
+ table += "| " + " | ".join([""] * len(rows[0])) + " |\n"
441
+ table += "|" + " - |" * len(rows[0]) + "\n"
442
+
443
+ # Populate rows
444
+ for idx, tr in enumerate(rows):
445
+ table += "| " + " | ".join(tr) + " |\n"
446
+
447
+ # Add separator if headings are enabled and it's the first row
448
+ if not idx and data.get("withHeadings", False):
449
+ table += "|" + " - |" * len(tr) + "\n"
450
+
451
+ return f"\n{table}\n"
452
+
453
+ @classmethod
454
+ def to_json(cls, node: MDChildNode) -> list[dict]:
455
+ # content":[["Yeah","Okay"],["<i>1</i>","<code class=\"inline-code\">2</code>"]]}}]
456
+ table = []
457
+ with_headings = False
458
+
459
+ # first row is headings or empty. If not empty, withHeadings is True
460
+ # second row must be ignored
461
+ for idx, row in enumerate(node.get("value", "").strip().split("\n")):
462
+ tr = [_.strip() for _ in row.split("|")[1:-1]]
463
+ if not idx:
464
+ # first
465
+ if any(tr):
466
+ with_headings = True
467
+ table.append(tr)
468
+
469
+ elif idx == 1:
470
+ continue
471
+ else:
472
+ table.append(tr)
473
+
474
+ return [
475
+ {
476
+ "type": "table",
477
+ "content": table,
478
+ "withHeadings": with_headings,
479
+ }
480
+ ]
481
+
482
+ @classmethod
483
+ def to_text(cls, node: MDChildNode) -> str:
484
+ raise TODO(node)
485
+
486
+
487
+ @block("linkTool")
488
+ class LinkBlock(EditorJSBlock):
489
+ @classmethod
490
+ def to_markdown(cls, data: EditorChildData) -> str:
491
+ link = data.get("link", "")
492
+ meta = data.get("meta", {})
493
+ title = meta.get("title", "")
494
+ description = meta.get("description", "")
495
+ image = meta.get("image", {}).get("url", "")
496
+ return f"""<editorjs type="linkTool" href="{link}" title="{title}" image="{image}">{description}</editorjs>"""
497
+
498
+ @classmethod
499
+ def to_json(cls, node: MDChildNode) -> list[dict]:
500
+ return [
501
+ {
502
+ "type": "linkTool",
503
+ "data": {
504
+ "link": node.get("href", ""),
505
+ "meta": {
506
+ "title": node.get("title", ""),
507
+ "description": node.get("body", ""),
508
+ "image": {
509
+ "url": node.get("image", ""),
510
+ },
511
+ },
512
+ },
513
+ }
514
+ ]
515
+
516
+ @classmethod
517
+ def to_text(cls, node: MDChildNode) -> str:
518
+ return ""
519
+
520
+
521
+ class AttributeParser(HTMLParser):
522
+ def __init__(self):
523
+ super().__init__()
524
+ self.attributes = {}
525
+ self.data = None
526
+
527
+ def handle_starttag(self, tag, attrs):
528
+ # Collect attributes when the tag is encountered
529
+ self.attributes = dict(attrs)
530
+
531
+ def handle_data(self, data):
532
+ self.data = data
533
+
534
+
535
+ class EditorJSCustom(EditorJSBlock):
536
+ """
537
+ Special type of block to deal with custom attributes
538
+ """
539
+
540
+ @classmethod
541
+ def parse_html(cls, html: str):
542
+ parser = AttributeParser()
543
+ parser.feed(html)
544
+
545
+ return parser.attributes, parser.data
546
+
547
+ @classmethod
548
+ def to_markdown(cls, data: EditorChildData) -> str:
549
+ raise TODO()
550
+
551
+ @classmethod
552
+ def to_json(cls, node: MDChildNode) -> list[dict]:
553
+ html = "".join(_["value"] for _ in node.get("children", []))
554
+ attrs, body = cls.parse_html(html)
555
+ _type = attrs.get("type", "")
556
+ attrs.setdefault("body", body) # only if there is no such attribute yet
557
+
558
+ if not (handler := BLOCKS.get(_type)):
559
+ raise ValueError(f"Unknown custom type {_type}")
560
+
561
+ return handler.to_json(attrs)
562
+
563
+ @classmethod
564
+ def to_text(cls, node: MDChildNode) -> str:
565
+ raise TODO()
@@ -18,7 +18,11 @@ class EditorJS:
18
18
  # internal representation is mdast, because we can convert to other types
19
19
  _mdast: MDRootNode
20
20
 
21
- def __init__(self, _mdast: str | dict, extras: list = ("task_list", "fenced-code-blocks")):
21
+ def __init__(
22
+ self,
23
+ _mdast: str | dict,
24
+ extras: list = ("task_list", "fenced-code-blocks", "tables"),
25
+ ):
22
26
  if not isinstance(_mdast, str | dict):
23
27
  raise TypeError("Only `str` or `dict` is supported!")
24
28
 
@@ -26,7 +30,7 @@ class EditorJS:
26
30
  MDRootNode, json.loads(_mdast) if isinstance(_mdast, str) else _mdast
27
31
  )
28
32
 
29
- self._md = markdown2.Markdown(extras=extras) # todo: striketrough, table, ?
33
+ self._md = markdown2.Markdown(extras=extras) # todo: striketrough, ?
30
34
 
31
35
  @classmethod
32
36
  def from_json(cls, data: str | dict) -> Self:
@@ -98,6 +102,7 @@ class EditorJS:
98
102
  Export HTML string
99
103
  """
100
104
  md = self.to_markdown()
105
+ # todo: deal with custom elements like linktool
101
106
  return self._md.convert(md)
102
107
 
103
108
  def __repr__(self):
@@ -0,0 +1,2 @@
1
+ # Created by coverage.py
2
+ *
@@ -0,0 +1,259 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5
+ <title>Coverage report</title>
6
+ <link rel="icon" sizes="32x32" href="favicon_32_cb_58284776.png">
7
+ <link rel="stylesheet" href="style_cb_8e611ae1.css" type="text/css">
8
+ <script src="coverage_html_cb_6fb7b396.js" defer></script>
9
+ </head>
10
+ <body class="indexfile">
11
+ <header>
12
+ <div class="content">
13
+ <h1>Coverage report:
14
+ <span class="pc_cov">76%</span>
15
+ </h1>
16
+ <aside id="help_panel_wrapper">
17
+ <input id="help_panel_state" type="checkbox">
18
+ <label for="help_panel_state">
19
+ <img id="keyboard_icon" src="keybd_closed_cb_ce680311.png" alt="Show/hide keyboard shortcuts">
20
+ </label>
21
+ <div id="help_panel">
22
+ <p class="legend">Shortcuts on this page</p>
23
+ <div class="keyhelp">
24
+ <p>
25
+ <kbd>f</kbd>
26
+ <kbd>n</kbd>
27
+ <kbd>s</kbd>
28
+ <kbd>m</kbd>
29
+ <kbd>x</kbd>
30
+ <kbd>c</kbd>
31
+ &nbsp; change column sorting
32
+ </p>
33
+ <p>
34
+ <kbd>[</kbd>
35
+ <kbd>]</kbd>
36
+ &nbsp; prev/next file
37
+ </p>
38
+ <p>
39
+ <kbd>?</kbd> &nbsp; show/hide this help
40
+ </p>
41
+ </div>
42
+ </div>
43
+ </aside>
44
+ <form id="filter_container">
45
+ <input id="filter" type="text" value="" placeholder="filter...">
46
+ <div>
47
+ <input id="hide100" type="checkbox" >
48
+ <label for="hide100">hide covered</label>
49
+ </div>
50
+ </form>
51
+ <h2>
52
+ <a class="button" href="index.html">Files</a>
53
+ <a class="button" href="function_index.html">Functions</a>
54
+ <a class="button current">Classes</a>
55
+ </h2>
56
+ <p class="text">
57
+ <a class="nav" href="https://coverage.readthedocs.io/en/7.6.4">coverage.py v7.6.4</a>,
58
+ created at 2024-10-31 13:29 +0100
59
+ </p>
60
+ </div>
61
+ </header>
62
+ <main id="index">
63
+ <table class="index" data-sortable>
64
+ <thead>
65
+ <tr class="tablehead" title="Click to sort">
66
+ <th id="file" class="name left" aria-sort="none" data-shortcut="f">File<span class="arrows"></span></th>
67
+ <th id="region" class="name left" aria-sort="none" data-default-sort-order="ascending" data-shortcut="n">class<span class="arrows"></span></th>
68
+ <th id="statements" aria-sort="none" data-default-sort-order="descending" data-shortcut="s">statements<span class="arrows"></span></th>
69
+ <th id="missing" aria-sort="none" data-default-sort-order="descending" data-shortcut="m">missing<span class="arrows"></span></th>
70
+ <th id="excluded" aria-sort="none" data-default-sort-order="descending" data-shortcut="x">excluded<span class="arrows"></span></th>
71
+ <th id="coverage" class="right" aria-sort="none" data-shortcut="c">coverage<span class="arrows"></span></th>
72
+ </tr>
73
+ </thead>
74
+ <tbody>
75
+ <tr class="region">
76
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9___init___py.html">pyeditorjs/__init__.py</a></td>
77
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9___init___py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
78
+ <td>14</td>
79
+ <td>3</td>
80
+ <td>0</td>
81
+ <td class="right" data-ratio="11 14">79%</td>
82
+ </tr>
83
+ <tr class="region">
84
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t42">pyeditorjs/blocks.py</a></td>
85
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t42"><data value='EditorJsBlock'>EditorJsBlock</data></a></td>
86
+ <td>4</td>
87
+ <td>3</td>
88
+ <td>0</td>
89
+ <td class="right" data-ratio="1 4">25%</td>
90
+ </tr>
91
+ <tr class="region">
92
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t87">pyeditorjs/blocks.py</a></td>
93
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t87"><data value='HeaderBlock'>HeaderBlock</data></a></td>
94
+ <td>9</td>
95
+ <td>1</td>
96
+ <td>0</td>
97
+ <td class="right" data-ratio="8 9">89%</td>
98
+ </tr>
99
+ <tr class="region">
100
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t120">pyeditorjs/blocks.py</a></td>
101
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t120"><data value='ParagraphBlock'>ParagraphBlock</data></a></td>
102
+ <td>2</td>
103
+ <td>0</td>
104
+ <td>0</td>
105
+ <td class="right" data-ratio="2 2">100%</td>
106
+ </tr>
107
+ <tr class="region">
108
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t134">pyeditorjs/blocks.py</a></td>
109
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t134"><data value='ListBlock'>ListBlock</data></a></td>
110
+ <td>8</td>
111
+ <td>1</td>
112
+ <td>0</td>
113
+ <td class="right" data-ratio="7 8">88%</td>
114
+ </tr>
115
+ <tr class="region">
116
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t168">pyeditorjs/blocks.py</a></td>
117
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t168"><data value='DelimiterBlock'>DelimiterBlock</data></a></td>
118
+ <td>1</td>
119
+ <td>0</td>
120
+ <td>0</td>
121
+ <td class="right" data-ratio="1 1">100%</td>
122
+ </tr>
123
+ <tr class="region">
124
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t174">pyeditorjs/blocks.py</a></td>
125
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t174"><data value='ImageBlock'>ImageBlock</data></a></td>
126
+ <td>10</td>
127
+ <td>1</td>
128
+ <td>0</td>
129
+ <td class="right" data-ratio="9 10">90%</td>
130
+ </tr>
131
+ <tr class="region">
132
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t236">pyeditorjs/blocks.py</a></td>
133
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t236"><data value='QuoteBlock'>QuoteBlock</data></a></td>
134
+ <td>7</td>
135
+ <td>7</td>
136
+ <td>0</td>
137
+ <td class="right" data-ratio="0 7">0%</td>
138
+ </tr>
139
+ <tr class="region">
140
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t253">pyeditorjs/blocks.py</a></td>
141
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t253"><data value='TableBlock'>TableBlock</data></a></td>
142
+ <td>11</td>
143
+ <td>11</td>
144
+ <td>0</td>
145
+ <td class="right" data-ratio="0 11">0%</td>
146
+ </tr>
147
+ <tr class="region">
148
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t275">pyeditorjs/blocks.py</a></td>
149
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t275"><data value='CodeBlock'>CodeBlock</data></a></td>
150
+ <td>4</td>
151
+ <td>4</td>
152
+ <td>0</td>
153
+ <td class="right" data-ratio="0 4">0%</td>
154
+ </tr>
155
+ <tr class="region">
156
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t286">pyeditorjs/blocks.py</a></td>
157
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t286"><data value='WarningBlock'>WarningBlock</data></a></td>
158
+ <td>6</td>
159
+ <td>6</td>
160
+ <td>0</td>
161
+ <td class="right" data-ratio="0 6">0%</td>
162
+ </tr>
163
+ <tr class="region">
164
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t304">pyeditorjs/blocks.py</a></td>
165
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html#t304"><data value='RawBlock'>RawBlock</data></a></td>
166
+ <td>4</td>
167
+ <td>4</td>
168
+ <td>0</td>
169
+ <td class="right" data-ratio="0 4">0%</td>
170
+ </tr>
171
+ <tr class="region">
172
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html">pyeditorjs/blocks.py</a></td>
173
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_blocks_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
174
+ <td>80</td>
175
+ <td>0</td>
176
+ <td>0</td>
177
+ <td class="right" data-ratio="80 80">100%</td>
178
+ </tr>
179
+ <tr class="region">
180
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_exceptions_py.html#t8">pyeditorjs/exceptions.py</a></td>
181
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_exceptions_py.html#t8"><data value='EditorJsException'>EditorJsException</data></a></td>
182
+ <td>0</td>
183
+ <td>0</td>
184
+ <td>0</td>
185
+ <td class="right" data-ratio="0 0">100%</td>
186
+ </tr>
187
+ <tr class="region">
188
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_exceptions_py.html#t14">pyeditorjs/exceptions.py</a></td>
189
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_exceptions_py.html#t14"><data value='EditorJsParseError'>EditorJsParseError</data></a></td>
190
+ <td>0</td>
191
+ <td>0</td>
192
+ <td>0</td>
193
+ <td class="right" data-ratio="0 0">100%</td>
194
+ </tr>
195
+ <tr class="region">
196
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_exceptions_py.html#t18">pyeditorjs/exceptions.py</a></td>
197
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_exceptions_py.html#t18"><data value='EditorJSUnsupportedBlock'>EditorJSUnsupportedBlock</data></a></td>
198
+ <td>0</td>
199
+ <td>0</td>
200
+ <td>0</td>
201
+ <td class="right" data-ratio="0 0">100%</td>
202
+ </tr>
203
+ <tr class="region">
204
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_exceptions_py.html">pyeditorjs/exceptions.py</a></td>
205
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_exceptions_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
206
+ <td>4</td>
207
+ <td>0</td>
208
+ <td>0</td>
209
+ <td class="right" data-ratio="4 4">100%</td>
210
+ </tr>
211
+ <tr class="region">
212
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_parser_py.html#t10">pyeditorjs/parser.py</a></td>
213
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_parser_py.html#t10"><data value='EditorJsParser'>EditorJsParser</data></a></td>
214
+ <td>19</td>
215
+ <td>6</td>
216
+ <td>0</td>
217
+ <td class="right" data-ratio="13 19">68%</td>
218
+ </tr>
219
+ <tr class="region">
220
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_parser_py.html">pyeditorjs/parser.py</a></td>
221
+ <td class="name left"><a href="z_a93c8aeb4b8fa1f9_parser_py.html"><data value=''><span class='no-noun'>(no class)</span></data></a></td>
222
+ <td>15</td>
223
+ <td>0</td>
224
+ <td>0</td>
225
+ <td class="right" data-ratio="15 15">100%</td>
226
+ </tr>
227
+ </tbody>
228
+ <tfoot>
229
+ <tr class="total">
230
+ <td class="name left">Total</td>
231
+ <td class="name left">&nbsp;</td>
232
+ <td>198</td>
233
+ <td>47</td>
234
+ <td>0</td>
235
+ <td class="right" data-ratio="151 198">76%</td>
236
+ </tr>
237
+ </tfoot>
238
+ </table>
239
+ <p id="no_rows">
240
+ No items found using the specified filter.
241
+ </p>
242
+ </main>
243
+ <footer>
244
+ <div class="content">
245
+ <p>
246
+ <a class="nav" href="https://coverage.readthedocs.io/en/7.6.4">coverage.py v7.6.4</a>,
247
+ created at 2024-10-31 13:29 +0100
248
+ </p>
249
+ </div>
250
+ <aside class="hidden">
251
+ <a id="prevFileLink" class="nav" href=""></a>
252
+ <a id="nextFileLink" class="nav" href=""></a>
253
+ <button type="button" class="button_prev_file" data-shortcut="["></button>
254
+ <button type="button" class="button_next_file" data-shortcut="]"></button>
255
+ <button type="button" class="button_show_hide_help" data-shortcut="?"></button>
256
+ </aside>
257
+ </footer>
258
+ </body>
259
+ </html>