lepture / mistune

A fast yet powerful Python Markdown parser with renderers and plugins.
http://mistune.lepture.com/
BSD 3-Clause "New" or "Revised" License
2.61k stars 250 forks source link

Context data needed in custom renderer #391

Open juanitocalero opened 2 months ago

juanitocalero commented 2 months ago

I'm writing a custom renderer, and I would like to modify my generated img tags to customize its url with a prefix.

That prefix depends on some extra data that depends on the current execution, but I'm unable to find a way to insert a context object in my custom render methods.

I know I could add an external prepopulated state object, but following the code, I can't find a way for that state to reach the rendering methods. The method's signature doesn't include a state. I've reviewed renderers/html.py render_token method, and it seems that the state is never used in the render functions.

How could I achieve that? The options that come to my mind are:

Any ideas?

lepture commented 2 months ago

You can inherit the BaseRenderer. Check code of MarkdownRenderer and rst renderer.

juanitocalero commented 2 months ago

I finally solved it like this: My Renderer class overrides the render_token method, and there, I get the state object and put my supplied information in the attributes dict. This attributes dict is used in the parent render_token to dynamically call the specific render methods with the attributes unpacked.

It's a little hacky but works ok, and it doesn't affect anything else.

class WikiRenderer(mistune.HTMLRenderer):

    def render_token(self, token, state) -> str:
        attrs = token.get('attrs')
        if attrs and token['type'] in ['wikilink', 'link']:
            attrs['doc'] = state.env["doc"]
        return mistune.HTMLRenderer.render_token(self, token, state)

    def wikilink(self, text, title, doc):
        return f'<a ...'

    def link(self, text, url, title=None, doc=None):
       ...

I call the markdown rendering like this:

def markdown_view(space, doc):
    state = markdown.block.state_cls()
    state.env["doc"] =doc
    text, _ = markdown.parse(space.fs.read_text(doc.path), state)
    return text