You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by po...@apache.org on 2022/06/21 09:24:20 UTC
[airflow] branch main updated: Switch Markdown engine to markdown-it-py (#19702)
This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 88363b543f Switch Markdown engine to markdown-it-py (#19702)
88363b543f is described below
commit 88363b543f6f963247c332e9d7830bc782ed6e2d
Author: Torbjørn Vatn <to...@unacast.com>
AuthorDate: Tue Jun 21 11:24:13 2022 +0200
Switch Markdown engine to markdown-it-py (#19702)
---
airflow/www/static/css/main.css | 4 ++
airflow/www/utils.py | 5 +-
setup.cfg | 3 ++
tests/www/test_utils.py | 112 +++++++++++++++++++++++++++++++++-------
4 files changed, 103 insertions(+), 21 deletions(-)
diff --git a/airflow/www/static/css/main.css b/airflow/www/static/css/main.css
index 05eda9d42a..e735b7b9bb 100644
--- a/airflow/www/static/css/main.css
+++ b/airflow/www/static/css/main.css
@@ -465,6 +465,10 @@ label[for="timezone-other"],
z-index: 1070;
}
+details summary {
+ display: list-item;
+}
+
.menu-scroll {
max-height: 300px;
overflow-y: auto;
diff --git a/airflow/www/utils.py b/airflow/www/utils.py
index 2516e9108a..c8b97ec901 100644
--- a/airflow/www/utils.py
+++ b/airflow/www/utils.py
@@ -21,7 +21,6 @@ import time
from typing import Any, Dict, List, Optional, Union
from urllib.parse import urlencode
-import markdown
import sqlalchemy as sqla
from flask import Response, request, url_for
from flask.helpers import flash
@@ -31,6 +30,7 @@ from flask_appbuilder.models.sqla import filters as fab_sqlafilters
from flask_appbuilder.models.sqla.filters import get_field_setup_query, set_value_to_type
from flask_appbuilder.models.sqla.interface import SQLAInterface
from flask_babel import lazy_gettext
+from markdown_it import MarkdownIt
from markupsafe import Markup
from pendulum.datetime import DateTime
from pygments import highlight, lexers
@@ -476,10 +476,11 @@ def json_render(obj, lexer):
def wrapped_markdown(s, css_class='rich_doc'):
"""Convert a Markdown string to HTML."""
+ md = MarkdownIt("gfm-like")
if s is None:
return None
s = textwrap.dedent(s)
- return Markup(f'<div class="{css_class}" >' + markdown.markdown(s, extensions=['tables']) + "</div>")
+ return Markup(f'<div class="{css_class}" >{md.render(s)}</div>')
def get_attr_renderer():
diff --git a/setup.cfg b/setup.cfg
index e0976f9ba3..1512a6201c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -135,6 +135,7 @@ install_requires =
# we pin to the same upper-bound as connexion.
jsonschema>=3.2.0, <5.0
lazy-object-proxy
+ linkify-it-py>=2.0.0
lockfile>=0.12.2
markdown>=3.0
# Markupsafe 2.1.0 breaks with error: import name 'soft_unicode' from 'markupsafe'.
@@ -142,8 +143,10 @@ install_requires =
# https://github.com/pallets/markupsafe/issues/284
# or when we will be able to upgrade JINJA to newer version (currently limited due to Flask and
# Flask Application Builder)
+ markdown-it-py>=2.1.0
markupsafe>=1.1.1,<2.1.0
marshmallow-oneofschema>=2.0.1
+ mdit-py-plugins>=0.3.0
packaging>=14.0
pathspec~=0.9.0
pendulum>=2.0
diff --git a/tests/www/test_utils.py b/tests/www/test_utils.py
index 01c49e1fdc..8ac06f75de 100644
--- a/tests/www/test_utils.py
+++ b/tests/www/test_utils.py
@@ -184,29 +184,54 @@ class TestAttrRenderer(unittest.TestCase):
class TestWrappedMarkdown(unittest.TestCase):
def test_wrapped_markdown_with_docstring_curly_braces(self):
rendered = wrapped_markdown("{braces}", css_class="a_class")
- assert '<div class="a_class" ><p>{braces}</p></div>' == rendered
+ assert (
+ '''<div class="a_class" ><p>{braces}</p>
+</div>'''
+ == rendered
+ )
def test_wrapped_markdown_with_some_markdown(self):
- rendered = wrapped_markdown("*italic*\n**bold**\n", css_class="a_class")
+ rendered = wrapped_markdown(
+ """*italic*
+ **bold**
+ """,
+ css_class="a_class",
+ )
+
assert (
'''<div class="a_class" ><p><em>italic</em>
-<strong>bold</strong></p></div>'''
+<strong>bold</strong></p>
+</div>'''
== rendered
)
def test_wrapped_markdown_with_table(self):
rendered = wrapped_markdown(
- """| Job | Duration |
- | ----------- | ----------- |
- | ETL | 14m |"""
+ """
+| Job | Duration |
+| ----------- | ----------- |
+| ETL | 14m |
+"""
)
assert (
- '<div class="rich_doc" ><table>\n<thead>\n<tr>\n<th>Job</th>\n'
- '<th>Duration</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>ETL'
- '</td>\n<td>14m</td>\n</tr>\n</tbody>\n'
- '</table></div>'
- ) == rendered
+ '''<div class="rich_doc" ><table>
+<thead>
+<tr>
+<th>Job</th>
+<th>Duration</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>ETL</td>
+<td>14m</td>
+</tr>
+</tbody>
+</table>
+</div>'''
+ == rendered
+ )
def test_wrapped_markdown_with_indented_lines(self):
rendered = wrapped_markdown(
@@ -217,7 +242,11 @@ class TestWrappedMarkdown(unittest.TestCase):
"""
)
- assert '<div class="rich_doc" ><h1>header</h1>\n<p>1st line\n2nd line</p></div>' == rendered
+ assert (
+ '''<div class="rich_doc" ><h1>header</h1>\n<p>1st line\n2nd line</p>
+</div>'''
+ == rendered
+ )
def test_wrapped_markdown_with_raw_code_block(self):
rendered = wrapped_markdown(
@@ -235,10 +264,12 @@ class TestWrappedMarkdown(unittest.TestCase):
)
assert (
- '<div class="rich_doc" ><h1>Markdown code block</h1>\n'
- '<p>Inline <code>code</code> works well.</p>\n'
- '<pre><code>Code block\ndoes not\nrespect\nnewlines\n</code></pre></div>'
- ) == rendered
+ '''<div class="rich_doc" ><h1>Markdown code block</h1>
+<p>Inline <code>code</code> works well.</p>
+<pre><code>Code block\ndoes not\nrespect\nnewlines\n</code></pre>
+</div>'''
+ == rendered
+ )
def test_wrapped_markdown_with_nested_list(self):
rendered = wrapped_markdown(
@@ -251,6 +282,49 @@ class TestWrappedMarkdown(unittest.TestCase):
)
assert (
- '<div class="rich_doc" ><h3>Docstring with a code block</h3>\n'
- '<ul>\n<li>And<ul>\n<li>A nested list</li>\n</ul>\n</li>\n</ul></div>'
- ) == rendered
+ '''<div class="rich_doc" ><h3>Docstring with a code block</h3>
+<ul>
+<li>And
+<ul>
+<li>A nested list</li>
+</ul>
+</li>
+</ul>
+</div>'''
+ == rendered
+ )
+
+ def test_wrapped_markdown_with_collapsible_section(self):
+ rendered = wrapped_markdown(
+ """
+# A collapsible section with markdown
+<details>
+ <summary>Click to expand!</summary>
+
+ ## Heading
+ 1. A numbered
+ 2. list
+ * With some
+ * Sub bullets
+</details>
+ """
+ )
+
+ assert (
+ '''<div class="rich_doc" ><h1>A collapsible section with markdown</h1>
+<details>
+ <summary>Click to expand!</summary>
+<h2>Heading</h2>
+<ol>
+<li>A numbered</li>
+<li>list
+<ul>
+<li>With some</li>
+<li>Sub bullets</li>
+</ul>
+</li>
+</ol>
+</details>
+</div>'''
+ == rendered
+ )