diff --git a/tests/unit/test_filters.py b/tests/unit/test_filters.py
index 07c39944a8bc..261e224eb887 100644
--- a/tests/unit/test_filters.py
+++ b/tests/unit/test_filters.py
@@ -35,12 +35,9 @@ def test_camo_url():
class TestReadmeRender:
- def test_can_render(self, monkeypatch):
- monkeypatch.setattr(
- readme_renderer.rst,
- "render",
- lambda raw: "rendered",
- )
+ def test_can_render_rst(self, monkeypatch):
+ renderer = pretend.call_recorder(lambda raw: "rendered")
+ monkeypatch.setattr(readme_renderer.rst, "render", renderer)
ctx = {
"request": pretend.stub(
@@ -53,15 +50,18 @@ def test_can_render(self, monkeypatch):
),
}
- result = filters.readme(ctx, "raw thing", format="rst")
+ result = filters.readme(
+ ctx, "raw thing", description_content_type="text/x-rst",
+ )
assert result == jinja2.Markup("rendered")
+ assert renderer.calls == [pretend.call('raw thing')]
- def test_cant_render(self, monkeypatch):
- monkeypatch.setattr(readme_renderer.rst, "render", lambda raw: None)
- monkeypatch.setattr(
- readme_renderer.txt, "render", lambda raw: "rendered
thing",
- )
+ def test_cant_render_rst(self, monkeypatch):
+ rst_renderer = pretend.call_recorder(lambda raw: None)
+ txt_renderer = pretend.call_recorder(lambda raw: "rendered
thing")
+ monkeypatch.setattr(readme_renderer.rst, "render", rst_renderer)
+ monkeypatch.setattr(readme_renderer.txt, "render", txt_renderer)
ctx = {
"request": pretend.stub(
@@ -74,9 +74,57 @@ def test_cant_render(self, monkeypatch):
),
}
- result = filters.readme(ctx, "raw thing", format="rst")
+ result = filters.readme(
+ ctx, "raw thing", description_content_type="text/x-rst",
+ )
assert result == jinja2.Markup("rendered
thing")
+ assert rst_renderer.calls == [pretend.call('raw thing')]
+ assert txt_renderer.calls == [pretend.call('raw thing')]
+
+ def test_can_render_plaintext(self, monkeypatch):
+ renderer = pretend.call_recorder(lambda raw: "rendered")
+ monkeypatch.setattr(readme_renderer.txt, "render", renderer)
+
+ ctx = {
+ "request": pretend.stub(
+ registry=pretend.stub(
+ settings={
+ "camo.url": "https://camo.example.net/",
+ "camo.key": "fake key",
+ },
+ ),
+ ),
+ }
+
+ result = filters.readme(
+ ctx, "raw thing", description_content_type="text/plain",
+ )
+
+ assert result == jinja2.Markup("rendered")
+ assert renderer.calls == [pretend.call('raw thing')]
+
+ def test_can_render_markdown(self, monkeypatch):
+ renderer = pretend.call_recorder(lambda raw: "rendered")
+ monkeypatch.setattr(readme_renderer.markdown, "render", renderer)
+
+ ctx = {
+ "request": pretend.stub(
+ registry=pretend.stub(
+ settings={
+ "camo.url": "https://camo.example.net/",
+ "camo.key": "fake key",
+ },
+ ),
+ ),
+ }
+
+ result = filters.readme(
+ ctx, "raw thing", description_content_type="text/markdown",
+ )
+
+ assert result == jinja2.Markup("rendered")
+ assert renderer.calls == [pretend.call('raw thing')]
def test_renders_camo(self, monkeypatch):
html = ""
@@ -98,7 +146,9 @@ def test_renders_camo(self, monkeypatch):
),
}
- result = filters.readme(ctx, "raw thing", format="rst")
+ result = filters.readme(
+ ctx, "raw thing", description_content_type="text/x-rst",
+ )
assert result == jinja2.Markup(
'
'
@@ -131,7 +181,9 @@ def test_renders_camo_no_src(self, monkeypatch):
),
}
- result = filters.readme(ctx, "raw thing", format="rst")
+ result = filters.readme(
+ ctx, "raw thing", description_content_type="text/x-rst",
+ )
assert result == jinja2.Markup("
")
assert gen_camo_url.calls == []
diff --git a/warehouse/filters.py b/warehouse/filters.py
index c0cd4b0d976e..040bf8bdd2c8 100644
--- a/warehouse/filters.py
+++ b/warehouse/filters.py
@@ -11,6 +11,7 @@
# limitations under the License.
import binascii
+import cgi
import collections
import enum
import hmac
@@ -24,6 +25,7 @@
import jinja2
import packaging.version
+import readme_renderer.markdown
import readme_renderer.rst
import readme_renderer.txt
@@ -31,6 +33,13 @@
from warehouse.utils.http import is_valid_uri
+_renderers = {
+ '': readme_renderer.rst, # Default if description_content_type is None
+ 'text/plain': readme_renderer.txt,
+ 'text/x-rst': readme_renderer.rst,
+ 'text/markdown': readme_renderer.markdown,
+}
+
class PackageType(enum.Enum):
bdist_dmg = "OSX Disk Image"
@@ -63,23 +72,22 @@ def _camo_url(camo_url, camo_key, url):
@jinja2.contextfilter
-def readme(ctx, value, *, format):
+def readme(ctx, value, *, description_content_type):
request = ctx.get("request") or get_current_request()
camo_url = request.registry.settings["camo.url"].format(request=request)
camo_key = request.registry.settings["camo.key"]
- # The format parameter is here so we can more easily expand this to cover
- # READMEs which do not use restructuredtext, but for now rst is the only
- # format we support.
- assert format == "rst", "We currently only support rst rendering."
+ content_type, parameters = cgi.parse_header(description_content_type)
+
+ # Get the appropriate renderer
+ renderer = _renderers[content_type]
# Actually render the given value, this will not only render the value, but
# also ensure that it's had any disallowed markup removed.
- rendered = readme_renderer.rst.render(value)
+ rendered = renderer.render(value, **parameters)
- # If the content was not rendered, we'll replace the newlines with breaks
- # so that it shows up nicer when rendered.
+ # If the content was not rendered, we'll render as plaintext instead
if rendered is None:
rendered = readme_renderer.txt.render(value)
diff --git a/warehouse/templates/packaging/detail.html b/warehouse/templates/packaging/detail.html
index c6d89588ffa8..e389d4d180ca 100644
--- a/warehouse/templates/packaging/detail.html
+++ b/warehouse/templates/packaging/detail.html
@@ -234,7 +234,7 @@