pythondjangomarkdownurlizecommonmark

Using Django's urlize with CommonMark


I'd like to use Django's urlize function together with django-markwhat's CommonMark filter.

I'm imagining something like this:

{{ "http://example.com"|urlize|commonmark }}

Which outputs <p><a href="http://example.com">http://example.com</a></p>. The issue here is that URLs already marked up in commonmark, like <http://example.com>, will get rendered with angled brackets around them.

If I apply urlize after commonmark, like this:

{{ "http://example.com"|commonmark|urlize }}

The urlize function doesn't detect the url in <p>http://example.com</p> (and it's mentioned in the urlize docs that it won't work as expected for html input).

I haven't come up with a solution yet, so let me know if you have any ideas.


Solution

  • For completeness, the easy answer is to switch to a Markdown parser which provides the behavior you want out of the box. Multiple extensions exist which give Python-Markdown the desired behavior.

    However, if you want this behavior with CommonMark, which does not support this behavior itself, then you will need to create a custom urlize filter which operates on HTML. Fortunately, The Bleach library provides a linkify function which will handle that correctly.

    Of course, you then need to create a template filter which wraps that:

    from django import template
    import bleach
    from html5lib.tokenizer import HTMLTokenizer
    
    register = template.Library()
    
    @register.filter(is_safe=True)
    def linkify(value):
        return bleach.linkify(value, skip_pre=True, parse_email=True, tokenizer=HTMLTokenizer)
    

    Note that I'm assuming you do want to parse email addresses as well as URLS, you do not want to have URLs in code blocks parsed, and you do not want CommonMark's output sanitized. Feel free to review Bleach's docs and adjust accordingly.

    To use your custom filter, save the above code to a file named linkify.py within a templatetags directory in your app (see the Django docs for a full explanation). Then within a template, do the following:

    {% load linkify %}
    
    {{ "http://example.com"|commonmark|linkify }}