Skip to content

WIP: Update to mistune v2 - #57

Open
CrossNox wants to merge 110 commits into
developmentfrom
v2
Open

WIP: Update to mistune v2#57
CrossNox wants to merge 110 commits into
developmentfrom
v2

Conversation

@CrossNox

Copy link
Copy Markdown
Owner

No description provided.

@ahopkins

Copy link
Copy Markdown

What is the state of this?

@maximee-leroyy

Copy link
Copy Markdown

@CrossNox any update on this ?

@daquintero

daquintero commented Jan 12, 2024

Copy link
Copy Markdown

Further updates here #66

Unfortunately I'm going to have to release our version to pypi as we need it to build our project for an urgent release (we were using poetry git= requirements before) but doesn't work on pypi release. Happy to take it down if we integrate the changes and deploy a newer version of m2r2.

Happy to become an active mantainer too - as you like

is_sphinx is an internal renderer detail for Sphinx integration.
The Sphinx codepath uses M2R2.from_sphinx_config() directly, never
convert(). Updated test to use M2R2 class directly.
The config mapping duplicated information already expressed by the
M2R2 constructor signature. Callers now construct M2R2 directly with
explicit config attribute access. setup() inlines add_config_value
calls.
is_sphinx was introduced during the migration to switch between
:: and .. code-block:: for unlabeled code blocks. The original
m2r2 always used .. code-block::, and :: is standard RST anyway
(not Sphinx-specific). Removed from M2R2, RestRenderer, and all
callers.
The old codebase supported 'python -m m2r2' but the package
restructuring broke it. Add the standard __main__.py entry point
and update the CLI test to use sys.executable -m m2r2 instead of
the bare 'm2r2' command for portability.
The old extension registered 'no_underscore_emphasis' without the
m2r_ prefix. Register both names and migrate the old value via a
config-inited handler that emits a DeprecationWarning, so existing
conf.py files keep working while nudging users to the new name.
parse_from_file was part of the old public API (importable from
m2r2 directly). Re-export it from __init__.py and fix the circular
import this introduces by having cli/m2r2.py import from m2r2.m2r2
instead of the package root.
The hasattr(md.inline, '_rules') branch was dead code — the _rules
attribute does not exist in mistune v3. Removing from md.inline.rules
is sufficient. Also add an iteration limit (10) to the raw-html merge
loop to prevent infinite loops if the regex ever produces a fixpoint.
Remove references to nonexistent files (constants.py, typing.py,
rst/directives.py, rst/parser.py), add sphinx/directives.py and
__main__.py, and list test_sphinx.py in the testing section.
Trim from 324 to 59 lines by removing sphinx-quickstart boilerplate
comments, unused LaTeX/man/texinfo sections, dead sys.path.append
hack, and broken intersphinx_mapping. Update copyright to CrossNox.
Mirrors the convert() signature for a consistent, self-documenting API
instead of blindly forwarding arbitrary keyword arguments.
~~text~~ was passing through unconverted because the strikethrough
plugin was never registered. Add the mistune plugin and a RestRenderer
method that renders it as :raw-html-m2r:`<del>text</del>`.
Pass instantiated Reader, RstParser, and Writer to Publisher instead of
using set_components() with string names — the latter is deprecated and
will be removed in docutils 2.0. Also add proper assertions to
test_strikethrough now that the plugin is enabled.
Runs the CLI end-to-end via subprocess with --dry-run and verifies the
output matches the expected .rst fixture.
mistune v3 combines all inline patterns into one large regex via named
groups and alternation. Using m.group(1) returned None because group 1
referred to a different pattern's capture group. Switch to named groups
((?P<emph_text>...) and (?P<strong_text>...)) and m.group('emph_text')
to always capture the correct match.
Passing plugins=some_list to M2R2() would append 4 items (rst_directives,
table, footnotes, strikethrough) to the caller's list, mutating it
unexpectedly. Copy with list(plugins) to avoid side effects.
Bug fixes:
- Fix code block info string: split on whitespace to extract language only
  (e.g. 'python title=test' -> 'python')
- Fix _raw_html() to escape backticks as &#96; preventing broken RST roles
- Remove dead paragraph condition: text.strip().startswith('\n\n') was
  unreachable; keep only the image_link path check
- Wrap list() item processing in try/finally to always restore state

Dead code removal:
- Remove finalize(): not in mistune v3 RSTRenderer, never called
- Remove table_cell(): never dispatched to (table_head/table_row call
  render_children directly)
- Remove render_referrences/inline_images dead code from __call__

Comment improvements:
- Add semantic comment explaining why table_head and table_row are separate
- Update footnote comments: .lower() normalizes mistune v3's uppercasing
- Guard app.connect('config-inited', ...) with try/except KeyError for
  Sphinx < 1.8 which doesn't have the config-inited event
- Invert add_source_parser try/except: try modern API first (single arg),
  fall back to old 2-arg API for Sphinx < 4.0
- Add --version flag showing package version
- Add --use-mermaid flag to render mermaid blocks as directives
- Add try/except for FileNotFoundError/OSError in run_m2r2 with stderr
  output and exit code 1
- Guard interactive input() with sys.stdin.isatty() check to prevent
  blocking in non-interactive environments (pipes, CI)
- Update tests to mock sys.stdin.isatty for overwrite/decline tests
- Add test for multiple input files
- docs/conf.py: source_suffix .md -> ['.rst', '.md'], master_doc ->
  root_doc (Sphinx 4.0+), copyright year 2025 -> 2026, htmlhelp_basename
  M2Rdoc -> M2R2doc
- pyproject.toml: remove follow_imports='skip' that neutered mypy
- README.md: fix import path from m2r2.cli to m2r2
- Add 'check' job that runs lint and tests before deployment
- Both untagged_deploy and tagged_deploy now need: [check]
- Fix branches syntax: 'master' -> [master] (YAML list required)
- no_underscore_emphasis: asterisk emphasis/strong, underscore passthrough,
  mixed emphasis in sentence (regression for #1)
- Code block info string with extra metadata (regression for #6)
- Inline HTML with backtick escaping (regression for #23)
- image_title: add actual assertions for image directive output
- Complex text: add assertions for code blocks, headings, thematic break
- Heading levels 2-6: verify all underline characters (-, ^, ~, ", #)
- Footnote mixed-case: verify key normalization to lowercase
… link fallback

- Set tok['prev'] = prev_tok in iter_tokens to maintain the parent
  RSTRenderer's contract (parent's block_quote accesses token['prev'])
- Remove dead state.env['inline_images'] initialization from __call__
  (was needed by removed render_referrences code, never read anywhere)
- Fix relative links with path+fragment (e.g. page.md#section): fall
  back to regular link instead of silently discarding the anchor, since
  :doc: cannot target specific anchors
- Fix rest_code_block plugin token to use 'raw' key instead of 'text'
  to skip unnecessary inline parsing on empty content
… config precedence

CLI:
- Validate all input files exist before processing any, rather than
  failing on first error and skipping remaining valid files
- Send skip/warning messages to stderr instead of stdout to avoid
  corrupting piped output (e.g. with --dry-run)

Sphinx:
- Fix deprecated config migration: if the user explicitly sets the new
  m2r_no_underscore_emphasis config, it takes precedence over the
  deprecated no_underscore_emphasis value
CI security:
- Add permissions: contents: read to all workflows (least privilege)
- Add permissions: contents: write to tagged_deploy job (needs gh-pages push)
- Pin ad-m/github-push-action to commit SHA (57116acb)
- Pin pypa/gh-action-pypi-publish to release/v1.13

Config:
- Fix mypy: replace global ignore_missing_imports with per-module
  override for mistune.* only, so real import errors are caught
- Add py.typed marker file for PEP 561 downstream type checking
- Fix CHANGES.md: docutils version 0.21.2+ -> 0.19+ to match pyproject.toml
- Fix CONTRIBUTING.md: update exports list and cli description
CLI tests:
- Rewrite file-writing tests to use tempdir instead of mutating the
  shared test.rst fixture (prevents corruption on crash/kill)
- Use StringIO for stdout/stderr capture instead of mock.patch('builtins.print')
- Add tests: --version flag, --parse-relative-links, --use-mermaid,
  missing files validation (all reported before exit), non-interactive skip

Renderer tests:
- Strengthen test_strikethrough: assert full role content
- Update test_relative_link_with_anchor for new fallback behavior
- Add TestEdgeCases: empty input, whitespace-only, multiple newlines
- Add TestInstanceReuse: list/table/footnote state isolation across calls

Fixtures:
- Expand test.md/test.rst pair to cover lists (nested, ordered),
  inline code, code blocks, images, block quotes, tables, horizontal
  rules, and footnotes
- Escape link/title attrs with html.escape() to prevent XSS injection
- Fix _RAW_HTML_MERGE_PATTERN: allow colons but exclude newlines to
  prevent cross-line merges that consume unrelated RST content
- Simplify post_process newline normalization to lstrip + prepend
- Add PackageNotFoundError fallback for __version__
- Remove unused tight param from _render_list_item and bullet key
  from list tokens in plugins.py
- Fix paragraph() docstring accuracy
- Improve test assertions: backtick escape, codespan role, version regex
- Pin pypa/gh-action-pypi-publish to SHA, add concurrency group,
  clear gh-pages before deploy, add -j auto to Sphinx CI builds
- Standardize CHANGES.md heading levels
- Rewrite CONTRIBUTING.md to reference pre-commit hooks
… :doc:

The migration changed relative links with anchors (e.g. [text](page.md#anchor))
to fall back to plain hyperlinks, which don't resolve in Sphinx builds.
Restore the original m2r behavior: drop the fragment and emit :doc: instead,
since the :doc: directive does not support anchors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants