From a9abad0e21fc647b65a5553f1cea05b39c0489d6 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Wed, 26 May 2021 16:40:16 -0400 Subject: [PATCH 1/3] identify example blocks in docstrings --- pylsp/_utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pylsp/_utils.py b/pylsp/_utils.py index 92376f6c..e5c9cae2 100644 --- a/pylsp/_utils.py +++ b/pylsp/_utils.py @@ -6,6 +6,7 @@ import logging import os import pathlib +import re import threading import jedi @@ -14,6 +15,7 @@ log = logging.getLogger(__name__) +EX_SNIPPET_RE = re.compile(r"\>\>\> .*(?:\r?\n(?!\r?\n).*)*") def debounce(interval_s, keyed_by=None): """Debounce calls to this function until interval_s seconds have passed.""" @@ -146,6 +148,9 @@ def format_docstring(contents): """ contents = contents.replace('\t', u'\u00A0' * 4) contents = contents.replace(' ', u'\u00A0' * 2) + example_snippets = re.findall(EX_SNIPPET_RE, contents) + for snippet in example_snippets: + contents = contents.replace(snippet, f"```python\n{snippet}\n```") return contents From c9d3a6f6ccd83792529a8b503c498b9698382840 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Wed, 26 May 2021 18:56:08 -0400 Subject: [PATCH 2/3] pycodestyle fix --- pylsp/_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylsp/_utils.py b/pylsp/_utils.py index e5c9cae2..5c7232d9 100644 --- a/pylsp/_utils.py +++ b/pylsp/_utils.py @@ -17,6 +17,7 @@ EX_SNIPPET_RE = re.compile(r"\>\>\> .*(?:\r?\n(?!\r?\n).*)*") + def debounce(interval_s, keyed_by=None): """Debounce calls to this function until interval_s seconds have passed.""" def wrapper(func): From f98adbe6579829880c507f8f36b2812b5d2fe23b Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Wed, 26 May 2021 20:06:33 -0400 Subject: [PATCH 3/3] add comments and a couple of tests --- pylsp/_utils.py | 16 ++++++++-- test/test_utils.py | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/pylsp/_utils.py b/pylsp/_utils.py index 5c7232d9..3a5f178f 100644 --- a/pylsp/_utils.py +++ b/pylsp/_utils.py @@ -149,9 +149,19 @@ def format_docstring(contents): """ contents = contents.replace('\t', u'\u00A0' * 4) contents = contents.replace(' ', u'\u00A0' * 2) - example_snippets = re.findall(EX_SNIPPET_RE, contents) - for snippet in example_snippets: - contents = contents.replace(snippet, f"```python\n{snippet}\n```") + + # If examples exist in the docstring wrap them in backticks + if ">>>" in contents: + # add an additional newline just in case the end of the + # docstring doesn't end in a blank line. + if contents[-2:] != "\n\n": + contents = f"{contents}\n" + # search for the example block regex + example_snippets = re.findall(EX_SNIPPET_RE, contents) + # wrap the snippets that were found in backticks + for snippet in example_snippets: + contents = contents.replace(snippet, f"```python\n{snippet}\n```") + return contents diff --git a/test/test_utils.py b/test/test_utils.py index 4b41155b..c03880d8 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -94,3 +94,82 @@ def test_clip_column(): assert _utils.clip_column(2, ['123\n', '123'], 0) == 2 assert _utils.clip_column(3, ['123\n', '123'], 0) == 3 assert _utils.clip_column(4, ['123\n', '123'], 1) == 3 + + +def test_format_docstring(): + teststr = """\ +Examples +-------- +Abc + +>>> a = 'test' +>>> b = 5 +>>> a +test +>>> b + 3 +8 + +another + +>>> x = np.array([1, 2, 3]) +>>> y = np.array([4, 5, 6]) +>>> x + y +array([5, 7, 9]) + +""" + resultstr = _utils.format_docstring(teststr) + assert resultstr == """\ +Examples +-------- +Abc + +```python +>>> a = 'test' +>>> b = 5 +>>> a +test +>>> b + 3 +8 +``` + +another + +```python +>>> x = np.array([1, 2, 3]) +>>> y = np.array([4, 5, 6]) +>>> x + y +array([5, 7, 9]) +``` + +""" + + +def test_format_docstring_missing_newline(): + teststr = """\ +Examples +-------- +Abc + +>>> a = 'test' +>>> b = 5 +>>> a +test +>>> b + 3 +8 +""" + resultstr = _utils.format_docstring(teststr) + assert resultstr == """\ +Examples +-------- +Abc + +```python +>>> a = 'test' +>>> b = 5 +>>> a +test +>>> b + 3 +8 +``` + +"""