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 @@

Classifiers

Project Description

{% if release.description %}
- {{ release.description|readme(format="rst") }} + {{ release.description|readme(description_content_type=release.description_content_type) }}
{% else %}