Skip to content
This repository has been archived by the owner on Sep 24, 2022. It is now read-only.

Unable to call HtmlRenderer's paragraph method #73

Open
noisytoken opened this issue Sep 14, 2019 · 5 comments
Open

Unable to call HtmlRenderer's paragraph method #73

noisytoken opened this issue Sep 14, 2019 · 5 comments

Comments

@noisytoken
Copy link

I am creating a customer renderer to adds admonition tags to Markdown.

import misaka as m
import re
import houdini as h


class CustomHTMLRenderer(m.HtmlRenderer):
    def paragraph(self, content):
        _prefix_re = re.compile(r'^\s*(!{1,4})\s+')

        CLASSES = {
            1: 'note',
            2: 'info',
            3: 'tip',
            4: 'warning',
        }

        match = _prefix_re.match(content)
        if match is None:
            return super(CustomHTMLRenderer, self).paragraph(content)  ## this call is the problem
        else:
            # do something
            pass

When paragraph() method is called I get this error:

AttributeError: 'super' object has no attribute 'paragraph'
From cffi callback <function cb_paragraph at 0x7f4fb4108b70>:
Traceback (most recent call last):
  File "/home/x/project/env/lib/python3.6/site-packages/misaka/callbacks.py", line 82, in cb_paragraph
    result = renderer.paragraph(content)
  File "/home/x/project/django_project/wiki/utils.py", line 99, in paragraph
    return super(CustomHTMLRenderer, self).paragraph(content)
AttributeError: 'super' object has no attribute 'paragraph'
@FSX
Copy link
Owner

FSX commented Sep 14, 2019

Unfortunately the current design doesn't support overriding parent methods, because HtmlRenderer doesn't have any methods. It just fills a struct with function pointers on init (which are not accessible from Python).

I'm planning to address this in the next major version.

For now you have to re-implement the paragraph rendering method yourself. See code below for an example.

import misaka as m
import re
import houdini as h


class CustomHTMLRenderer(m.HtmlRenderer):
    def paragraph(self, content):
        _prefix_re = re.compile(r'^\s*(!{1,4})\s+')

        CLASSES = {
            1: 'note',
            2: 'info',
            3: 'tip',
            4: 'warning',
        }

        match = _prefix_re.match(content)
        if match is None:
            return '<p>' + content + '</p>\n'
        else:
            length = len(match.group(1))
            value = CLASSES[length]
            return '<p class="' + value + '">' + content[length+1:] + '</p>\n'


if __name__ == '__main__':
    rndr = CustomHTMLRenderer(flags=('escape',))
    md = m.Markdown(rndr)
    print(md("""
some <b>text</b>

!!!! some more text
"""))

@noisytoken
Copy link
Author

Thanks that would work.
BTW, how can I add Table of contents. The docs mention the HtmlTocRenderer class but doesn't give a clue about how to use it?

@FSX
Copy link
Owner

FSX commented Sep 24, 2019

You can add nesting_level=6 to CustomHTMLRenderer to add id attributes to the HTML headers. And HtmlTocRenderer can be used to render the input text to a nested list of header contents.

Here's your code updated with a toc:

import misaka as m
import re
import houdini as h


class CustomHTMLRenderer(m.HtmlRenderer):
    def paragraph(self, content):
        _prefix_re = re.compile(r'^\s*(!{1,4})\s+')

        CLASSES = {
            1: 'note',
            2: 'info',
            3: 'tip',
            4: 'warning',
        }

        match = _prefix_re.match(content)
        if match is None:
            return '<p>' + content + '</p>\n'
        else:
            length = len(match.group(1))
            value = CLASSES[length]
            return '<p class="' + value + '">' + content[length+1:] + '</p>\n'


def render_table_of_contents(text):
    rndr = m.HtmlTocRenderer()
    md = m.Markdown(rndr)
    return md(text)


def render_contents(text, flags):
    rndr = CustomHTMLRenderer(nesting_level=6, flags=('escape',))
    md = m.Markdown(rndr)
    return md(text)


if __name__ == '__main__':
    flags = ('escape',)
    text = """
# H1

some <b>text</b>

## H2

!!!! some more text
"""

    print(
        render_table_of_contents(text) +
        render_contents(text, flags)
    )

@noisytoken
Copy link
Author

Unfortunately the current design doesn't support overriding parent methods, because HtmlRenderer doesn't have any methods. It just fills a struct with function pointers on init (which are not accessible from Python).

I'm planning to address this in the next major version.

For now you have to re-implement the paragraph rendering method yourself. See code below for an example.

import misaka as m
import re
import houdini as h


class CustomHTMLRenderer(m.HtmlRenderer):
    def paragraph(self, content):
        _prefix_re = re.compile(r'^\s*(!{1,4})\s+')

        CLASSES = {
            1: 'note',
            2: 'info',
            3: 'tip',
            4: 'warning',
        }

        match = _prefix_re.match(content)
        if match is None:
            return '<p>' + content + '</p>\n'
        else:
            length = len(match.group(1))
            value = CLASSES[length]
            return '<p class="' + value + '">' + content[length+1:] + '</p>\n'


if __name__ == '__main__':
    rndr = CustomHTMLRenderer(flags=('escape',))
    md = m.Markdown(rndr)
    print(md("""
some <b>text</b>

!!!! some more text
"""))

This code renders the HTML_HARD_WRAP flag useless.

@noisytoken
Copy link
Author

noisytoken commented Jul 14, 2020

You can add nesting_level=6 to CustomHTMLRenderer to add id attributes to the HTML headers. And HtmlTocRenderer can be used to render the input text to a nested list of header contents.

Here's your code updated with a toc:

import misaka as m
import re
import houdini as h


class CustomHTMLRenderer(m.HtmlRenderer):
    def paragraph(self, content):
        _prefix_re = re.compile(r'^\s*(!{1,4})\s+')

        CLASSES = {
            1: 'note',
            2: 'info',
            3: 'tip',
            4: 'warning',
        }

        match = _prefix_re.match(content)
        if match is None:
            return '<p>' + content + '</p>\n'
        else:
            length = len(match.group(1))
            value = CLASSES[length]
            return '<p class="' + value + '">' + content[length+1:] + '</p>\n'


def render_table_of_contents(text):
    rndr = m.HtmlTocRenderer()
    md = m.Markdown(rndr)
    return md(text)


def render_contents(text, flags):
    rndr = CustomHTMLRenderer(nesting_level=6, flags=('escape',))
    md = m.Markdown(rndr)
    return md(text)


if __name__ == '__main__':
    flags = ('escape',)
    text = """
# H1

some <b>text</b>

## H2

!!!! some more text
"""

    print(
        render_table_of_contents(text) +
        render_contents(text, flags)
    )

There is one problem with this code. Consider the following:

# from PIL import Image
# import webp
#
# img = Image.open('/home/x/Downloads/img1.png')
# webp.save_image(img, '/home/x/Downloads/error.webp', quality=80)

import misaka as m
import re
import houdini as h


class CustomHTMLRenderer(m.HtmlRenderer):
    def paragraph(self, content):
        _prefix_re = re.compile(r'^\s*(!{1,4})\s+')

        CLASSES = {
            1: 'note',
            2: 'info',
            3: 'tip',
            4: 'warning',
        }

        match = _prefix_re.match(content)
        if match is None:
            return '<p>' + content + '</p>\n'
        else:
            length = len(match.group(1))
            value = CLASSES[length]
            return '<p class="' + value + '">' + content[length+1:] + '</p>\n'


def render_table_of_contents(text):
    rndr = m.HtmlTocRenderer()
    md = m.Markdown(rndr)
    return md(text)


def render_contents(text, flags):
    rndr = CustomHTMLRenderer(nesting_level=6, flags=('escape',))
    md = m.Markdown(rndr)
    return md(text)


if __name__ == '__main__':
    flags = ('escape',)
    text = """
# H1

some <b>text</b>

## H2

!!!! some more text

```python
# comment

print("hello")
print(
    render_table_of_contents(text) +
    render_contents(text, flags)
)

Here the line `# comment` is part of python code, but it is still being rendered in toc.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants