easyvista-python-client 0.4.0__tar.gz → 0.4.1__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 (77) hide show
  1. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/CHANGELOG.md +55 -1
  2. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/PKG-INFO +1 -1
  3. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/content.rst +43 -21
  4. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_version.py +1 -1
  5. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/content/conversion.py +48 -6
  6. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/pyproject.toml +1 -1
  7. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/skills/easyvista-asset-workflow/SKILL.md +1 -1
  8. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/skills/easyvista-client-setup/SKILL.md +1 -1
  9. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/skills/easyvista-directory/SKILL.md +1 -1
  10. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/skills/easyvista-document-workflow/SKILL.md +1 -1
  11. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/skills/easyvista-instance-discovery/SKILL.md +1 -1
  12. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/skills/easyvista-reporting-and-context/SKILL.md +1 -1
  13. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/skills/easyvista-search-syntax/SKILL.md +1 -1
  14. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/skills/easyvista-ticket-actions/SKILL.md +3 -2
  15. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/skills/easyvista-ticket-workflow/SKILL.md +1 -1
  16. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/.gitignore +0 -0
  17. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/.pre-commit-config.yaml +0 -0
  18. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/.readthedocs.yaml +0 -0
  19. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/CONTRIBUTING.md +0 -0
  20. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/LICENSE +0 -0
  21. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/README.md +0 -0
  22. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/_static/.gitkeep +0 -0
  23. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/_templates/.gitkeep +0 -0
  24. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/api_reference.rst +0 -0
  25. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/conf.py +0 -0
  26. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/development.rst +0 -0
  27. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/index.rst +0 -0
  28. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/installation.rst +0 -0
  29. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/publishing.rst +0 -0
  30. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/sponsoring.rst +0 -0
  31. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/user_guide.rst +0 -0
  32. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/docs/vendor-api-reference.md +0 -0
  33. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/__init__.py +0 -0
  34. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_async/__init__.py +0 -0
  35. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_async/_concurrency.py +0 -0
  36. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_async/_transport.py +0 -0
  37. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_async/client.py +0 -0
  38. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_fields.py +0 -0
  39. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_html.py +0 -0
  40. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_sync/__init__.py +0 -0
  41. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_sync/_concurrency.py +0 -0
  42. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_sync/_transport.py +0 -0
  43. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_sync/client.py +0 -0
  44. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/_transport.py +0 -0
  45. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/config.py +0 -0
  46. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/content/__init__.py +0 -0
  47. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/context.py +0 -0
  48. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/directory.py +0 -0
  49. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/discovery.py +0 -0
  50. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/exceptions.py +0 -0
  51. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/field_model.py +0 -0
  52. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/filters.py +0 -0
  53. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/models/__init__.py +0 -0
  54. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/models/action.py +0 -0
  55. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/models/asset.py +0 -0
  56. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/models/common.py +0 -0
  57. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/models/department.py +0 -0
  58. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/models/document.py +0 -0
  59. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/models/employee.py +0 -0
  60. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/models/generic.py +0 -0
  61. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/models/request.py +0 -0
  62. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/pagination.py +0 -0
  63. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/py.typed +0 -0
  64. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/references.py +0 -0
  65. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/reporting.py +0 -0
  66. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/resources/__init__.py +0 -0
  67. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/resources/actions.py +0 -0
  68. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/resources/assets.py +0 -0
  69. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/resources/departments.py +0 -0
  70. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/resources/descriptor.py +0 -0
  71. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/resources/discovery.py +0 -0
  72. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/resources/documents.py +0 -0
  73. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/resources/employees.py +0 -0
  74. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/resources/requests.py +0 -0
  75. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/timestamps.py +0 -0
  76. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/easyvista_python_client/workflow.py +0 -0
  77. {easyvista_python_client-0.4.0 → easyvista_python_client-0.4.1}/skills/README.md +0 -0
@@ -15,6 +15,59 @@ is the error. Tags carry no `v` prefix.
15
15
 
16
16
  ## [Unreleased]
17
17
 
