From a8abcf966c3f86b40c2a23e39b14896c4499c83b Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 8 Mar 2024 16:53:26 +0000 Subject: [PATCH 01/11] RED: triple-ticks end test --- Lib/test/test_doctest/test_doctestmd.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Lib/test/test_doctest/test_doctestmd.py diff --git a/Lib/test/test_doctest/test_doctestmd.py b/Lib/test/test_doctest/test_doctestmd.py new file mode 100644 index 00000000000000..b2e0063e68e30b --- /dev/null +++ b/Lib/test/test_doctest/test_doctestmd.py @@ -0,0 +1,20 @@ +import unittest +import doctest + +def dummyfunction_codeblocks(): + """ + A dummy function that uses codeblocks in the examples. + + It is used like this: + ``` + >>> 1 == 1 + True + ``` + """ + pass + +class TestMarkdownDocstring(unittest.TestCase): + def test_DoctestRunner(self): + test = doctest.DocTestFinder().find(dummyfunction_codeblocks)[0] + results = doctest.DocTestRunner().run(test) + self.assertEqual(results, (0,1)) From a98f9933d99ecf47500e4295bece83e0fab47fb6 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 8 Mar 2024 16:54:36 +0000 Subject: [PATCH 02/11] GREEN: triple-ticks end test --- Lib/doctest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/doctest.py b/Lib/doctest.py index 6049423b5147a5..0b9d7f54a3c4e5 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -618,6 +618,7 @@ class DocTestParser: \n? # Want consists of any non-blank lines that do not start with PS1. (?P (?:(?![ ]*$) # Not a blank line + (?![ ]*```) # Not end of a code block (?![ ]*>>>) # Not a line starting with PS1 .+$\n? # But any other line )*) From c832e7d49b7cc274b0d3256ab6c19cefc20604e4 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 8 Mar 2024 19:22:18 +0000 Subject: [PATCH 03/11] add new in version 3.13 note near top of docs --- Doc/library/doctest.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst index 1bfcd69f72df2e..c897adce62514d 100644 --- a/Doc/library/doctest.rst +++ b/Doc/library/doctest.rst @@ -136,6 +136,9 @@ examples of doctests in the standard Python test suite and libraries. Especially useful examples can be found in the standard test file :file:`Lib/test/test_doctest/test_doctest.py`. +.. versionadded:: 3.13 + Doctest will parse markdown files and accept codeblock delimiters in + docstrings. .. _doctest-simple-testmod: From c87637d9f575d5a9aa351c64bbc08cbd6378f7e3 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sat, 9 Mar 2024 07:40:52 +0000 Subject: [PATCH 04/11] include rendered tripleticks in "new in 3.13" --- Doc/library/doctest.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst index c897adce62514d..90b52aa26cf76c 100644 --- a/Doc/library/doctest.rst +++ b/Doc/library/doctest.rst @@ -137,7 +137,7 @@ Especially useful examples can be found in the standard test file :file:`Lib/test/test_doctest/test_doctest.py`. .. versionadded:: 3.13 - Doctest will parse markdown files and accept codeblock delimiters in + Doctest will parse markdown files and accept codeblock delimiters ``````` in docstrings. .. _doctest-simple-testmod: From ecaa7773306bdc3d29597b4c4512867357bc4fbb Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sat, 9 Mar 2024 08:08:59 +0000 Subject: [PATCH 05/11] add codeblock info to "How examples recognised" --- Doc/library/doctest.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst index 90b52aa26cf76c..0e737bcaa7f4a8 100644 --- a/Doc/library/doctest.rst +++ b/Doc/library/doctest.rst @@ -343,9 +343,13 @@ but doctest isn't trying to do an exact emulation of any specific Python shell. single: >>>; interpreter prompt single: ...; interpreter prompt +.. versionadded:: 3.13 + Examples can also be enclosed in markdown codeblocks, although this is + *not required* + Any expected output must immediately follow the final ``'>>> '`` or ``'... '`` line containing the code, and the expected output (if any) extends to the next -``'>>> '`` or all-whitespace line. +``'>>> '``, ``````` or all-whitespace line. The fine print: @@ -354,6 +358,9 @@ The fine print: blank line, put ```` in your doctest example each place a blank line is expected. +* Expected output cannot contain triplebackticks ```````, since such a line is + taken to signal the end of expected output. + * All hard tab characters are expanded to spaces, using 8-column tab stops. Tabs in output generated by the tested code are not modified. Because any hard tabs in the sample output *are* expanded, this means that if the code From 8d228d9394f6c2cc4b323818b860ae0c24badf55 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sat, 9 Mar 2024 08:30:52 +0000 Subject: [PATCH 06/11] extend coverage to include multiple codeblocks in docstring --- Lib/test/test_doctest/test_doctestmd.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_doctest/test_doctestmd.py b/Lib/test/test_doctest/test_doctestmd.py index b2e0063e68e30b..9b41101c6a224d 100644 --- a/Lib/test/test_doctest/test_doctestmd.py +++ b/Lib/test/test_doctest/test_doctestmd.py @@ -13,8 +13,31 @@ def dummyfunction_codeblocks(): """ pass +def dummyfunction_multiplecodeblocks(): + """ + A dummy function that uses codeblocks in the examples. + + It is used like this: + ``` + >>> 1 == 1 + True + ``` + + Or like this: + ``` + >>> 1 + 2 + 3 + ``` + """ + pass + class TestMarkdownDocstring(unittest.TestCase): - def test_DoctestRunner(self): + def test_DoctestRunner_codeblocks(self): test = doctest.DocTestFinder().find(dummyfunction_codeblocks)[0] results = doctest.DocTestRunner().run(test) self.assertEqual(results, (0,1)) + + def test_DoctestRunner_multiplecodeblocks(self): + test = doctest.DocTestFinder().find(dummyfunction_multiplecodeblocks)[0] + results = doctest.DocTestRunner().run(test) + self.assertEqual(results, (0,2)) From 64d888d0908be2b8280749ffecccff7c1c063d38 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sat, 9 Mar 2024 10:17:14 +0000 Subject: [PATCH 07/11] extend coverage to include markdown files --- Lib/test/test_doctest/test_doctestmd.py | 44 +++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Lib/test/test_doctest/test_doctestmd.py b/Lib/test/test_doctest/test_doctestmd.py index 9b41101c6a224d..6ba52468b38392 100644 --- a/Lib/test/test_doctest/test_doctestmd.py +++ b/Lib/test/test_doctest/test_doctestmd.py @@ -31,6 +31,35 @@ def dummyfunction_multiplecodeblocks(): """ pass +mdfile = r''' +# A Markdown file + +Some documentation in an md file which contains codeblocks like this: + +```pycon +>>> 1 == 1 +True +``` + +and another one like this: + +```pycon +>>> 1 + 2 +3 +``` + +and sometimes with multiple cases in one codeblock +```pycon +>>> 2 == 2 +True + +>>> 2 + 2 +4 +``` + +It would be nice if doctest could test them too ... +''' + class TestMarkdownDocstring(unittest.TestCase): def test_DoctestRunner_codeblocks(self): test = doctest.DocTestFinder().find(dummyfunction_codeblocks)[0] @@ -41,3 +70,18 @@ def test_DoctestRunner_multiplecodeblocks(self): test = doctest.DocTestFinder().find(dummyfunction_multiplecodeblocks)[0] results = doctest.DocTestRunner().run(test) self.assertEqual(results, (0,2)) + +class TestMarkdownFile(unittest.TestCase): + """Test DocTestParser processes markdown files""" + + def test_DocTestParser_getdoctest(self): + parser = doctest.DocTestParser() + tests = parser.get_doctest( + mdfile, + globs=dict(), + name="mdfile", + filename=None, + lineno=None, + ) + results = doctest.DocTestRunner().run(tests) + self.assertEqual(results, (0,4)) From 8ae119ce9821ef6d2d9e79483e47ab682ff1a0ea Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sat, 9 Mar 2024 10:17:57 +0000 Subject: [PATCH 08/11] Document tests and make naming consistent --- Lib/test/test_doctest/test_doctestmd.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_doctest/test_doctestmd.py b/Lib/test/test_doctest/test_doctestmd.py index 6ba52468b38392..c6a8a0f2ea1108 100644 --- a/Lib/test/test_doctest/test_doctestmd.py +++ b/Lib/test/test_doctest/test_doctestmd.py @@ -1,3 +1,7 @@ +"""Test that doctest processes strings where examples are enclosed in +markdown-style codeblocks. +""" + import unittest import doctest @@ -61,12 +65,20 @@ def dummyfunction_multiplecodeblocks(): ''' class TestMarkdownDocstring(unittest.TestCase): - def test_DoctestRunner_codeblocks(self): + """Test DocTestFinder processes docstrings including markup codeblocks""" + + def test_DocTestFinder_codeblocks(self): + """A single codeblock in the docstring""" + + # DocTestFinder returns a list of tests, we only need the first test = doctest.DocTestFinder().find(dummyfunction_codeblocks)[0] results = doctest.DocTestRunner().run(test) self.assertEqual(results, (0,1)) - def test_DoctestRunner_multiplecodeblocks(self): + def test_DocTestFinder_multiplecodeblocks(self): + """Multiple codeblocks in the docstring""" + + # DocTestFinder returns a list of tests, we only need the first test = doctest.DocTestFinder().find(dummyfunction_multiplecodeblocks)[0] results = doctest.DocTestRunner().run(test) self.assertEqual(results, (0,2)) From be35d875c4d939b43835e1b99615f08f06788a1a Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sat, 9 Mar 2024 15:21:14 +0000 Subject: [PATCH 09/11] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst diff --git a/Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst b/Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst new file mode 100644 index 00000000000000..a00065a4fa4382 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst @@ -0,0 +1 @@ +Adjusted the regex used in :class:`doctest.DocTestParser` to also consider triple backticks ``````` to signify the end of an example block (in addition to a blank line and `>>>`). From 09cbf3057628e94b22f9ddee442b2a18b8a56173 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sat, 9 Mar 2024 16:12:17 +0000 Subject: [PATCH 10/11] fix lint: specify role in news blurb I think this was a blurb.it bug, as I'm 99% sure I did this on the web interface --- .../Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst b/Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst index a00065a4fa4382..ab71d471f8db34 100644 --- a/Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst +++ b/Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst @@ -1 +1,3 @@ -Adjusted the regex used in :class:`doctest.DocTestParser` to also consider triple backticks ``````` to signify the end of an example block (in addition to a blank line and `>>>`). +Adjusted the regex used in :class:`doctest.DocTestParser` to also consider +triple backticks ``````` to signify the end of an example block +(in addition to a blank line and `>>>`). From d1eca8844446a0bd33f04c96760fcff93859f360 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sat, 9 Mar 2024 16:22:45 +0000 Subject: [PATCH 11/11] fix lint: use double backtick for inline literal --- .../next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst b/Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst index ab71d471f8db34..f0895bbeadba30 100644 --- a/Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst +++ b/Misc/NEWS.d/next/Library/2024-03-09-15-21-13.gh-issue-116546.7v8S44.rst @@ -1,3 +1,3 @@ Adjusted the regex used in :class:`doctest.DocTestParser` to also consider triple backticks ``````` to signify the end of an example block -(in addition to a blank line and `>>>`). +(in addition to a blank line and ``>>>``).