diff --git a/requirements/requirements-optionals.txt b/requirements/requirements-optionals.txt index 2b7a18a13..739555667 100644 --- a/requirements/requirements-optionals.txt +++ b/requirements/requirements-optionals.txt @@ -1,6 +1,7 @@ # Optional packages which may be used with REST framework. psycopg2-binary>=2.8.5, <2.9 -markdown==3.1.1 +markdown==3.3;python_version>="3.6" +markdown==3.2.2;python_version=="3.5" pygments==2.4.2 django-guardian==2.2.0 django-filter>=2.2.0, <2.3 diff --git a/tests/test_description.py b/tests/test_description.py index ae00fe4a9..9e7e4dc32 100644 --- a/tests/test_description.py +++ b/tests/test_description.py @@ -1,192 +1,180 @@ -from django.test import TestCase - -from rest_framework.compat import apply_markdown -from rest_framework.utils.formatting import dedent -from rest_framework.views import APIView - -# We check that docstrings get nicely un-indented. -DESCRIPTION = """an example docstring -==================== - -* list -* list - -another header --------------- - - code block - -indented - -# hash style header # - -``` json -[{ - "alpha": 1, - "beta: "this is a string" -}] -```""" - - -# If markdown is installed we also test it's working -# (and that our wrapped forces '=' to h2 and '-' to h3) -MARKED_DOWN_HILITE = """ -
[{
"alpha": 1,
\ - "beta: "this\ - is a \ -string"
}]
- -


""" - -MARKED_DOWN_NOT_HILITE = """ -