18
+ ## [0.4.1] - 2026-10-02
19
+
20
+ A patch release of the content reader. A `<u>`, `<mark>` or `<ins>` round a
21
+ block no longer reads as broken Markdown, four guards of 0.4.0's fixes gain
22
+ the tests they lacked, and `docs/content.rst` corrects what it said about
23
+ `start` values, `ValueError`, the CVE-2025-6069 tail and a second round
24
+ trip. Writing is unchanged, and so is everything outside
25
+ `easyvista_python_client.content`. `glpi_python_client`'s port of this
26
+ converter makes the same correction.
27
+
28
+ ### Fixed
29
+
30
+ - **A block inside `<u>`, `<mark>` or `<ins>` read as broken Markdown**, since
31
+ 0.4.0 kept these tags raw (fix 11). Markdown has no inline tag round blocks:
32
+ a table inside one read as pipe text, a list, a heading, a quote or a rule
33
+ as its Markdown source, paragraphs were not a fixed point, and
34
+ `<u><pre>code</pre></u>` left a fence open that showed the rest of the memo
35
+ as code. Round a block the tag is now dropped and the blocks kept, as
36
+ `glpi_python_client`'s reader at `917f030` read them. Inside a table cell
37
+ or a heading, whose blocks are one line, the tag stays, and around inline
38
+ content nothing changes. The Markdown read from such a memo changes once.
39
+
40
+ ### Documentation
41
+
42
+ - `docs/content.rst`, corrected after a review of the converter:
43
+ - a `start` that is not a decimal number counts from 1 as a browser
44
+ counts one holding no digit; a browser reads `" 3"`, `"+3"` or
45
+ `"3abc"` as 3, which the converter counts from 1;
46
+ - any `ValueError` raised while converting degrades the memo to text, not
47
+ only an unreadable `colspan` or `start` (the module and
48
+ `from_transport` docstrings say so too);
49
+ - the CVE-2025-6069 note says that everything after a memo's last `>`
50
+ reads as text, an unterminated comment and a closing tag cut short
51
+ inside a link included, where a patched CPython drops some of it;
52
+ - a second write-and-read cycle changes nothing more except for two
53
+ shapes, adjacent lists with different bullets and a fence whose info
54
+ string holds a character reference, where 0.4.0's page and its
55
+ changelog said it changes nothing;
56
+ - `<u>`, `<mark>` and `<ins>` round a block, and a known hole for
57
+ `<b>`, `<em>` and `<s>` round blocks.
58
+ - The `easyvista-ticket-actions` skill says a `<u>` round a block is
59
+ dropped.
60
+
61
+ ### Notes
62
+
63
+ - Four guards of the 0.4.0 fixes that no test caught are pinned, each by a
64
+ test that fails without it: a second `<` before a space keeps one escape
65
+ (fix 5), a header cell escapes a `|` in `<kbd>` or `<samp>` and splits a
66
+ line break in inline code (fix 6), a table inside an `<a>` without `href`
67
+ stays a table (fix 7), and an ordered item numbered 10 or more indents its
68
+ content by its bullet's width (fix 12). A count pins that the walk finding
69
+ blocks inside `<u>`, `<mark>` and `<ins>` checks each tag about once.
70
+
18
71
  ## [0.4.0] - 2026-10-02
19
72
 
20
73
  Adds Markdown <-> memo HTML conversion as an optional extra, drops Python
@@ -1592,7 +1645,8 @@ Initial public release.
1592
1645
  status/error code, with non-retryable validation errors (HTTP 590, code 2013).
1593
1646
  - `py.typed` marker — the package ships inline type information.
1594
1647
 
1595
- [Unreleased]: https://github.com/baraline/easyvista_python_client/compare/0.4.0...HEAD
1648
+ [Unreleased]: https://github.com/baraline/easyvista_python_client/compare/0.4.1...HEAD
1649
+ [0.4.1]: https://github.com/baraline/easyvista_python_client/compare/0.4.0...0.4.1
1596
1650
  [0.4.0]: https://github.com/baraline/easyvista_python_client/compare/0.3.0...0.4.0
1597
1651
  [0.3.0]: https://github.com/baraline/easyvista_python_client/compare/0.2.0...0.3.0
1598
1652
  [0.2.0]: https://github.com/baraline/easyvista_python_client/compare/0.1.0...0.2.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: easyvista-python-client
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Typed Python client for the EasyVista Service Manager REST API
5
5
  Project-URL: Homepage, https://github.com/baraline/easyvista_python_client
6
6
  Project-URL: Documentation, https://easyvista-python-client.readthedocs.io/en/latest/
@@ -141,15 +141,21 @@ What each kind of element becomes:
141
141
  stay as those raw tags, and **struck text** (``<s>``, ``<del>``,
142
142
  ``<strike>``) as a raw ``<s>``. CommonMark has no spelling for any of them,
