From 524feca0de6e3ce161231891d2dcaf866077d9b3 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 24 Feb 2013 17:40:18 +0100 Subject: [PATCH 1/8] Add TestDocstring --- test/regression.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/regression.py b/test/regression.py index a7cdc12f..fd5b4299 100755 --- a/test/regression.py +++ b/test/regression.py @@ -328,6 +328,16 @@ class TestRegression(TestBase): assert 'start' in words +class TestDocstring(TestBase): + + def test_function_doc(self): + defs = self.definition(""" + def func(): + '''Docstring of `func`.''' + func""") + self.assertEqual(defs[0].raw_doc, 'Docstring of `func`.') + + class TestFeature(TestBase): def test_full_name(self): """ feature request #61""" From 19b41c1cb4de8dab55868547aa88891ba2bd6a7f Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 24 Feb 2013 19:26:23 +0100 Subject: [PATCH 2/8] Make pr.Statement "document-able" --- jedi/parsing_representation.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/jedi/parsing_representation.py b/jedi/parsing_representation.py index 44a78344..a4a5fa9d 100644 --- a/jedi/parsing_representation.py +++ b/jedi/parsing_representation.py @@ -659,7 +659,8 @@ class Statement(Simple): :type start_pos: tuple(int, int) """ __slots__ = ('token_list', 'used_vars', - 'set_vars', '_commands', '_assignment_details') + 'set_vars', '_commands', '_assignment_details', + 'docstr') def __init__(self, module, set_vars, used_vars, token_list, start_pos, end_pos, parent=None): @@ -670,12 +671,17 @@ class Statement(Simple): s.parent = self.use_as_parent self.set_vars = self._remove_executions_from_set_vars(set_vars) self.parent = parent + self.docstr = '' # cache self._commands = None self._assignment_details = [] # this is important for other scripts + def add_docstr(self, string): + """ Clean up a docstring """ + self.docstr = cleandoc(literal_eval(string)) + def _remove_executions_from_set_vars(self, set_vars): """ Important mainly for assosiative arrays: From 740f27c8b8aebdfbb55c69c11ef3eb6a52b8a00f Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 24 Feb 2013 19:26:56 +0100 Subject: [PATCH 3/8] Add attribute docstrings (PEP 257) support --- jedi/parsing.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/jedi/parsing.py b/jedi/parsing.py index 95992b54..ac0ccfd4 100644 --- a/jedi/parsing.py +++ b/jedi/parsing.py @@ -395,6 +395,22 @@ class Parser(object): self._check_user_stmt(stmt) + # Attribute docstring (PEP 257) support + try: + # If string literal is being parsed + first_tok = stmt.token_list[0] + if (not stmt.set_vars and + not stmt.used_vars and + len(stmt.token_list) == 1 and + first_tok[0] == tokenize.STRING): + # ... and the last statement was assignment + last_stmt = self.scope.statements[-1] + if last_stmt.assignment_details[0][1] == '=': + # ... then set it as a docstring + last_stmt.add_docstr(first_tok[1]) + except (IndexError, AttributeError): + pass + if tok in always_break + not_first_break: self._gen.push_last_back() return stmt, tok From b6839fb2643a3ca77dafa7e79b1ec8d99da37378 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 24 Feb 2013 19:32:19 +0100 Subject: [PATCH 4/8] Define Name.docstr as a proxy for Statement.docstr --- jedi/parsing_representation.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jedi/parsing_representation.py b/jedi/parsing_representation.py index a4a5fa9d..cd8c6188 100644 --- a/jedi/parsing_representation.py +++ b/jedi/parsing_representation.py @@ -1271,6 +1271,11 @@ class Name(Simple): """ Returns the names in a full string format """ return ".".join(self.names) + @property + def docstr(self): + """Return attribute docstring (PEP 257) if exists.""" + return self.parent.docstr + def __str__(self): return self.get_code() From 540eff3d42d102a4ae683074e13aa45ed851aa02 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 24 Feb 2013 17:59:08 +0100 Subject: [PATCH 5/8] Add test_attribute_docstring --- test/regression.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/regression.py b/test/regression.py index fd5b4299..313b6a30 100755 --- a/test/regression.py +++ b/test/regression.py @@ -337,6 +337,14 @@ class TestDocstring(TestBase): func""") self.assertEqual(defs[0].raw_doc, 'Docstring of `func`.') + def test_attribute_docstring(self): + # TODO: use self.definition + defs = self.goto(""" + x = None + '''Docstring of `x`.''' + x""") + self.assertEqual(defs[0].raw_doc, 'Docstring of `x`.') + class TestFeature(TestBase): def test_full_name(self): From e458b35f1c8e68dacaf6a710f26a736144f80e08 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 24 Feb 2013 19:45:47 +0100 Subject: [PATCH 6/8] Unconditionally set attribute docstrings As Statement.assignment_details triggers parsing inside statement let's don't check assignment_details when setting attribute docstrings. --- jedi/parsing.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/jedi/parsing.py b/jedi/parsing.py index ac0ccfd4..08f0a44c 100644 --- a/jedi/parsing.py +++ b/jedi/parsing.py @@ -403,11 +403,8 @@ class Parser(object): not stmt.used_vars and len(stmt.token_list) == 1 and first_tok[0] == tokenize.STRING): - # ... and the last statement was assignment - last_stmt = self.scope.statements[-1] - if last_stmt.assignment_details[0][1] == '=': - # ... then set it as a docstring - last_stmt.add_docstr(first_tok[1]) + # ... then set it as a docstring + self.scope.statements[-1].add_docstr(first_tok[1]) except (IndexError, AttributeError): pass From aad9c34db61d0893a89cd439f501b8b341a35ffd Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 24 Feb 2013 20:56:09 +0100 Subject: [PATCH 7/8] Include statements to Script.definition if it has docstring --- jedi/evaluate.py | 3 +++ test/regression.py | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/jedi/evaluate.py b/jedi/evaluate.py index 47bc6ae8..f5d727a9 100644 --- a/jedi/evaluate.py +++ b/jedi/evaluate.py @@ -216,6 +216,9 @@ def find_name(scope, name_str, position=None, search_global=False, # so just ignore it. continue + if r.docstr: + res_new.append(r) + scopes = follow_statement(r, seek_name=name_str) add += remove_statements(scopes) diff --git a/test/regression.py b/test/regression.py index 313b6a30..e52793b2 100755 --- a/test/regression.py +++ b/test/regression.py @@ -338,8 +338,7 @@ class TestDocstring(TestBase): self.assertEqual(defs[0].raw_doc, 'Docstring of `func`.') def test_attribute_docstring(self): - # TODO: use self.definition - defs = self.goto(""" + defs = self.definition(""" x = None '''Docstring of `x`.''' x""") From 7f00b9414e8de60f6ababb22da240512cdd2bc26 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 24 Feb 2013 20:58:43 +0100 Subject: [PATCH 8/8] Check the case where multiple docstrings are defined --- test/regression.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/regression.py b/test/regression.py index e52793b2..9933b4eb 100755 --- a/test/regression.py +++ b/test/regression.py @@ -344,6 +344,16 @@ class TestDocstring(TestBase): x""") self.assertEqual(defs[0].raw_doc, 'Docstring of `x`.') + def test_multiple_docstrings(self): + defs = self.definition(""" + def func(): + '''Original docstring.''' + x = func + '''Docstring of `x`.''' + x""") + docs = [d.raw_doc for d in defs] + self.assertEqual(docs, ['Original docstring.', 'Docstring of `x`.']) + class TestFeature(TestBase): def test_full_name(self):