json -[{ - "alpha": 1, - "beta: "this is a string" -}]

""" - -# We support markdown < 2.1 and markdown >= 2.1 -MARKED_DOWN_lt_21 = """

an example docstring

- -

another header

-
code block
-
-

indented

-

hash style header

%s""" - -MARKED_DOWN_gte_21 = """

an example docstring

- -

another header

-
code block
-
-

indented

-

hash style header

%s""" - - -class TestViewNamesAndDescriptions(TestCase): - def test_view_name_uses_class_name(self): - """ - Ensure view names are based on the class name. - """ - class MockView(APIView): - pass - assert MockView().get_view_name() == 'Mock' - - def test_view_name_uses_name_attribute(self): - class MockView(APIView): - name = 'Foo' - assert MockView().get_view_name() == 'Foo' - - def test_view_name_uses_suffix_attribute(self): - class MockView(APIView): - suffix = 'List' - assert MockView().get_view_name() == 'Mock List' - - def test_view_name_preferences_name_over_suffix(self): - class MockView(APIView): - name = 'Foo' - suffix = 'List' - assert MockView().get_view_name() == 'Foo' - - def test_view_description_uses_docstring(self): - """Ensure view descriptions are based on the docstring.""" - class MockView(APIView): - """an example docstring - ==================== - - * list - * list - - another header - -------------- - - code block - - indented - - # hash style header # - - ``` json - [{ - "alpha": 1, - "beta: "this is a string" - }] - ```""" - - assert MockView().get_view_description() == DESCRIPTION - - def test_view_description_uses_description_attribute(self): - class MockView(APIView): - description = 'Foo' - assert MockView().get_view_description() == 'Foo' - - def test_view_description_allows_empty_description(self): - class MockView(APIView): - """Description.""" - description = '' - assert MockView().get_view_description() == '' - - def test_view_description_can_be_empty(self): - """ - Ensure that if a view has no docstring, - then it's description is the empty string. - """ - class MockView(APIView): - pass - assert MockView().get_view_description() == '' - - def test_view_description_can_be_promise(self): - """ - Ensure a view may have a docstring that is actually a lazily evaluated - class that can be converted to a string. - - See: https://github.com/encode/django-rest-framework/issues/1708 - """ - # use a mock object instead of gettext_lazy to ensure that we can't end - # up with a test case string in our l10n catalog - - class MockLazyStr: - def __init__(self, string): - self.s = string - - def __str__(self): - return self.s - - class MockView(APIView): - __doc__ = MockLazyStr("a gettext string") - - assert MockView().get_view_description() == 'a gettext string' - - def test_markdown(self): - """ - Ensure markdown to HTML works as expected. - """ - if apply_markdown: - md_applied = apply_markdown(DESCRIPTION) - gte_21_match = ( - md_applied == ( - MARKED_DOWN_gte_21 % MARKED_DOWN_HILITE) or - md_applied == ( - MARKED_DOWN_gte_21 % MARKED_DOWN_NOT_HILITE)) - lt_21_match = ( - md_applied == ( - MARKED_DOWN_lt_21 % MARKED_DOWN_HILITE) or - md_applied == ( - MARKED_DOWN_lt_21 % MARKED_DOWN_NOT_HILITE)) - assert gte_21_match or lt_21_match - - -def test_dedent_tabs(): - result = 'first string\n\nsecond string' - assert dedent(" first string\n\n second string") == result - assert dedent("first string\n\n second string") == result - assert dedent("\tfirst string\n\n\tsecond string") == result - assert dedent("first string\n\n\tsecond string") == result +import sys + +import pytest +from django.test import TestCase + +from rest_framework.compat import apply_markdown +from rest_framework.utils.formatting import dedent +from rest_framework.views import APIView + +# We check that docstrings get nicely un-indented. +DESCRIPTION = """an example docstring +==================== + +* list +* list + +another header +-------------- + + code block + +indented + +# hash style header # + +``` json +[{ + "alpha": 1, + "beta: "this is a string" +}] +```""" + + +# If markdown is installed we also test it's working +# (and that our wrapped forces '=' to h2 and '-' to h3) +MARKDOWN_BASE = """

an example docstring

+ +

another header

+
code block
+
+

indented

+

hash style header

%s""" + +MARKDOWN_gte_33 = """ +
[{
\ + "alpha":\ + 1,
\ + "beta: "this\ + is a \ +string"
}]\ +
+


""" + +MARKDOWN_lt_33 = """ +
[{
\ + "alpha":\ + 1,
\ + "beta: "this\ + is a\ + string"
}]\ +
+ +


""" + + +class TestViewNamesAndDescriptions(TestCase): + def test_view_name_uses_class_name(self): + """ + Ensure view names are based on the class name. + """ + class MockView(APIView): + pass + assert MockView().get_view_name() == 'Mock' + + def test_view_name_uses_name_attribute(self): + class MockView(APIView): + name = 'Foo' + assert MockView().get_view_name() == 'Foo' + + def test_view_name_uses_suffix_attribute(self): + class MockView(APIView): + suffix = 'List' + assert MockView().get_view_name() == 'Mock List' + + def test_view_name_preferences_name_over_suffix(self): + class MockView(APIView): + name = 'Foo' + suffix = 'List' + assert MockView().get_view_name() == 'Foo' + + def test_view_description_uses_docstring(self): + """Ensure view descriptions are based on the docstring.""" + class MockView(APIView): + """an example docstring + ==================== + + * list + * list + + another header + -------------- + + code block + + indented + + # hash style header # + + ``` json + [{ + "alpha": 1, + "beta: "this is a string" + }] + ```""" + + assert MockView().get_view_description() == DESCRIPTION + + def test_view_description_uses_description_attribute(self): + class MockView(APIView): + description = 'Foo' + assert MockView().get_view_description() == 'Foo' + + def test_view_description_allows_empty_description(self): + class MockView(APIView): + """Description.""" + description = '' + assert MockView().get_view_description() == '' + + def test_view_description_can_be_empty(self): + """ + Ensure that if a view has no docstring, + then it's description is the empty string. + """ + class MockView(APIView): + pass + assert MockView().get_view_description() == '' + + def test_view_description_can_be_promise(self): + """ + Ensure a view may have a docstring that is actually a lazily evaluated + class that can be converted to a string. + + See: https://github.com/encode/django-rest-framework/issues/1708 + """ + # use a mock object instead of gettext_lazy to ensure that we can't end + # up with a test case string in our l10n catalog + + class MockLazyStr: + def __init__(self, string): + self.s = string + + def __str__(self): + return self.s + + class MockView(APIView): + __doc__ = MockLazyStr("a gettext string") + + assert MockView().get_view_description() == 'a gettext string' + + @pytest.mark.skipif(not apply_markdown, reason="Markdown is not installed") + def test_markdown(self): + """ + Ensure markdown to HTML works as expected. + """ + # Markdown 3.3 is only supported on Python 3.6 and higher + if sys.version_info >= (3, 6): + assert apply_markdown(DESCRIPTION) == MARKDOWN_BASE % MARKDOWN_gte_33 + else: + assert apply_markdown(DESCRIPTION) == MARKDOWN_BASE % MARKDOWN_lt_33 + + +def test_dedent_tabs(): + result = 'first string\n\nsecond string' + assert dedent(" first string\n\n second string") == result + assert dedent("first string\n\n second string") == result + assert dedent("\tfirst string\n\n\tsecond string") == result + assert dedent("first string\n\n\tsecond string") == result