143
143
  and ``to_transport`` passes the tags through, so the formatting survives.
144
- The cost is raw HTML in the Markdown.
144
+ The cost is raw HTML in the Markdown. Markdown has no inline tag round
145
+ blocks, so a ``<u>``, ``<mark>`` or ``<ins>`` holding a table, a list, a
146
+ heading, a quote, a ``<pre>``, a rule or paragraphs is dropped and its
147
+ blocks kept; inside a table cell or a heading, which hold one line, it
148
+ stays.
145
149
  * **Links** become ``[text](https://... "title")``, and a link whose text is
146
150
  its own URL, a pasted link, becomes the autolink ``<https://...>``. No link
147
151
  target is filtered, ``javascript:`` included (see `It is not a sanitiser`_).
148
- * **Lists** nest and keep their numbers, including an ``<ol start>``.
149
- **Tables** become GFM tables, and a ``<br>`` inside a cell stays a raw
150
- ``<br>``, since a GFM cell is one line. **Preformatted blocks** become fences
151
- that keep the ``language-`` class ``cmark-gfm`` writes, with a fence longer
152
- than any run of backticks in the code.
152
+ * **Lists** nest and keep their numbers, including an ``<ol start>``. A
153
+ ``start`` that is not a decimal number counts from 1, as a browser counts
154
+ one holding no digit, such as ``²``; a browser reads ``" 3"``, ``"+3"`` or
155
+ ``"3abc"`` as 3. **Tables** become GFM tables, and a ``<br>`` inside a cell
156
+ stays a raw ``<br>``, since a GFM cell is one line. **Preformatted blocks**
157
+ become fences that keep the ``language-`` class ``cmark-gfm`` writes, with a
158
+ fence longer than any run of backticks in the code.
153
159
  * ``<head>``, ``<script>``, ``<style>``, ``<template>`` and ``<title>`` are
154
160
  dropped, as a browser does not display them. Styling such as ``<font>``
155
161
  colours or ``<span style>`` keeps its text and loses the style.
@@ -180,10 +186,12 @@ and 3.14.6 (measured 2026-10-02, default recursion limit). The converter does
180
186
  not predict that. It attempts the conversion and, if the walk does not fit,
181
187
  reads the memo as its text instead, a line per block. Every word the
182
188
  conversion would have produced is still there, in order. What is lost is
183
- structure: link targets, image alt text, emphasis and code fencing. A
184
- ``colspan`` or ``start`` attribute ``markdownify`` cannot read as a number,
185
- such as ``"²"``, takes the same path, and so does a document ``html.parser``
186
- refuses outright, such as one carrying an unknown ``<![FOO[`` marked section.
189
+ structure: link targets, image alt text, emphasis and code fencing. Any
190
+ ``ValueError`` from the conversion takes the same path -- ``markdownify``
191
+ raises one for a ``colspan`` or ``start`` attribute it cannot read as a
192
+ number, such as a ``colspan`` of ``"²"`` -- and so does a document
193
+ ``html.parser`` refuses outright, such as one carrying an unknown
194
+ ``<![FOO[`` marked section.
187
195
 
188
196
  Because the budget is whatever stack is left when the call starts, the same
189
197
  memo can convert from one call site and degrade from a deeper one. A caller
@@ -275,9 +283,13 @@ A few things do not come back:
275
283
  * a line holding only ``*`` is an empty list item in CommonMark, and reads
276
284
  back as nothing.
277
285
 
278
- A second cycle changes nothing more. That is the property a two-way sync
279
- relies on: once a text has made one trip, writing what was read back and
280
- reading it again gives exactly the same Markdown.
286
+ After that first cycle, a further one changes nothing more, with two
287
+ exceptions (reproduced 2026-10-02): two adjacent lists with different
288
+ bullets read as one loose list, then as one tight list; and a fence whose
289
+ info string holds a character reference, such as ``&amp;amp;``, loses one
290
+ level of it on each cycle. A two-way sync relies on the rest: once a text
291
+ has made one trip, writing what was read back and reading it again gives
292
+ the same Markdown.
281
293
 
282
294
  **A memo read, written back and read again.** This direction is held to more.
283
295
  The aim is that what the Markdown displays is what the memo displayed, and
@@ -332,7 +344,12 @@ in the preproduction sample:
332
344
  * a table whose ``<td>`` and ``<tr>`` are never closed folds into one cell,
333
345
  keeping its words;
334
346
  * a definition list (``<dl>``) reads as a ``term`` line and a
335
- ``: definition`` line, so its display gains the colon.
347
+ ``: definition`` line, so its display gains the colon;
348
+ * bold, italic or struck text (``<b>``, ``<em>``, ``<s>`` and their
349
+ synonyms) wrapped round blocks, other than a single paragraph, shows its
350
+ markers or its tag as text, and a list, a table or a heading inside it as
351
+ Markdown source; round a ``<pre>`` it leaves a fence open, so the rest of
352
+ the memo shows as code.
336
353
 
337
354
  Some shapes are not fixed points at the first read, and settle after one more
338
355
  cycle. None broke a fixed point in the preproduction sample. Adjacent lists
@@ -390,9 +407,12 @@ fixed in those releases according to their changelogs). Many unfinished tags
390
407
  after a memo's last ``>`` is the shape that reaches the converter, and a memo
391
408
  is outside data. So ``from_transport`` spells every ``<`` after the last ``>``
392
409
  as ``&lt;`` before parsing, since no ``<`` there can finish a tag. As a side
393
- effect, an unfinished tag at the very end, ``x <a``, reads as the text
394
- ``x \<a`` on every interpreter, whereas a patched CPython would drop it. Prefer
395
- a patched interpreter anyway: the guard covers the converter's input, and the
410
+ effect, whatever follows the memo's last ``>`` reads as text on every
411
+ interpreter, where a patched CPython drops some of it: an unfinished tag at
412
+ the very end, ``x <a``, reads as ``x \<a``; an unterminated comment,
413
+ ``<!-- note``, as ``\<!-- note``; and a closing tag cut short inside a link,
414
+ ``<a href="...">lien</a``, leaves ``\</a`` in the link's text. Prefer a
415
+ patched interpreter anyway: the guard covers the converter's input, and the
396
416
  CPython fix covers the parser itself.
397
417
 
398
418
  Where it comes from
@@ -414,12 +434,14 @@ proposed to ``glpi_python_client``. The fixes cover:
414
434
  * ``<br>`` inside inline code;
415
435
  * tables inside headings and links, and the spacing of flattened cells;
416
436
  * ``<center>``;
417
- * underline and highlight, as raw tags;
437
+ * underline and highlight, as raw tags, except round a block (corrected in
438
+ 0.4.1);
418
439
  * numbering a long ordered list in linear time;
419
440
  * a quadratic pattern on runs of spaces;
420
441
  * unreadable ``colspan`` and ``start`` values;
421
442
  * the CVE-2025-6069 tail.
422
443
 
423
- ``CHANGELOG.md`` lists them under 0.4.0. Apart from those fixes, the names and
424
- the error messages, the only difference from ``glpi_python_client`` is that the
425
- libraries are an optional extra here, not dependencies.
444
+ ``CHANGELOG.md`` lists them under 0.4.0, and the correction under 0.4.1.
445
+ Apart from those fixes, the names and the error messages, the only
446
+ difference from ``glpi_python_client`` is that the libraries are an optional
447
+ extra here, not dependencies.
@@ -5,4 +5,4 @@
5
5
  live only in ``__init__``.
6
6
  """
7
7
 
8
- __version__ = "0.4.0"
8
+ __version__ = "0.4.1"
@@ -26,9 +26,10 @@ page lists them too. The glue below covers what the three libraries leave
26
26
  out: plain text, line breaks a browser does not show, bold and italic
27
27
  CommonMark would not close, link targets, and an mdformat set up without
28
28
  its nesting cap and with its quadratic lookups made linear. A body nested
29
- too deeply for the stack is read as its text (:func:`_text_of`); anything
30
- else that fails raises
31
- :class:`~easyvista_python_client.EasyvistaContentError`.
29
+ too deeply for the stack, or on which the conversion raises ``ValueError``
30
+ -- markdownify does for a ``colspan`` or ``start`` it cannot read as a
31
+ number -- is read as its text (:func:`_text_of`); anything else that fails
32
+ raises :class:`~easyvista_python_client.EasyvistaContentError`.
32
33
 
33
34
  A memo with no HTML element in it is read as literal lines, a line break
34
35
  per line. How EasyVista's web UI displays such a memo is unverified: the
@@ -138,6 +139,11 @@ _EMPHASIS = frozenset({"b", "strong", "em", "i"})
138
139
  _BEFORE, _AFTER = "data-ev-before", "data-ev-after"
139
140
  _NUMBER = "data-ev-number" # an ordered item's number (_Converter.convert_li)
140
141
 
142
+ #: The elements kept as raw tags (:meth:`_Converter.convert_u`), and the
143
+ #: attribute marking one that holds a block (:func:`_note_blocks`).
144
+ _RAW_INLINE = frozenset({"u", "mark", "ins"})
145
+ _HOLDS_BLOCK = "data-ev-block"
146
+
141
147
  #: Text split into leading line breaks and spaces, content, trailing ones.
142
148
  #: The content ends on its last character that is neither, found greedily:
143
149
  #: a lazy ``.*?`` rescanned the run after it at every step, quadratic.
@@ -330,6 +336,33 @@ def _note_sides(root: Tag) -> None:
330
336
  element[_AFTER] = " "
331
337
 
332
338
 
339
+ def _note_blocks(root: Tag) -> None:
340
+ """Mark each ``<u>``, ``<mark>`` or ``<ins>`` that holds a block.
341
+
342
+ Markdown has no inline tag around blocks: such a tag wrapped round them
343
+ showed a table, a list or a heading as its Markdown source, and round a
344
+ ``<pre>`` left a fence open to the end of the body. One walk: a block
345
+ marks the open ones from the innermost out and stops at one already
346
+ marked, whose holders were marked with it, so each is marked once
347
+ however deeply they nest.
348
+ """
349
+
350
+ holders: list[Tag] = []
351
+ for node, entering in _walk(root):
352
+ if not isinstance(node, Tag):
353
+ continue
354
+ if node.name in _RAW_INLINE:
355
+ if entering:
356
+ holders.append(node)
357
+ else:
358
+ holders.pop()
359
+ elif entering and node.name in _BLOCKS:
360
+ for holder in reversed(holders):
361
+ if holder.has_attr(_HOLDS_BLOCK):
362
+ break
363
+ holder[_HOLDS_BLOCK] = ""
364
+
365
+
333
366
  def _punctuation(char: str) -> bool:
334
367
  """Return whether CommonMark counts ``char`` as punctuation."""
335
368
 
@@ -426,8 +459,14 @@ class _Converter(MarkdownConverter):
426
459
  convert_strike = convert_s
427
460
 
428
461
  def convert_u(self, el: Tag, text: str, parent_tags: set[str]) -> str:
429
- """Underlined or highlighted text: CommonMark has neither, so raw HTML."""
462
+ """Underlined or highlighted text: CommonMark has neither, so raw HTML.
430
463
 
464
+ Round a block the tag is dropped and the blocks kept, except on the
465
+ one line of a cell or a heading, where the blocks are inline text.
466
+ """
467
+
468
+ if el.has_attr(_HOLDS_BLOCK) and "_inline" not in parent_tags:
469
+ return text
431
470
  return self._markup(el, text, parent_tags, "", el.name)
432
471
 
433
472
  convert_mark = convert_ins = convert_u
@@ -705,6 +744,7 @@ def html_to_markdown(html: str) -> str:
705
744
  _flatten_nested_tables(soup)
706
745
  _drop_trailing_breaks(soup)
707
746
  _note_sides(soup)
747
+ _note_blocks(soup)
708
748
  return str(_FORMATTER.render(_CONVERTER.convert_soup(soup))).strip()
709
749
 
710
750
 
@@ -762,8 +802,10 @@ class EasyvistaContentConverter:
762
802
  ------
763
803
  EasyvistaContentError
764
804
  The value could not be converted. A body nested too deeply for
765
- the stack left is read as its text instead, so this is a
766
- backstop.
805
+ the stack left, or on which the conversion raises
806
+ ``ValueError`` (a ``colspan`` or ``start`` markdownify cannot
807
+ read as a number, among others), is read as its text instead,
808
+ so this is a backstop.
767
809
  RecursionError
768
810
  Only when called from within a few frames of the recursion
769
811
  limit, where no stack is left even to report the failure as
@@ -44,7 +44,7 @@ exclude = [
44
44
 
45
45
  [project]
46
46
  name = "easyvista-python-client"
47
- version = "0.4.0"
47
+ version = "0.4.1"
48
48
  description = "Typed Python client for the EasyVista Service Manager REST API"
49
49
  readme = "README.md"
50
50
  requires-python = ">=3.11"
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: "Requires Python 3.11+, easyvista-python-client, network access to an EasyVista Service Manager REST API, and a profile authorized for the assets resource."
6
6
  metadata:
7
7
  package: easyvista-python-client
8
- version: "0.4.0"
8
+ version: "0.4.1"
9
9
  ---
10
10
 
11
11
  > **Sync and async.** Examples use `EasyvistaClient`. For `AsyncEasyvistaClient`,
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: "Requires Python 3.11+, easyvista-python-client, network access to an EasyVista Service Manager REST API, and valid EasyVista credentials."
6
6
  metadata:
7
7
  package: easyvista-python-client
8
- version: "0.4.0"
8
+ version: "0.4.1"
9
9
  ---
10
10
 
11
11
  `easyvista_python_client` ships both clients over one surface:
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: "Requires Python 3.11+, easyvista-python-client, network access to an EasyVista Service Manager REST API, and a profile authorized for the departments and employees resources (writes are additionally profile-gated)."
6
6
  metadata:
7
7
  package: easyvista-python-client
8
- version: "0.4.0"
8
+ version: "0.4.1"
9
9
  ---
10
10
 
11
11
  > **Sync and async.** Examples use `EasyvistaClient`. For `AsyncEasyvistaClient`,
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: "Requires Python 3.11+, easyvista-python-client, network access to an EasyVista Service Manager REST API, and a profile authorized for the documents sub-resource."
6
6
  metadata:
7
7
  package: easyvista-python-client
8
- version: "0.4.0"
8
+ version: "0.4.1"
9
9
  ---
10
10
 
11
11
  > **Sync and async.** Examples use `EasyvistaClient`. For `AsyncEasyvistaClient`,
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: "Requires Python 3.11+, easyvista-python-client, and network access to an EasyVista Service Manager REST API. Every call here is a GET; nothing is created, updated or deleted."
6
6
  metadata:
7
7
  package: easyvista-python-client
8
- version: "0.4.0"
8
+ version: "0.4.1"
9
9
  ---
10
10
 
11
11
  > **Sync and async.** Examples use `EasyvistaClient`. For `AsyncEasyvistaClient`,
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: "Requires Python 3.11+, easyvista-python-client, and network access to an EasyVista Service Manager REST API."
6
6
  metadata:
7
7
  package: easyvista-python-client
8
- version: "0.4.0"
8
+ version: "0.4.1"
9
9
  ---
10
10
 
11
11
  > **Sync and async.** Examples use `EasyvistaClient`. For `AsyncEasyvistaClient`,
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: "Requires Python 3.11+, easyvista-python-client, and network access to an EasyVista Service Manager REST API."
6
6
  metadata:
7
7
  package: easyvista-python-client
8
- version: "0.4.0"
8
+ version: "0.4.1"
9
9
  ---
10
10
 
11
11
  > **Sync and async.** Examples use `EasyvistaClient`. For `AsyncEasyvistaClient`,
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: "Requires Python 3.11+, easyvista-python-client, network access to an EasyVista Service Manager REST API, and a profile authorized for the actions sub-resource."
6
6
  metadata:
7
7
  package: easyvista-python-client
8
- version: "0.4.0"
8
+ version: "0.4.1"
9
9
  ---
10
10
 
11
11
  > **Sync and async.** Examples use `EasyvistaClient`. For `AsyncEasyvistaClient`,
@@ -618,7 +618,8 @@ with EasyvistaClient.from_env() as client:
618
618
  `init`. Reading spells a note's text as literal text (`__init__` comes back
619
619
  as `\_\_init\_\_`, a displayed `<b>` as `\<b>`), so render the Markdown
620
620
  rather than stripping its backslashes. Underline and strike come back as raw
621
- `<u>` and `<s>` tags, and a note with no HTML in it reads as literal lines.
621
+ `<u>` and `<s>` tags (a `<u>` round a block is dropped and the block kept),
622
+ and a note with no HTML in it reads as literal lines.
622
623
  It sanitises nothing: raw HTML, `javascript:` link targets and
623
624
  `<javascript:...>` autolinks go out live, and reading keeps a memo's
624
625
  `javascript:` links, so neutralise both in Markdown you did not write — a
@@ -5,7 +5,7 @@ license: MIT
5
5
  compatibility: "Requires Python 3.11+, easyvista-python-client, network access to an EasyVista Service Manager REST API, and a profile authorized for the requests resource."
6
6
  metadata:
7
7
  package: easyvista-python-client
8
- version: "0.4.0"
8
+ version: "0.4.1"
9
9
  ---
10
10
 
11
11
  > **Sync and async.** Examples use `EasyvistaClient`. For `AsyncEasyvistaClient`,