Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
15245c1
Add tests for user settings and cache persistence
ondratu Oct 28, 2026
6e98bf3
Add tests for JSONPath filtering in the JSON preview
ondratu Oct 28, 2026
3b3f163
Add tests for dialog filters and AUTHORS parsing
ondratu Oct 28, 2026
58d567e
Add tests for editor list continuation and file handling
ondratu Oct 28, 2026
2894d16
Add tests for renderer output, fonts and hover label
ondratu Oct 28, 2026
8decfff
Add tests for command line handling and entry points
ondratu Oct 28, 2026
0e3b48c
Add tests for document page refresh logic
ondratu Oct 28, 2026
a5400fd
Add tests for WebKit event handlers
ondratu Oct 28, 2026
bc6825d
Add tests for litehtml DOM helpers, vim widget and legacy mistune
ondratu Oct 28, 2026
1905035
Run the tests with litehtmlpy built in CI
ondratu Oct 28, 2026
bb0ff10
Render Markdown in the hover test without building a Renderer widget
ondratu Oct 28, 2026
392a737
Keep crash dumps visible when the entry point is imported in tests
ondratu Oct 28, 2026
aeafecb
Install optional dependencies in CI and fix the litehtmlpy build
ondratu Oct 28, 2026
8a09cf2
Document how to reproduce headless CI crashes locally
ondratu Oct 28, 2026
7c08fe9
Fix mdinclude with m2r2 2.1
ondratu Oct 28, 2026
e50c7ea
Install optional packages by name in CI, docutils-htmlwriter is not o…
ondratu Oct 28, 2026
ce0eee5
Pass Docutils a writer instance so older Docutils keep working
ondratu Oct 28, 2026
02ca6cf
Install the Pango headers needed to build litehtmlpy in CI
ondratu Oct 28, 2026
774b493
Reject out-of-range views with ValueError instead of assert
ondratu Oct 28, 2026
bc633d7
Share one Renderer adapter between the render tests
ondratu Oct 28, 2026
935081e
Check the mdinclude Sphinx stand-in against the installed m2r2
ondratu Oct 28, 2026
bee54c3
Run the litehtmlpy tests as a matrix entry of the pytest job
ondratu Oct 28, 2026
7488d41
Describe the display-less test objects as observed, not guaranteed
ondratu Oct 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@ permissions:

jobs:
pytest:
name: Run pytest
name: Run pytest${{ matrix.litehtml && ' with litehtmlpy' || '' }}
runs-on: ubuntu-latest
strategy:
# litehtmlpy is not on PyPI and is built from source, so a failing
# build must not hide the results of the plain run.
fail-fast: false
matrix:
litehtml: [false, true]

steps:
- name: Checkout
Expand All @@ -24,13 +30,43 @@ jobs:
python3-docutils \
python3-gi \
python3-jsonpath-ng \
python3-pip \
python3-pytest \
python3-venv \
gir1.2-adw-1 \
gir1.2-gtksource-5 \
gir1.2-spelling-1 \
gir1.2-webkit-6.0
- name: Install build dependencies of litehtmlpy
if: matrix.litehtml
run: |
sudo apt-get install --no-install-recommends --yes \
build-essential \
cmake \
git \
libcairo2-dev \
libpango1.0-dev \
ninja-build \
pybind11-dev \
python3-cairo \
python3-dev
- name: Install optional Python dependencies
run: |
python3 -m venv --system-site-packages "$RUNNER_TEMP/venv"
# Not `.[extra]`: it lists docutils-htmlwriter, which is not on PyPI.
"$RUNNER_TEMP/venv/bin/pip" install \
pynvim m2r2 mistune Pygments docutils-tinyhtmlwriter
- name: Build litehtmlpy
if: matrix.litehtml
# The same source as the `litehtml` extra in pyproject.toml, without
# installing formiko itself: the tests must import the checkout.
run: |
"$RUNNER_TEMP/venv/bin/pip" install \
'litehtmlpy @ git+https://github.com/m32/litehtmlpy.git'
- name: Run tests
run: env -u DISPLAY -u WAYLAND_DISPLAY python3 -m pytest -m "not gtk_integration" -q
run: |
env -u DISPLAY -u WAYLAND_DISPLAY \
"$RUNNER_TEMP/venv/bin/python" -m pytest -m "not gtk_integration" -q -rs
gtk-integration:
name: Run GTK integration tests
runs-on: ubuntu-latest
Expand Down
34 changes: 34 additions & 0 deletions CONTRIBUTION.rst
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,40 @@ tests.
full suite, including GTK integration tests, requires the private Mutter
headless compositor described above.

**Beware, unsetting the variables is not enough locally.** On a desktop
session GTK still finds the compositor's socket through
``XDG_RUNTIME_DIR`` (``$XDG_RUNTIME_DIR/wayland-0``) and connects to it, so
the command above can pass on a developer machine and still crash in CI,
where there is no display at all. To reproduce CI, hide the socket too::

env -u DISPLAY -u WAYLAND_DISPLAY XDG_RUNTIME_DIR=$(mktemp -d) \
python3 -m pytest -m "not gtk_integration" -q

Without a display, ``Gdk.Display.get_default()`` and
``Gtk.Settings.get_default()`` return ``None`` and constructing a real
widget can crash the interpreter with a segmentation fault instead of
raising. A crash has no Python traceback in the CI log, only
``Segmentation fault (core dumped)``; run with ``-v`` to see the test that
was running, and note that pytest may lose the ``faulthandler`` output (see
below). Typical offenders and what to do instead:

* Do not build widgets such as ``Renderer`` (a ``Gtk.Overlay``) or
``Adw.*`` windows and dialogs in a plain test. Borrow the method under
test from the class and call it on a ``types.SimpleNamespace`` adapter
(see ``tests/test_renderer_logic.py`` and
``tests/test_renderer_scroll_sync.py``), or move the test to the
``gtk_integration`` job. These objects have been observed to work
without a display: ``Gtk.FileFilter``, ``Gio.ListStore``,
``GtkSource.Buffer``, ``Gdk.RGBA`` and ``Adw.Application``. That is
what the CI runs have shown so far, not a guarantee, so run the
``XDG_RUNTIME_DIR`` command above before relying on anything else.
* Keep the adapter for ``Renderer`` in one place,
``tests/renderer_adapter.py``, rather than copying its private
attributes into each test.
* Do not import ``formiko.__main__`` at module level in a test: it calls
``faulthandler.enable()`` on import, which redirects the crash dump into
pytest's captured stderr, so a segfault leaves no trace in the log.

Commits
-------

Expand Down
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Changelog

Version 2.1.0.dev

* Fix the mdinclude directive failing with the newest m2r2 (2.1)
* Add desktop actions to open documents in a new window or as new tabs
in an existing window
* Add a "Mistune Markdown" parser option: renders Markdown straight to
Expand Down
4 changes: 4 additions & 0 deletions formiko/directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,15 @@ class _M2RConfig:
m2r_parse_relative_links = False
m2r_anonymous_references = False
m2r_disable_inline_math = False
m2r_inline_math = "legacy" # m2r2 >= 2.1, same as the old default
m2r_use_mermaid = False

class _M2REnv:
config = _M2RConfig()

def note_included(self, filename):
"""Ignore the Sphinx hook that m2r2 >= 2.1 calls on include."""

class StandaloneMdInclude(MdInclude):
"""MdInclude with standalone docutils support.

Expand Down
2 changes: 1 addition & 1 deletion formiko/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ def render_output(self): # noqa: C901, PLR0911
"source": self.src,
"source_path": self.file_name,
"parser": self.parser_instance,
"writer": self.writer_instance or "html",
"writer": self.writer_instance,
"settings_overrides": settings,
}
if self.__writer["key"] == "pep":
Expand Down
4 changes: 3 additions & 1 deletion formiko/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ class View:
def __new__(cls, value):
"""Create enum value from string or number value."""
value = int(value)
assert 0 < value < View.BOTH + 1
if not View.EDITOR <= value <= View.BOTH:
msg = f"{value} is not a valid view"
raise ValueError(msg)
return value


Expand Down
31 changes: 31 additions & 0 deletions tests/renderer_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Duck-typed stand-in for :class:`formiko.renderer.Renderer`.

``Renderer`` is a ``Gtk.Overlay`` and cannot be built without a display
(the constructor segfaults), so tests borrow methods from the class and run
them against this adapter. Keep the knowledge of which private attributes
``render_output`` reads, including the name-mangled ones, in this one place.

Import it after ``gi.require_version`` has been called.
"""

from types import SimpleNamespace

from formiko.renderer import PARSERS, WRITERS, Renderer


def make_render_adapter(parser="rst", writer="html4", **overrides):
"""Return an adapter that ``Renderer.render_output`` can run against."""
adapter = SimpleNamespace(
src="Title\n=====\n\ntext\n",
file_name="/tmp/x.rst", # noqa: S108
tab_width=8,
style="",
parser_instance=PARSERS[parser]["class"](),
writer_instance=WRITERS[writer]["class"](),
_embed_stylesheet=Renderer._embed_stylesheet,
)
adapter._Renderer__parser = PARSERS[parser]
adapter._Renderer__writer = WRITERS[writer]
for key, value in overrides.items():
setattr(adapter, key, value)
return adapter
Loading
Loading